Skip to content

Commit 55d3356

Browse files
authored
feat: Add IDOR Vulnerability (#504)
* feat: Add IDOR Vulnerability * WIP: preparing refactor * refactor: IDOR RBAC implementation
1 parent 516e56f commit 55d3356

File tree

23 files changed

+1191
-2
lines changed

23 files changed

+1191
-2
lines changed

src/main/java/org/sasanlabs/configuration/VulnerableAppConfiguration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ public DataSourceInitializer adminDataSourceInitializer(
131131
populator.addScript(new ClassPathResource("scripts/xss/PersistentXSS/db/schema.sql"));
132132
populator.addScript(new ClassPathResource("scripts/XXEVulnerability/schema.sql"));
133133
populator.addScript(new ClassPathResource("scripts/SQLInjection/db/data.sql"));
134+
populator.addScript(new ClassPathResource("scripts/IDOR/db/schema.sql"));
135+
populator.addScript(new ClassPathResource("scripts/IDOR/db/data.sql"));
134136
populator.setSeparator(";");
135137

136138
DataSourceInitializer initializer = new DataSourceInitializer();

src/main/java/org/sasanlabs/service/vulnerability/idor/IDORVulnerability.java

Lines changed: 458 additions & 0 deletions
Large diffs are not rendered by default.

src/main/java/org/sasanlabs/vulnerability/types/VulnerabilityType.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ public enum VulnerabilityType {
4444
// Cryptographic Failures
4545
WEAK_CRYPTOGRAPHIC_HASH(327, null),
4646
INSECURE_CRYPTOGRAPHIC_STORAGE(326, null),
47-
USE_OF_BROKEN_CRYPTOGRAPHIC_ALGORITHM(330, null);
47+
USE_OF_BROKEN_CRYPTOGRAPHIC_ALGORITHM(330, null),
48+
49+
// IDOR - Broken Access Control
50+
INSECURE_DIRECT_OBJECT_REFERENCE(639, 13);
4851

4952
private Integer cweID;
5053
private Integer wascID;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
IDOR_PAYLOAD_LEVEL_1=Login as one user and modify the 'id' request parameter to access another user's data.
2+
IDOR_PAYLOAD_LEVEL_2=Inspect and modify the value of 'userId' cookie in the browser developer tools and then click on 'Fetch Profile' button to access another user's profile information. For example, if you are logged in as Alice and value of 'userId' cookie is 1, change it to 2 to access Bob's profile information.
3+
IDOR_PAYLOAD_LEVEL_3=Decode the Base64 token, modify userId or role, re-encode it.
4+
IDOR_PAYLOAD_LEVEL_4=Decode the token, change role to ADMIN, re-encode and access other user's profile
5+
IDOR_PAYLOAD_LEVEL_5=Even if you decode the token and change role to ADMIN, you should not be able to access other user's profile due to proper RBAC implementation.

src/main/resources/i18n/messages.properties

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,4 +314,12 @@ CRYPTOGRAPHIC_FAILURES_SHA1_HASHING=Password is hashed using SHA1 algorithm whic
314314
CRYPTOGRAPHIC_FAILURES_PLAINTEXT_STORAGE=Password is stored and returned in plaintext without any encryption or hashing, making it immediately visible in the API response.
315315
CRYPTOGRAPHIC_FAILURES_BASE64_ENCODING=Password is "encrypted" using Base64 encoding which is not encryption at all. The user must decode the Base64 string to find the original password.
316316
CRYPTOGRAPHIC_FAILURES_SECURE_SHA256=Password is hashed using SHA-256 with salt, making rainbow table attacks ineffective. In production, use bcrypt, Argon2, or PBKDF2 for password hashing.
317-
CRYPTOGRAPHIC_FAILURES_SECURE_AES=Data is encrypted using AES-256 with a secret key that is never exposed. Without the key, the data cannot be decrypted.
317+
CRYPTOGRAPHIC_FAILURES_SECURE_AES=Data is encrypted using AES-256 with a secret key that is never exposed. Without the key, the data cannot be decrypted.
318+
319+
### IDOR Vulnerability
320+
IDOR_VULNERABILITY=Insecure Direct Object Reference (IDOR)
321+
IDOR_LEVEL_1_NO_AUTHORIZATION=Login and try accessing another user's id
322+
IDOR_LEVEL_2_COOKIE_TAMPERING=The application stores authentication state in a client-side cookie. Since cookies can be modified by users, this allows impersonation of other users.
323+
IDOR_LEVEL_3_JWT_TAMPERING=The application uses a JWT-like token stored on the client side. The token is not signed or validated. Try modifying the token payload.
324+
IDOR_LEVEL_4_BROKEN_RBAC=The application uses role-based access control (RBAC) but has a broken implementation. Try changing the role in the token or cookie to ADMIN and access other user's profile.
325+
IDOR_LEVEL_5_SECURE_RBAC=The application uses role-based access control (RBAC) and properly enforces it. Even if you try to change the role in the token or cookie, you should not be able to access other user's profile. This is the secure implementation.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
INSERT INTO idor_users VALUES (1, 'Alice', 'password', 50000, 'USER');
2+
INSERT INTO idor_users VALUES (2, 'Bob', 'password', 60000, 'USER');
3+
INSERT INTO idor_users VALUES (3, 'Charlie', 'password', 70000, 'ADMIN');
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
DROP TABLE IF EXISTS idor_users;
2+
3+
CREATE TABLE idor_users (
4+
id INT PRIMARY KEY,
5+
username VARCHAR(50),
6+
password VARCHAR(50),
7+
salary INT,
8+
role VARCHAR(20)
9+
);
10+
11+
GRANT SELECT ON idor_users TO application;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#idor_level_1 {
2+
text-align: center;
3+
}
4+
5+
#loginSection,
6+
#profileSection {
7+
font-size: 15px;
8+
margin-top: 2px;
9+
}
10+
11+
#loggedInDisplay {
12+
margin-top: 10px;
13+
font-size: 14px;
14+
font-weight: bold;
15+
}
16+
17+
#loginBtn,
18+
#fetchBtn {
19+
background: blueviolet;
20+
display: inline-block;
21+
padding: 8px 8px;
22+
margin: 10px;
23+
border: 2px solid transparent;
24+
border-radius: 3px;
25+
transition: 0.2s opacity;
26+
color: #FFF;
27+
font-size: 12px;
28+
}
29+
30+
#response {
31+
margin-top: 10px;
32+
font-size: 16px;
33+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<div id="idor_level_1">
2+
3+
<h3>IDOR Vulnerability - Level 1</h3>
4+
5+
<h5>Login</h5>
6+
<div id="loginSection">
7+
<select id="username">
8+
<option value="Alice">Alice</option>
9+
<option value="Bob">Bob</option>
10+
<option value="Charlie">Charlie</option>
11+
</select>
12+
<input type="password" id="password" value="password" />
13+
<button id="loginBtn">Login</button>
14+
</div>
15+
16+
<div id="loggedInDisplay"></div>
17+
18+
<h5>Fetch Profile</h5>
19+
<div id="profileSection">
20+
<select id="profileId">
21+
<option value="1">1 - Alice</option>
22+
<option value="2">2 - Bob</option>
23+
<option value="3">3 - Charlie</option>
24+
</select>
25+
<button id="fetchBtn">Fetch Profile</button>
26+
</div>
27+
28+
<div id="response"></div>
29+
30+
</div>
31+
32+
<script src="IDOR.js"></script>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
function showResponse(data) {
2+
document.getElementById("response").innerHTML = data.content;
3+
}
4+
5+
// LOGIN
6+
document.getElementById("loginBtn").addEventListener("click", function () {
7+
let username = document.getElementById("username").value;
8+
let password = document.getElementById("password").value;
9+
10+
let url = getUrlForVulnerabilityLevel();
11+
let queryParams = "?username=" + username + "&password=" + password;
12+
13+
doGetAjaxCall(
14+
function (data) {
15+
if (data.isValid) {
16+
document.getElementById("loggedInDisplay").innerHTML = data.content;
17+
} else {
18+
document.getElementById("loggedInDisplay").innerHTML = data.content;
19+
}
20+
},
21+
url + queryParams,
22+
true
23+
);
24+
});
25+
26+
// FETCH PROFILE
27+
document.getElementById("fetchBtn").addEventListener("click", function () {
28+
let id = document.getElementById("profileId").value;
29+
30+
let url = getUrlForVulnerabilityLevel() + "?id=" + id;
31+
32+
doGetAjaxCall(showResponse, url, true);
33+
});

0 commit comments

Comments
 (0)