Skip to content

Commit 8522e52

Browse files
committed
feat: Add IDOR Vilnerability
1 parent 31b2c65 commit 8522e52

File tree

13 files changed

+455
-2
lines changed

13 files changed

+455
-2
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package org.sasanlabs.service.vulnerability.idor;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import org.sasanlabs.internal.utility.LevelConstants;
6+
import org.sasanlabs.internal.utility.Variant;
7+
import org.sasanlabs.internal.utility.annotations.AttackVector;
8+
import org.sasanlabs.internal.utility.annotations.VulnerableAppRequestMapping;
9+
import org.sasanlabs.internal.utility.annotations.VulnerableAppRestController;
10+
import org.sasanlabs.service.vulnerability.bean.GenericVulnerabilityResponseBean;
11+
import org.sasanlabs.vulnerability.types.VulnerabilityType;
12+
import org.springframework.http.HttpStatus;
13+
import org.springframework.http.ResponseEntity;
14+
import org.springframework.web.bind.annotation.RequestParam;
15+
16+
@VulnerableAppRestController(descriptionLabel = "IDOR_VULNERABILITY", value = "IDORVulnerability")
17+
public class IDORVulnerability {
18+
19+
private static final String USER_ID = "userId";
20+
21+
private static final Map<String, String> userData = new HashMap<>();
22+
23+
static {
24+
userData.put("101", "User 101 - Account Balance: ₹5000");
25+
userData.put("102", "User 102 - Account Balance: ₹15000");
26+
userData.put("103", "User 103 - Account Balance: ₹25000");
27+
}
28+
29+
private ResponseEntity<GenericVulnerabilityResponseBean<String>> getResponse(String userId) {
30+
String data = userData.getOrDefault(userId, "User not found");
31+
return new ResponseEntity<>(
32+
new GenericVulnerabilityResponseBean<>(data, true), HttpStatus.OK);
33+
}
34+
35+
// LEVEL 1 - Direct access without any check
36+
@AttackVector(
37+
vulnerabilityExposed = VulnerabilityType.INSECURE_DIRECT_OBJECT_REFERENCE,
38+
description = "IDOR_LEVEL_1_NO_AUTH_CHECK",
39+
payload = "Try changing userId parameter to access other users")
40+
@VulnerableAppRequestMapping(value = LevelConstants.LEVEL_1, htmlTemplate = "LEVEL_1/IDOR")
41+
public ResponseEntity<GenericVulnerabilityResponseBean<String>> idorLevel1(
42+
@RequestParam(USER_ID) String userId) {
43+
44+
// No authentication or authorization
45+
return getResponse(userId);
46+
}
47+
48+
// LEVEL 2 - Fake logged in user but no ownership validation
49+
@AttackVector(
50+
vulnerabilityExposed = VulnerabilityType.INSECURE_DIRECT_OBJECT_REFERENCE,
51+
description = "IDOR_LEVEL_2_FAKE_LOGIN_NO_AUTHZ",
52+
payload = "Logged in user is 101 but try accessing 102")
53+
@VulnerableAppRequestMapping(value = LevelConstants.LEVEL_2, htmlTemplate = "LEVEL_2/IDOR")
54+
public ResponseEntity<GenericVulnerabilityResponseBean<String>> idorLevel2(
55+
@RequestParam(USER_ID) String userId,
56+
@RequestParam("loggedInUser") String loggedInUser) {
57+
58+
// Still no ownership check
59+
return getResponse(userId);
60+
}
61+
62+
// LEVEL 3 - Secure version (Proper Fix)
63+
@AttackVector(
64+
vulnerabilityExposed = VulnerabilityType.INSECURE_DIRECT_OBJECT_REFERENCE,
65+
description = "IDOR_LEVEL_3_SECURE",
66+
payload = "Access only allowed to your own account")
67+
@VulnerableAppRequestMapping(
68+
value = LevelConstants.LEVEL_3,
69+
htmlTemplate = "LEVEL_3/IDOR",
70+
variant = Variant.SECURE)
71+
public ResponseEntity<GenericVulnerabilityResponseBean<String>> idorLevel3(
72+
@RequestParam(USER_ID) String userId,
73+
@RequestParam("loggedInUser") String loggedInUser) {
74+
75+
if (!userId.equals(loggedInUser)) {
76+
return new ResponseEntity<>(
77+
new GenericVulnerabilityResponseBean<>(
78+
"Access Denied - Unauthorized Access", false),
79+
HttpStatus.OK);
80+
}
81+
82+
return getResponse(userId);
83+
}
84+
}

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;

src/main/resources/i18n/messages.properties

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,4 +314,11 @@ 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+
322+
IDOR_LEVEL_1_NO_AUTH_CHECK=User can access any user's data by modifying userId parameter.
323+
IDOR_LEVEL_2_FAKE_LOGIN_NO_AUTHZ=Authentication present but no ownership validation is performed.
324+
IDOR_LEVEL_3_SECURE=Secure implementation with proper ownership validation.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#IDORVulnerability {
2+
text-align: center;
3+
margin-top: 20px;
4+
}
5+
6+
.info-box {
7+
background-color: #f4f4f4;
8+
padding: 15px;
9+
margin: 20px auto;
10+
width: 60%;
11+
border-radius: 6px;
12+
text-align: left;
13+
}
14+
15+
.input-section {
16+
margin: 20px;
17+
}
18+
19+
.input-section input,
20+
.input-section select {
21+
padding: 6px;
22+
margin: 5px;
23+
}
24+
25+
#fetchDetails {
26+
background-color: darkred;
27+
color: white;
28+
padding: 8px 12px;
29+
border: none;
30+
border-radius: 4px;
31+
cursor: pointer;
32+
}
33+
34+
#fetchDetails:hover {
35+
background-color: crimson;
36+
}
37+
38+
.response-box {
39+
margin-top: 20px;
40+
font-size: 18px;
41+
font-weight: bold;
42+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<div id="IDORVulnerability">
2+
<h2>IDOR Vulnerability Demo</h2>
3+
4+
<div class="info-box">
5+
<p id="scenarioText">
6+
You are logged in as a user. Try accessing another user's account.
7+
</p>
8+
9+
<p><strong>Available Users:</strong></p>
10+
<ul>
11+
<li>101 - Alice</li>
12+
<li>102 - Bob</li>
13+
<li>103 - Charlie</li>
14+
</ul>
15+
</div>
16+
17+
<div class="input-section">
18+
<label>User ID (Target Account):</label>
19+
<input type="text" id="userId" placeholder="Enter userId (e.g., 101)" />
20+
21+
<button id="fetchDetails">Fetch Details</button>
22+
</div>
23+
24+
<div id="response" class="response-box"></div>
25+
</div>
26+
27+
<script src="IDOR.js"></script>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
function addingEventListenerToFetchData() {
2+
document
3+
.getElementById("fetchDetails")
4+
.addEventListener("click", function () {
5+
6+
let userId = document.getElementById("userId").value;
7+
let url = getUrlForVulnerabilityLevel();
8+
let queryParams = "?userId=" + userId;
9+
10+
let loggedInUserField = document.getElementById("loggedInUser");
11+
12+
if (loggedInUserField) {
13+
queryParams += "&loggedInUser=" + loggedInUserField.value;
14+
}
15+
16+
doGetAjaxCall(fetchDataCallback, url + queryParams, true);
17+
});
18+
}
19+
20+
addingEventListenerToFetchData();
21+
22+
function fetchDataCallback(data) {
23+
document.getElementById("response").innerHTML = data.content;
24+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#IDORVulnerability {
2+
text-align: center;
3+
margin-top: 20px;
4+
}
5+
6+
.info-box {
7+
background-color: #f4f4f4;
8+
padding: 15px;
9+
margin: 20px auto;
10+
width: 60%;
11+
border-radius: 6px;
12+
text-align: left;
13+
}
14+
15+
.input-section {
16+
margin: 20px;
17+
}
18+
19+
.input-section input,
20+
.input-section select {
21+
padding: 6px;
22+
margin: 5px;
23+
}
24+
25+
#fetchDetails {
26+
background-color: darkred;
27+
color: white;
28+
padding: 8px 12px;
29+
border: none;
30+
border-radius: 4px;
31+
cursor: pointer;
32+
}
33+
34+
#fetchDetails:hover {
35+
background-color: crimson;
36+
}
37+
38+
.response-box {
39+
margin-top: 20px;
40+
font-size: 18px;
41+
font-weight: bold;
42+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<div id="IDORVulnerability">
2+
<h2>IDOR Vulnerability Demo</h2>
3+
4+
<div class="info-box">
5+
<p id="scenarioText">
6+
You are logged in as a user. Try accessing another user's account by modifying the <code>userId</code> parameter.
7+
</p>
8+
9+
<p><strong>Available Users:</strong></p>
10+
<ul>
11+
<li>101 - Alice</li>
12+
<li>102 - Bob</li>
13+
<li>103 - Charlie</li>
14+
</ul>
15+
</div>
16+
17+
<div class="input-section">
18+
<label>User ID (Target Account):</label>
19+
<input type="text" id="userId" placeholder="Enter userId (e.g., 101)" />
20+
21+
<label>Logged In User:</label>
22+
<select id="loggedInUser">
23+
<option value="101">101 - Alice</option>
24+
<option value="102">102 - Bob</option>
25+
<option value="103">103 - Charlie</option>
26+
</select>
27+
28+
<button id="fetchDetails">Fetch Details</button>
29+
</div>
30+
31+
<div id="response" class="response-box"></div>
32+
</div>
33+
34+
<script src="IDOR.js"></script>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
function addingEventListenerToFetchData() {
2+
document
3+
.getElementById("fetchDetails")
4+
.addEventListener("click", function () {
5+
6+
let userId = document.getElementById("userId").value;
7+
let url = getUrlForVulnerabilityLevel();
8+
let queryParams = "?userId=" + userId;
9+
10+
let loggedInUserField = document.getElementById("loggedInUser");
11+
12+
if (loggedInUserField) {
13+
queryParams += "&loggedInUser=" + loggedInUserField.value;
14+
}
15+
16+
doGetAjaxCall(fetchDataCallback, url + queryParams, true);
17+
});
18+
}
19+
20+
addingEventListenerToFetchData();
21+
22+
function fetchDataCallback(data) {
23+
document.getElementById("response").innerHTML = data.content;
24+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#IDORVulnerability {
2+
text-align: center;
3+
margin-top: 20px;
4+
}
5+
6+
.info-box {
7+
background-color: #f4f4f4;
8+
padding: 15px;
9+
margin: 20px auto;
10+
width: 60%;
11+
border-radius: 6px;
12+
text-align: left;
13+
}
14+
15+
.input-section {
16+
margin: 20px;
17+
}
18+
19+
.input-section input,
20+
.input-section select {
21+
padding: 6px;
22+
margin: 5px;
23+
}
24+
25+
#fetchDetails {
26+
background-color: darkred;
27+
color: white;
28+
padding: 8px 12px;
29+
border: none;
30+
border-radius: 4px;
31+
cursor: pointer;
32+
}
33+
34+
#fetchDetails:hover {
35+
background-color: crimson;
36+
}
37+
38+
.response-box {
39+
margin-top: 20px;
40+
font-size: 18px;
41+
font-weight: bold;
42+
}

0 commit comments

Comments
 (0)