@hoanghainh/komagu 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +1 -1
- package/assess/lsa/examples/eval-run-4/ground-truth.json +823 -0
- package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/ai-run-stats.json +58 -0
- package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/coverage.json +85 -0
- package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/findings/priv-privilege-001.json +822 -0
- package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/findings/priv-privilege-002.json +393 -0
- package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/findings.json +4477 -0
- package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/mass-assignment.candidates.json +232 -0
- package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/routes/routes-001.json +425 -0
- package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/routes/routes-002.json +333 -0
- package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/routes/routes-003.json +400 -0
- package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/routes/routes-004.json +403 -0
- package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/routes/routes-005.json +398 -0
- package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/routes/routes-006.json +63 -0
- package/assess/lsa/examples/eval-run-4/iter0-gptoss-a/score.json +120 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/ai-run-stats.json +55 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/coverage.json +50 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/findings/priv-privilege-001.json +613 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/findings/priv-privilege-002.json +287 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/findings.json +4870 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/mass-assignment.candidates.json +232 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/routes/routes-001.json +425 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/routes/routes-002.json +333 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/routes/routes-003.json +400 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/routes/routes-004.json +403 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/routes/routes-005.json +398 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/routes/routes-006.json +63 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-a/score.json +107 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/ai-run-stats.json +58 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/coverage.json +92 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/findings/priv-privilege-001.json +645 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/findings/priv-privilege-002.json +222 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/findings.json +4859 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/mass-assignment.candidates.json +232 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/routes/routes-001.json +425 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/routes/routes-002.json +333 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/routes/routes-003.json +400 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/routes/routes-004.json +403 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/routes/routes-005.json +398 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/routes/routes-006.json +63 -0
- package/assess/lsa/examples/eval-run-4/iter1-gptoss-b/score.json +121 -0
- package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/coverage.json +111 -0
- package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/findings/priv-privilege-001.json +6 -0
- package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/findings/priv-privilege-002.json +344 -0
- package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/findings.json +3544 -0
- package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/mass-assignment.candidates.json +232 -0
- package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/routes/routes-001.json +425 -0
- package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/routes/routes-002.json +333 -0
- package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/routes/routes-003.json +400 -0
- package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/routes/routes-004.json +403 -0
- package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/routes/routes-005.json +398 -0
- package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/routes/routes-006.json +63 -0
- package/assess/lsa/examples/eval-run-4/iter1-qwen-a-incomplete/score.json +123 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/ai-run-stats.json +50 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/coverage.json +42 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/findings/priv-privilege-001.json +540 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/findings/priv-privilege-002.json +238 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/findings.json +4383 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/findings.sarif +8729 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/mass-assignment.candidates.json +232 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/report.ja.md +1911 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/report.md +1911 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/routes/routes-001.json +425 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/routes/routes-002.json +333 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/routes/routes-003.json +400 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/routes/routes-004.json +403 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/routes/routes-005.json +398 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/routes/routes-006.json +63 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/score.json +101 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-a/stack-profile.json +226 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/ai-run-stats.json +54 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/coverage.json +66 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/findings/priv-privilege-001.json +440 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/findings/priv-privilege-002.json +229 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/findings.json +3424 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/mass-assignment.candidates.json +232 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/routes/routes-001.json +425 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/routes/routes-002.json +333 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/routes/routes-003.json +400 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/routes/routes-004.json +403 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/routes/routes-005.json +398 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/routes/routes-006.json +63 -0
- package/assess/lsa/examples/eval-run-4/iter2-gptoss-b/score.json +120 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/ai-run-stats.json +88 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/coverage.json +36 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/findings/priv-privilege-001.json +530 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/findings/priv-privilege-002.json +248 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/findings.json +4766 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/findings.sarif +9326 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/mass-assignment.candidates.json +232 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/report.ja.md +2011 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/report.md +2011 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/routes/routes-001.json +425 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/routes/routes-002.json +333 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/routes/routes-003.json +400 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/routes/routes-004.json +403 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/routes/routes-005.json +398 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/routes/routes-006.json +63 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/score.json +80 -0
- package/assess/lsa/examples/eval-run-5/gptoss-a/stack-profile.json +226 -0
- package/assess/lsa/examples/eval-run-5/gptoss-b/ai-run-stats.json +83 -0
- package/assess/lsa/examples/eval-run-5/gptoss-b/coverage.json +44 -0
- package/assess/lsa/examples/eval-run-5/gptoss-b/findings/priv-privilege-001.json +579 -0
- package/assess/lsa/examples/eval-run-5/gptoss-b/findings/priv-privilege-002.json +268 -0
- package/assess/lsa/examples/eval-run-5/gptoss-b/findings.json +3913 -0
- package/assess/lsa/examples/eval-run-5/gptoss-b/mass-assignment.candidates.json +232 -0
- package/assess/lsa/examples/eval-run-5/gptoss-b/routes/routes-001.json +425 -0
- package/assess/lsa/examples/eval-run-5/gptoss-b/routes/routes-002.json +333 -0
- package/assess/lsa/examples/eval-run-5/gptoss-b/routes/routes-003.json +400 -0
- package/assess/lsa/examples/eval-run-5/gptoss-b/routes/routes-004.json +403 -0
- package/assess/lsa/examples/eval-run-5/gptoss-b/routes/routes-005.json +398 -0
- package/assess/lsa/examples/eval-run-5/gptoss-b/routes/routes-006.json +63 -0
- package/assess/lsa/examples/eval-run-5/gptoss-b/score.json +94 -0
- package/assess/lsa/examples/eval-run-5/ground-truth.json +823 -0
- package/assess/lsa/knowledge/injection-patterns.md +8 -1
- package/assess/lsa/knowledge/stacks/nextjs.md +62 -0
- package/assess/lsa/prompts/35-privilege-auditor.md +3 -1
- package/assess/lsa/prompts/_shared-rules.md +1 -0
- package/assess/lsa/scripts/detect_stack.py +5 -1
- package/assess/lsa/scripts/find_injection.py +34 -5
- package/assess/lsa/scripts/lsa_common.py +9 -2
- package/assess/lsa/scripts/make_batches.py +2 -1
- package/assess/lsa/scripts/map_routes.py +10 -2
- package/assess/lsa/scripts/nextjs_routes.py +236 -0
- package/assess/lsa/templates/audit-config.json +1 -1
- package/assess/lsa/tests/EVAL-RESULTS.md +156 -0
- package/assess/lsa/tests/score_findings.py +58 -0
- package/assess/lsa/tests/test_default_scope.py +86 -0
- package/assess/lsa/tests/test_eval_run_4.py +73 -0
- package/assess/lsa/tests/test_eval_run_5.py +73 -0
- package/assess/lsa/tests/test_js_tagged_sql.py +112 -0
- package/assess/lsa/tests/test_nextjs.py +198 -0
- package/assess/lsa/tests/test_privilege_tier2.py +30 -0
- package/operate/ai/anchoring.py +130 -0
- package/operate/ai/client.py +13 -0
- package/operate/ai/context.py +105 -0
- package/operate/ai/runner.py +197 -69
- package/operate/cli/__init__.py +1 -1
- package/operate/cli/commands/scan.py +1 -1
- package/operate/tests/test_ai_anchoring.py +184 -0
- package/operate/tests/test_headless_ai.py +148 -1
- package/package.json +1 -1
|
@@ -0,0 +1,3544 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generated_by": "lsa merge_findings.py 0.3.1",
|
|
3
|
+
"summary": {
|
|
4
|
+
"total_reported": 72,
|
|
5
|
+
"active": 72,
|
|
6
|
+
"rejected_by_verifier": 0,
|
|
7
|
+
"by_severity": {
|
|
8
|
+
"critical": 33,
|
|
9
|
+
"high": 39,
|
|
10
|
+
"medium": 0,
|
|
11
|
+
"low": 0,
|
|
12
|
+
"info": 0
|
|
13
|
+
},
|
|
14
|
+
"by_category": {
|
|
15
|
+
"bola": 39,
|
|
16
|
+
"sql_injection": 33,
|
|
17
|
+
"nosql_injection": 0
|
|
18
|
+
},
|
|
19
|
+
"by_status": {
|
|
20
|
+
"unverified": 52,
|
|
21
|
+
"confirmed": 20,
|
|
22
|
+
"likely": 0,
|
|
23
|
+
"rejected": 0,
|
|
24
|
+
"needs_review": 0
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"findings": [
|
|
28
|
+
{
|
|
29
|
+
"finding_id": "INJ-I-0001-1",
|
|
30
|
+
"category": "sql_injection",
|
|
31
|
+
"subtype": "string_concatenation",
|
|
32
|
+
"title": "SQL Injection in member.asp via manual quote escaping",
|
|
33
|
+
"candidate_id": "I-0001",
|
|
34
|
+
"location": {
|
|
35
|
+
"file": "classic-asp/shop/member.asp",
|
|
36
|
+
"start_line": 6,
|
|
37
|
+
"end_line": 8
|
|
38
|
+
},
|
|
39
|
+
"evidence": [
|
|
40
|
+
{
|
|
41
|
+
"file": "classic-asp/shop/member.asp",
|
|
42
|
+
"line": 6,
|
|
43
|
+
"code": "sql = \"UPDATE members SET name = '\" & Replace(Request.Form(\"name\"), \"'\", \"''\") & \"'\"",
|
|
44
|
+
"why": "User input concatenated into SQL string"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"file": "classic-asp/shop/member.asp",
|
|
48
|
+
"line": 8,
|
|
49
|
+
"code": "conn.Execute sql",
|
|
50
|
+
"why": "Sink executes constructed query"
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"data_flow": [
|
|
54
|
+
"Request.Form(\"name\") (member.asp:6)",
|
|
55
|
+
"$u",
|
|
56
|
+
"$sql (member.asp:6)",
|
|
57
|
+
"conn.Execute (member.asp:8)"
|
|
58
|
+
],
|
|
59
|
+
"attack_scenario": "A crafted username containing special characters bypasses the manual quote escaping and alters the SQL logic.",
|
|
60
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
61
|
+
"severity": "critical",
|
|
62
|
+
"confidence": "high",
|
|
63
|
+
"cwe": [
|
|
64
|
+
"CWE-89"
|
|
65
|
+
],
|
|
66
|
+
"owasp": [
|
|
67
|
+
"A03:2021"
|
|
68
|
+
],
|
|
69
|
+
"recommendation": "Use parameterized queries",
|
|
70
|
+
"status": "unverified"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"finding_id": "INJ-I-0002-1",
|
|
74
|
+
"category": "sql_injection",
|
|
75
|
+
"subtype": "string_concatenation",
|
|
76
|
+
"title": "SQL Injection in member.asp via direct concatenation of form data",
|
|
77
|
+
"candidate_id": "I-0002",
|
|
78
|
+
"location": {
|
|
79
|
+
"file": "classic-asp/shop/member.asp",
|
|
80
|
+
"start_line": 7,
|
|
81
|
+
"end_line": 8
|
|
82
|
+
},
|
|
83
|
+
"evidence": [
|
|
84
|
+
{
|
|
85
|
+
"file": "classic-asp/shop/member.asp",
|
|
86
|
+
"line": 7,
|
|
87
|
+
"code": "sql = sql & \" WHERE member_id = \" & Request.Form(\"member_id\")",
|
|
88
|
+
"why": "User input concatenated into SQL string"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"file": "classic-asp/shop/member.asp",
|
|
92
|
+
"line": 8,
|
|
93
|
+
"code": "conn.Execute sql",
|
|
94
|
+
"why": "Sink executes constructed query"
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"data_flow": [
|
|
98
|
+
"Request.Form(\"member_id\") (member.asp:7)",
|
|
99
|
+
"$sql (member.asp:7)",
|
|
100
|
+
"conn.Execute (member.asp:8)"
|
|
101
|
+
],
|
|
102
|
+
"attack_scenario": "A crafted member_id changes the WHERE clause and bypasses the intended filtering.",
|
|
103
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
104
|
+
"severity": "critical",
|
|
105
|
+
"confidence": "high",
|
|
106
|
+
"cwe": [
|
|
107
|
+
"CWE-89"
|
|
108
|
+
],
|
|
109
|
+
"owasp": [
|
|
110
|
+
"A03:2021"
|
|
111
|
+
],
|
|
112
|
+
"recommendation": "Use parameterized queries",
|
|
113
|
+
"status": "unverified"
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"finding_id": "INJ-I-0003-1",
|
|
117
|
+
"category": "sql_injection",
|
|
118
|
+
"subtype": "string_concatenation",
|
|
119
|
+
"title": "SQL Injection in member.asp via direct concatenation of query string data",
|
|
120
|
+
"candidate_id": "I-0003",
|
|
121
|
+
"location": {
|
|
122
|
+
"file": "classic-asp/shop/member.asp",
|
|
123
|
+
"start_line": 10,
|
|
124
|
+
"end_line": 10
|
|
125
|
+
},
|
|
126
|
+
"evidence": [
|
|
127
|
+
{
|
|
128
|
+
"file": "classic-asp/shop/member.asp",
|
|
129
|
+
"line": 10,
|
|
130
|
+
"code": "conn.Execute \"DELETE FROM members WHERE member_id = \" & Request.QueryString(\"member_id\")",
|
|
131
|
+
"why": "User input concatenated into SQL string"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"file": "classic-asp/shop/member.asp",
|
|
135
|
+
"line": 10,
|
|
136
|
+
"code": "conn.Execute ...",
|
|
137
|
+
"why": "Sink executes constructed query"
|
|
138
|
+
}
|
|
139
|
+
],
|
|
140
|
+
"data_flow": [
|
|
141
|
+
"Request.QueryString(\"member_id\") (member.asp:10)",
|
|
142
|
+
"$sql (member.asp:10)",
|
|
143
|
+
"conn.Execute (member.asp:10)"
|
|
144
|
+
],
|
|
145
|
+
"attack_scenario": "A crafted member_id changes the WHERE clause and bypasses the intended filtering.",
|
|
146
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
147
|
+
"severity": "critical",
|
|
148
|
+
"confidence": "high",
|
|
149
|
+
"cwe": [
|
|
150
|
+
"CWE-89"
|
|
151
|
+
],
|
|
152
|
+
"owasp": [
|
|
153
|
+
"A03:2021"
|
|
154
|
+
],
|
|
155
|
+
"recommendation": "Use parameterized queries",
|
|
156
|
+
"status": "unverified"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"finding_id": "INJ-I-0004-1",
|
|
160
|
+
"category": "sql_injection",
|
|
161
|
+
"subtype": "string_concatenation",
|
|
162
|
+
"title": "SQL Injection in EditOrder.aspx.vb via direct concatenation of request data",
|
|
163
|
+
"candidate_id": "I-0004",
|
|
164
|
+
"location": {
|
|
165
|
+
"file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
|
|
166
|
+
"start_line": 6,
|
|
167
|
+
"end_line": 12
|
|
168
|
+
},
|
|
169
|
+
"evidence": [
|
|
170
|
+
{
|
|
171
|
+
"file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
|
|
172
|
+
"line": 5,
|
|
173
|
+
"code": "id = Request(\"id\")",
|
|
174
|
+
"why": "Source: User input from request"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
|
|
178
|
+
"line": 6,
|
|
179
|
+
"code": "Set rs = conn.Execute(\"SELECT * FROM orders WHERE id = \" & id)",
|
|
180
|
+
"why": "User input concatenated into SQL string"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
|
|
184
|
+
"line": 12,
|
|
185
|
+
"code": "Set rs = conn.Execute(...)",
|
|
186
|
+
"why": "Sink executes constructed query"
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
"data_flow": [
|
|
190
|
+
"Request(\"id\") (EditOrder.aspx.vb:5)",
|
|
191
|
+
"$id",
|
|
192
|
+
"$sql (EditOrder.aspx.vb:6)",
|
|
193
|
+
"conn.Execute (EditOrder.aspx.vb:12)"
|
|
194
|
+
],
|
|
195
|
+
"attack_scenario": "A crafted id changes the WHERE clause and bypasses the intended filtering.",
|
|
196
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
197
|
+
"severity": "critical",
|
|
198
|
+
"confidence": "high",
|
|
199
|
+
"cwe": [
|
|
200
|
+
"CWE-89"
|
|
201
|
+
],
|
|
202
|
+
"owasp": [
|
|
203
|
+
"A03:2021"
|
|
204
|
+
],
|
|
205
|
+
"recommendation": "Use parameterized queries",
|
|
206
|
+
"status": "unverified"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"finding_id": "INJ-I-0006-1",
|
|
210
|
+
"category": "sql_injection",
|
|
211
|
+
"subtype": "template_placeholder",
|
|
212
|
+
"title": "SQL Injection in OrderService.cfc via raw ColdFusion interpolation",
|
|
213
|
+
"candidate_id": "I-0006",
|
|
214
|
+
"location": {
|
|
215
|
+
"file": "coldfusion/app/OrderService.cfc",
|
|
216
|
+
"start_line": 5,
|
|
217
|
+
"end_line": 7
|
|
218
|
+
},
|
|
219
|
+
"evidence": [
|
|
220
|
+
{
|
|
221
|
+
"file": "coldfusion/app/OrderService.cfc",
|
|
222
|
+
"line": 3,
|
|
223
|
+
"code": "<cfargument name=\"orderId\" type=\"string\" required=\"true\">",
|
|
224
|
+
"why": "Source: Function argument passed by caller"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"file": "coldfusion/app/OrderService.cfc",
|
|
228
|
+
"line": 5,
|
|
229
|
+
"code": "SELECT * FROM orders WHERE id = #arguments.orderId#",
|
|
230
|
+
"why": "User input interpolated into SQL string"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"file": "coldfusion/app/OrderService.cfc",
|
|
234
|
+
"line": 7,
|
|
235
|
+
"code": "<cfreturn q>",
|
|
236
|
+
"why": "Sink returns query result"
|
|
237
|
+
}
|
|
238
|
+
],
|
|
239
|
+
"data_flow": [
|
|
240
|
+
"arguments.orderId (OrderService.cfc:3)",
|
|
241
|
+
"#arguments.orderId#",
|
|
242
|
+
"SELECT ... (OrderService.cfc:5)"
|
|
243
|
+
],
|
|
244
|
+
"attack_scenario": "A malicious orderId passed to the remote function alters the SQL logic.",
|
|
245
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
246
|
+
"severity": "critical",
|
|
247
|
+
"confidence": "high",
|
|
248
|
+
"cwe": [
|
|
249
|
+
"CWE-89"
|
|
250
|
+
],
|
|
251
|
+
"owasp": [
|
|
252
|
+
"A03:2021"
|
|
253
|
+
],
|
|
254
|
+
"recommendation": "Use parameterized queries or cfqueryparam",
|
|
255
|
+
"status": "unverified"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"finding_id": "INJ-I-0007-1",
|
|
259
|
+
"category": "sql_injection",
|
|
260
|
+
"subtype": "template_placeholder",
|
|
261
|
+
"title": "SQL Injection in order.cfm via raw ColdFusion interpolation of URL parameter",
|
|
262
|
+
"candidate_id": "I-0007",
|
|
263
|
+
"location": {
|
|
264
|
+
"file": "coldfusion/app/order.cfm",
|
|
265
|
+
"start_line": 3,
|
|
266
|
+
"end_line": 4
|
|
267
|
+
},
|
|
268
|
+
"evidence": [
|
|
269
|
+
{
|
|
270
|
+
"file": "coldfusion/app/order.cfm",
|
|
271
|
+
"line": 3,
|
|
272
|
+
"code": "SELECT * FROM orders WHERE id = #url.id#",
|
|
273
|
+
"why": "User input interpolated into SQL string"
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
"file": "coldfusion/app/order.cfm",
|
|
277
|
+
"line": 4,
|
|
278
|
+
"code": "</cfquery>",
|
|
279
|
+
"why": "Sink executes query"
|
|
280
|
+
}
|
|
281
|
+
],
|
|
282
|
+
"data_flow": [
|
|
283
|
+
"url.id (order.cfm:3)",
|
|
284
|
+
"#url.id#",
|
|
285
|
+
"SELECT ... (order.cfm:3)"
|
|
286
|
+
],
|
|
287
|
+
"attack_scenario": "A malicious id passed via URL alters the SQL logic.",
|
|
288
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
289
|
+
"severity": "critical",
|
|
290
|
+
"confidence": "high",
|
|
291
|
+
"cwe": [
|
|
292
|
+
"CWE-89"
|
|
293
|
+
],
|
|
294
|
+
"owasp": [
|
|
295
|
+
"A03:2021"
|
|
296
|
+
],
|
|
297
|
+
"recommendation": "Use parameterized queries or cfqueryparam",
|
|
298
|
+
"status": "unverified"
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"finding_id": "INJ-I-0008",
|
|
302
|
+
"category": "sql_injection",
|
|
303
|
+
"subtype": "string_concatenation",
|
|
304
|
+
"title": "SQL Injection in InvoiceController.cs:28 via POST parameter customer",
|
|
305
|
+
"candidate_id": "I-0008",
|
|
306
|
+
"location": {
|
|
307
|
+
"file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
|
|
308
|
+
"start_line": 26,
|
|
309
|
+
"end_line": 28
|
|
310
|
+
},
|
|
311
|
+
"evidence": [
|
|
312
|
+
{
|
|
313
|
+
"file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
|
|
314
|
+
"line": 26,
|
|
315
|
+
"code": "public ActionResult Search(string customer)",
|
|
316
|
+
"why": "source: POST parameter"
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
|
|
320
|
+
"line": 28,
|
|
321
|
+
"code": "var list = db.Database.SqlQuery<Invoice>(\"SELECT * FROM Invoices WHERE Customer = '\" + customer + \"'\").ToList();",
|
|
322
|
+
"why": "string concatenation into SQL without parameterization"
|
|
323
|
+
}
|
|
324
|
+
],
|
|
325
|
+
"data_flow": [
|
|
326
|
+
"customer (InvoiceController.cs:26)",
|
|
327
|
+
"$customer",
|
|
328
|
+
"SqlQuery (InvoiceController.cs:28)"
|
|
329
|
+
],
|
|
330
|
+
"attack_scenario": "A crafted customer parameter changes the WHERE clause and bypasses the intended filter.",
|
|
331
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
332
|
+
"severity": "critical",
|
|
333
|
+
"confidence": "high",
|
|
334
|
+
"cwe": [
|
|
335
|
+
"CWE-89"
|
|
336
|
+
],
|
|
337
|
+
"owasp": [
|
|
338
|
+
"A03:2021"
|
|
339
|
+
],
|
|
340
|
+
"recommendation": "Use parameterized queries with Entity Framework or raw SQL with parameters",
|
|
341
|
+
"status": "unverified"
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
"finding_id": "INJ-I-0009-1",
|
|
345
|
+
"category": "sql_injection",
|
|
346
|
+
"subtype": "string_concatenation",
|
|
347
|
+
"title": "SQL Injection in OrderDetail.aspx.cs via direct concatenation of query string data",
|
|
348
|
+
"candidate_id": "I-0009",
|
|
349
|
+
"location": {
|
|
350
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
351
|
+
"start_line": 13,
|
|
352
|
+
"end_line": 15
|
|
353
|
+
},
|
|
354
|
+
"evidence": [
|
|
355
|
+
{
|
|
356
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
357
|
+
"line": 10,
|
|
358
|
+
"code": "string id = Request.QueryString[\"id\"];",
|
|
359
|
+
"why": "Source: User input from query string"
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
363
|
+
"line": 13,
|
|
364
|
+
"code": "var cmd = new SqlCommand(\"SELECT * FROM Orders WHERE OrderId = \" + id, conn);",
|
|
365
|
+
"why": "User input concatenated into SQL string"
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
369
|
+
"line": 15,
|
|
370
|
+
"code": "var r = cmd.ExecuteReader();",
|
|
371
|
+
"why": "Sink executes constructed query"
|
|
372
|
+
}
|
|
373
|
+
],
|
|
374
|
+
"data_flow": [
|
|
375
|
+
"Request.QueryString[\"id\"] (OrderDetail.aspx.cs:10)",
|
|
376
|
+
"$id",
|
|
377
|
+
"$cmd (OrderDetail.aspx.cs:13)",
|
|
378
|
+
"cmd.ExecuteReader (OrderDetail.aspx.cs:15)"
|
|
379
|
+
],
|
|
380
|
+
"attack_scenario": "A crafted id changes the WHERE clause and bypasses the intended filtering.",
|
|
381
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
382
|
+
"severity": "critical",
|
|
383
|
+
"confidence": "high",
|
|
384
|
+
"cwe": [
|
|
385
|
+
"CWE-89"
|
|
386
|
+
],
|
|
387
|
+
"owasp": [
|
|
388
|
+
"A03:2021"
|
|
389
|
+
],
|
|
390
|
+
"recommendation": "Use parameterized queries",
|
|
391
|
+
"status": "unverified"
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
"finding_id": "INJ-I-0010-1",
|
|
395
|
+
"category": "sql_injection",
|
|
396
|
+
"subtype": "format_string",
|
|
397
|
+
"title": "SQL Injection in OrderDetail.aspx.cs via string.Format interpolation of request data",
|
|
398
|
+
"candidate_id": "I-0010",
|
|
399
|
+
"location": {
|
|
400
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
401
|
+
"start_line": 19,
|
|
402
|
+
"end_line": 21
|
|
403
|
+
},
|
|
404
|
+
"evidence": [
|
|
405
|
+
{
|
|
406
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
407
|
+
"line": 19,
|
|
408
|
+
"code": "string sql2 = string.Format(\"SELECT * FROM Logs WHERE Ref = '{0}'\", Request[\"ref\"]);",
|
|
409
|
+
"why": "User input interpolated into SQL string"
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
413
|
+
"line": 21,
|
|
414
|
+
"code": "new SqlCommand(sql2, conn).ExecuteReader();",
|
|
415
|
+
"why": "Sink executes constructed query"
|
|
416
|
+
}
|
|
417
|
+
],
|
|
418
|
+
"data_flow": [
|
|
419
|
+
"Request[\"ref\"] (OrderDetail.aspx.cs:19)",
|
|
420
|
+
"$sql2 (OrderDetail.aspx.cs:19)",
|
|
421
|
+
"SqlCommand(sql2) (OrderDetail.aspx.cs:21)"
|
|
422
|
+
],
|
|
423
|
+
"attack_scenario": "A crafted ref changes the WHERE clause and bypasses the intended filtering.",
|
|
424
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
425
|
+
"severity": "critical",
|
|
426
|
+
"confidence": "high",
|
|
427
|
+
"cwe": [
|
|
428
|
+
"CWE-89"
|
|
429
|
+
],
|
|
430
|
+
"owasp": [
|
|
431
|
+
"A03:2021"
|
|
432
|
+
],
|
|
433
|
+
"recommendation": "Use parameterized queries",
|
|
434
|
+
"status": "unverified"
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
"finding_id": "INJ-I-0011-1",
|
|
438
|
+
"category": "sql_injection",
|
|
439
|
+
"subtype": "string_interpolation",
|
|
440
|
+
"title": "SQL Injection in OrderDetail.aspx.cs via string interpolation of form data",
|
|
441
|
+
"candidate_id": "I-0011",
|
|
442
|
+
"location": {
|
|
443
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
444
|
+
"start_line": 20,
|
|
445
|
+
"end_line": 22
|
|
446
|
+
},
|
|
447
|
+
"evidence": [
|
|
448
|
+
{
|
|
449
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
450
|
+
"line": 20,
|
|
451
|
+
"code": "string sql3 = $\"DELETE FROM Carts WHERE CartId = {Request.Form[\"cart\"]}\";",
|
|
452
|
+
"why": "User input interpolated into SQL string"
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
456
|
+
"line": 22,
|
|
457
|
+
"code": "new SqlCommand(sql3, conn).ExecuteNonQuery();",
|
|
458
|
+
"why": "Sink executes constructed query"
|
|
459
|
+
}
|
|
460
|
+
],
|
|
461
|
+
"data_flow": [
|
|
462
|
+
"Request.Form[\"cart\"] (OrderDetail.aspx.cs:20)",
|
|
463
|
+
"$sql3 (OrderDetail.aspx.cs:20)",
|
|
464
|
+
"SqlCommand(sql3) (OrderDetail.aspx.cs:22)"
|
|
465
|
+
],
|
|
466
|
+
"attack_scenario": "A crafted cart changes the WHERE clause and bypasses the intended filtering.",
|
|
467
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
468
|
+
"severity": "critical",
|
|
469
|
+
"confidence": "high",
|
|
470
|
+
"cwe": [
|
|
471
|
+
"CWE-89"
|
|
472
|
+
],
|
|
473
|
+
"owasp": [
|
|
474
|
+
"A03:2021"
|
|
475
|
+
],
|
|
476
|
+
"recommendation": "Use parameterized queries",
|
|
477
|
+
"status": "unverified"
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
"finding_id": "INJ-I-0012-1",
|
|
481
|
+
"category": "sql_injection",
|
|
482
|
+
"subtype": "string_concatenation",
|
|
483
|
+
"title": "SQL Injection in detail.jsp via direct concatenation of request parameter",
|
|
484
|
+
"candidate_id": "I-0012",
|
|
485
|
+
"location": {
|
|
486
|
+
"file": "java-legacy/WebContent/order/detail.jsp",
|
|
487
|
+
"start_line": 5,
|
|
488
|
+
"end_line": 6
|
|
489
|
+
},
|
|
490
|
+
"evidence": [
|
|
491
|
+
{
|
|
492
|
+
"file": "java-legacy/WebContent/order/detail.jsp",
|
|
493
|
+
"line": 3,
|
|
494
|
+
"code": "String no = request.getParameter(\"no\");",
|
|
495
|
+
"why": "Source: User input from request"
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
"file": "java-legacy/WebContent/order/detail.jsp",
|
|
499
|
+
"line": 5,
|
|
500
|
+
"code": "ResultSet rs = c.createStatement().executeQuery(\"SELECT * FROM orders WHERE order_no = '\" + no + \"'\");",
|
|
501
|
+
"why": "User input concatenated into SQL string"
|
|
502
|
+
}
|
|
503
|
+
],
|
|
504
|
+
"data_flow": [
|
|
505
|
+
"request.getParameter(\"no\") (detail.jsp:3)",
|
|
506
|
+
"$no",
|
|
507
|
+
"$sql (detail.jsp:5)"
|
|
508
|
+
],
|
|
509
|
+
"attack_scenario": "A crafted no changes the WHERE clause and bypasses the intended filtering.",
|
|
510
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
511
|
+
"severity": "critical",
|
|
512
|
+
"confidence": "high",
|
|
513
|
+
"cwe": [
|
|
514
|
+
"CWE-89"
|
|
515
|
+
],
|
|
516
|
+
"owasp": [
|
|
517
|
+
"A03:2021"
|
|
518
|
+
],
|
|
519
|
+
"recommendation": "Use parameterized queries",
|
|
520
|
+
"status": "unverified"
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"finding_id": "INJ-I-0013-1",
|
|
524
|
+
"category": "sql_injection",
|
|
525
|
+
"subtype": "string_concatenation",
|
|
526
|
+
"title": "SQL Injection in detail.jsp via direct concatenation of request parameter (duplicate flow)",
|
|
527
|
+
"candidate_id": "I-0013",
|
|
528
|
+
"location": {
|
|
529
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
530
|
+
"start_line": 20,
|
|
531
|
+
"end_line": 22
|
|
532
|
+
},
|
|
533
|
+
"evidence": [
|
|
534
|
+
{
|
|
535
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
536
|
+
"line": 20,
|
|
537
|
+
"code": "string sql3 = $\"DELETE FROM Carts WHERE CartId = {Request.Form[\"cart\"]}\";",
|
|
538
|
+
"why": "User input interpolated into SQL string"
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
542
|
+
"line": 22,
|
|
543
|
+
"code": "new SqlCommand(sql3, conn).ExecuteNonQuery();",
|
|
544
|
+
"why": "Sink executes constructed query"
|
|
545
|
+
}
|
|
546
|
+
],
|
|
547
|
+
"data_flow": [
|
|
548
|
+
"Request.Form[\"cart\"] (OrderDetail.aspx.cs:20)",
|
|
549
|
+
"$sql3 (OrderDetail.aspx.cs:20)",
|
|
550
|
+
"SqlCommand(sql3) (OrderDetail.aspx.cs:22)"
|
|
551
|
+
],
|
|
552
|
+
"attack_scenario": "A crafted cart changes the WHERE clause and bypasses the intended filtering.",
|
|
553
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
554
|
+
"severity": "critical",
|
|
555
|
+
"confidence": "high",
|
|
556
|
+
"cwe": [
|
|
557
|
+
"CWE-89"
|
|
558
|
+
],
|
|
559
|
+
"owasp": [
|
|
560
|
+
"A03:2021"
|
|
561
|
+
],
|
|
562
|
+
"recommendation": "Use parameterized queries",
|
|
563
|
+
"status": "unverified"
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
"finding_id": "INJ-I-0015",
|
|
567
|
+
"category": "sql_injection",
|
|
568
|
+
"subtype": "template_placeholder",
|
|
569
|
+
"title": "SQL Injection in OrderMapper.xml:9 via MyBatis ${orderBy} (dynamic ORDER BY)",
|
|
570
|
+
"candidate_id": "I-0015",
|
|
571
|
+
"location": {
|
|
572
|
+
"file": "java-legacy/src/com/acme/mapper/OrderMapper.xml",
|
|
573
|
+
"start_line": 9,
|
|
574
|
+
"end_line": 9
|
|
575
|
+
},
|
|
576
|
+
"evidence": [
|
|
577
|
+
{
|
|
578
|
+
"file": "java-legacy/src/com/acme/mapper/OrderMapper.xml",
|
|
579
|
+
"line": 9,
|
|
580
|
+
"code": "ORDER BY ${orderBy}",
|
|
581
|
+
"why": "MyBatis ${} performs raw string substitution without parameterization"
|
|
582
|
+
}
|
|
583
|
+
],
|
|
584
|
+
"data_flow": [
|
|
585
|
+
"${orderBy} (OrderMapper.xml:9)",
|
|
586
|
+
"ORDER BY clause"
|
|
587
|
+
],
|
|
588
|
+
"attack_scenario": "A crafted orderBy parameter changes the ORDER BY clause to extract data or bypass filters.",
|
|
589
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
590
|
+
"severity": "critical",
|
|
591
|
+
"confidence": "high",
|
|
592
|
+
"cwe": [
|
|
593
|
+
"CWE-89"
|
|
594
|
+
],
|
|
595
|
+
"owasp": [
|
|
596
|
+
"A03:2021"
|
|
597
|
+
],
|
|
598
|
+
"recommendation": "Use an explicit allow-list for allowed sort columns in MyBatis",
|
|
599
|
+
"status": "unverified"
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
"finding_id": "INJ-I-0016-1",
|
|
603
|
+
"category": "sql_injection",
|
|
604
|
+
"subtype": "string_concatenation",
|
|
605
|
+
"title": "SQL Injection in EditUserAction.java via direct concatenation of request parameter",
|
|
606
|
+
"candidate_id": "I-0016",
|
|
607
|
+
"location": {
|
|
608
|
+
"file": "java-legacy/src/com/acme/struts/EditUserAction.java",
|
|
609
|
+
"start_line": 13,
|
|
610
|
+
"end_line": 14
|
|
611
|
+
},
|
|
612
|
+
"evidence": [
|
|
613
|
+
{
|
|
614
|
+
"file": "java-legacy/src/com/acme/struts/EditUserAction.java",
|
|
615
|
+
"line": 10,
|
|
616
|
+
"code": "String userId = request.getParameter(\"user_id\");",
|
|
617
|
+
"why": "Source: User input from request"
|
|
618
|
+
},
|
|
619
|
+
{
|
|
620
|
+
"file": "java-legacy/src/com/acme/struts/EditUserAction.java",
|
|
621
|
+
"line": 13,
|
|
622
|
+
"code": "PreparedStatement ps = conn.prepareStatement(\"UPDATE users SET email = '\" + email + \"' WHERE id = \" + userId);",
|
|
623
|
+
"why": "User input concatenated into SQL string"
|
|
624
|
+
}
|
|
625
|
+
],
|
|
626
|
+
"data_flow": [
|
|
627
|
+
"request.getParameter(\"user_id\") (EditUserAction.java:10)",
|
|
628
|
+
"$userId",
|
|
629
|
+
"$sql (EditUserAction.java:13)"
|
|
630
|
+
],
|
|
631
|
+
"attack_scenario": "A crafted userId changes the WHERE clause and bypasses the intended filtering.",
|
|
632
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
633
|
+
"severity": "critical",
|
|
634
|
+
"confidence": "high",
|
|
635
|
+
"cwe": [
|
|
636
|
+
"CWE-89"
|
|
637
|
+
],
|
|
638
|
+
"owasp": [
|
|
639
|
+
"A03:2021"
|
|
640
|
+
],
|
|
641
|
+
"recommendation": "Use parameterized queries",
|
|
642
|
+
"status": "unverified"
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
"finding_id": "INJ-I-0017-1",
|
|
646
|
+
"category": "sql_injection",
|
|
647
|
+
"subtype": "string_concatenation",
|
|
648
|
+
"title": "SQL Injection in InvoiceController.java via direct concatenation of request parameter in HQL",
|
|
649
|
+
"candidate_id": "I-0017",
|
|
650
|
+
"location": {
|
|
651
|
+
"file": "java-legacy/src/com/acme/web/InvoiceController.java",
|
|
652
|
+
"start_line": 29,
|
|
653
|
+
"end_line": 30
|
|
654
|
+
},
|
|
655
|
+
"evidence": [
|
|
656
|
+
{
|
|
657
|
+
"file": "java-legacy/src/com/acme/web/InvoiceController.java",
|
|
658
|
+
"line": 28,
|
|
659
|
+
"code": "public String search(@RequestParam(\"customer\") String customer, Model model) {",
|
|
660
|
+
"why": "Source: User input from request"
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
"file": "java-legacy/src/com/acme/web/InvoiceController.java",
|
|
664
|
+
"line": 29,
|
|
665
|
+
"code": "model.addAttribute(\"list\", session.createQuery(\"from Invoice i where i.customer = '\" + customer + \"'\").list());",
|
|
666
|
+
"why": "User input concatenated into SQL string"
|
|
667
|
+
}
|
|
668
|
+
],
|
|
669
|
+
"data_flow": [
|
|
670
|
+
"@RequestParam(\"customer\") (InvoiceController.java:28)",
|
|
671
|
+
"$customer",
|
|
672
|
+
"$sql (InvoiceController.java:29)"
|
|
673
|
+
],
|
|
674
|
+
"attack_scenario": "A crafted customer changes the WHERE clause and bypasses the intended filtering.",
|
|
675
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
676
|
+
"severity": "critical",
|
|
677
|
+
"confidence": "high",
|
|
678
|
+
"cwe": [
|
|
679
|
+
"CWE-89"
|
|
680
|
+
],
|
|
681
|
+
"owasp": [
|
|
682
|
+
"A03:2021"
|
|
683
|
+
],
|
|
684
|
+
"recommendation": "Use parameterized queries or Criteria API",
|
|
685
|
+
"status": "unverified"
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
"finding_id": "INJ-I-0018",
|
|
689
|
+
"category": "sql_injection",
|
|
690
|
+
"subtype": "string_concatenation",
|
|
691
|
+
"title": "SQL Injection in OrderServlet.java:15 via GET parameter orderId",
|
|
692
|
+
"candidate_id": "I-0018",
|
|
693
|
+
"location": {
|
|
694
|
+
"file": "java-legacy/src/com/acme/web/OrderServlet.java",
|
|
695
|
+
"start_line": 11,
|
|
696
|
+
"end_line": 15
|
|
697
|
+
},
|
|
698
|
+
"evidence": [
|
|
699
|
+
{
|
|
700
|
+
"file": "java-legacy/src/com/acme/web/OrderServlet.java",
|
|
701
|
+
"line": 11,
|
|
702
|
+
"code": "String id = request.getParameter(\"orderId\");",
|
|
703
|
+
"why": "source: GET parameter"
|
|
704
|
+
},
|
|
705
|
+
{
|
|
706
|
+
"file": "java-legacy/src/com/acme/web/OrderServlet.java",
|
|
707
|
+
"line": 15,
|
|
708
|
+
"code": "ResultSet rs = stmt.executeQuery(\"SELECT * FROM orders WHERE id = '\" + id + \"'\");",
|
|
709
|
+
"why": "string concatenation into SQL without parameterization"
|
|
710
|
+
}
|
|
711
|
+
],
|
|
712
|
+
"data_flow": [
|
|
713
|
+
"request.getParameter('orderId') (OrderServlet.java:11)",
|
|
714
|
+
"$id",
|
|
715
|
+
"stmt.executeQuery (OrderServlet.java:15)"
|
|
716
|
+
],
|
|
717
|
+
"attack_scenario": "A crafted orderId parameter changes the WHERE clause and bypasses the intended filter.",
|
|
718
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
719
|
+
"severity": "critical",
|
|
720
|
+
"confidence": "high",
|
|
721
|
+
"cwe": [
|
|
722
|
+
"CWE-89"
|
|
723
|
+
],
|
|
724
|
+
"owasp": [
|
|
725
|
+
"A03:2021"
|
|
726
|
+
],
|
|
727
|
+
"recommendation": "Use PreparedStatement with bound parameters",
|
|
728
|
+
"status": "unverified"
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
"finding_id": "INJ-I-0020-1",
|
|
732
|
+
"category": "sql_injection",
|
|
733
|
+
"subtype": "string_concatenation",
|
|
734
|
+
"title": "SQL Injection in orders.js via direct concatenation of query parameter",
|
|
735
|
+
"candidate_id": "I-0020",
|
|
736
|
+
"location": {
|
|
737
|
+
"file": "node-express/routes/orders.js",
|
|
738
|
+
"start_line": 17,
|
|
739
|
+
"end_line": 18
|
|
740
|
+
},
|
|
741
|
+
"evidence": [
|
|
742
|
+
{
|
|
743
|
+
"file": "node-express/routes/orders.js",
|
|
744
|
+
"line": 16,
|
|
745
|
+
"code": "router.get('/reports', ensureLoggedIn, function (req, res) {",
|
|
746
|
+
"why": "Source: User input from request"
|
|
747
|
+
},
|
|
748
|
+
{
|
|
749
|
+
"file": "node-express/routes/orders.js",
|
|
750
|
+
"line": 17,
|
|
751
|
+
"code": "db.query(\"SELECT * FROM reports WHERE month = '\" + req.query.month + \"'\", function (e, rows) { res.json(rows); });",
|
|
752
|
+
"why": "User input concatenated into SQL string"
|
|
753
|
+
}
|
|
754
|
+
],
|
|
755
|
+
"data_flow": [
|
|
756
|
+
"req.query.month (orders.js:17)",
|
|
757
|
+
"$sql (orders.js:17)",
|
|
758
|
+
"db.query (orders.js:17)"
|
|
759
|
+
],
|
|
760
|
+
"attack_scenario": "A crafted month changes the WHERE clause and bypasses the intended filtering.",
|
|
761
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
762
|
+
"severity": "critical",
|
|
763
|
+
"confidence": "high",
|
|
764
|
+
"cwe": [
|
|
765
|
+
"CWE-89"
|
|
766
|
+
],
|
|
767
|
+
"owasp": [
|
|
768
|
+
"A03:2021"
|
|
769
|
+
],
|
|
770
|
+
"recommendation": "Use parameterized queries",
|
|
771
|
+
"status": "unverified"
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
"finding_id": "INJ-I-0021-1",
|
|
775
|
+
"category": "sql_injection",
|
|
776
|
+
"subtype": "string_interpolation",
|
|
777
|
+
"title": "SQL Injection in orders.js via template literal interpolation of query parameter",
|
|
778
|
+
"candidate_id": "I-0021",
|
|
779
|
+
"location": {
|
|
780
|
+
"file": "node-express/routes/orders.js",
|
|
781
|
+
"start_line": 29,
|
|
782
|
+
"end_line": 30
|
|
783
|
+
},
|
|
784
|
+
"evidence": [
|
|
785
|
+
{
|
|
786
|
+
"file": "node-express/routes/orders.js",
|
|
787
|
+
"line": 28,
|
|
788
|
+
"code": "router.get('/stats', function (req, res) {",
|
|
789
|
+
"why": "Source: User input from request"
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
"file": "node-express/routes/orders.js",
|
|
793
|
+
"line": 29,
|
|
794
|
+
"code": "db.query(`SELECT COUNT(*) AS n FROM orders WHERE shop_id = ${req.query.shop}`, function (e, r) { res.json(r); });",
|
|
795
|
+
"why": "User input interpolated into SQL string"
|
|
796
|
+
}
|
|
797
|
+
],
|
|
798
|
+
"data_flow": [
|
|
799
|
+
"req.query.shop (orders.js:29)",
|
|
800
|
+
"$sql (orders.js:29)",
|
|
801
|
+
"db.query (orders.js:29)"
|
|
802
|
+
],
|
|
803
|
+
"attack_scenario": "A crafted shop_id changes the WHERE clause and bypasses the intended filtering.",
|
|
804
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
805
|
+
"severity": "critical",
|
|
806
|
+
"confidence": "high",
|
|
807
|
+
"cwe": [
|
|
808
|
+
"CWE-89"
|
|
809
|
+
],
|
|
810
|
+
"owasp": [
|
|
811
|
+
"A03:2021"
|
|
812
|
+
],
|
|
813
|
+
"recommendation": "Use parameterized queries",
|
|
814
|
+
"status": "unverified"
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
"finding_id": "INJ-I-0022-1",
|
|
818
|
+
"category": "sql_injection",
|
|
819
|
+
"subtype": "operator_injection",
|
|
820
|
+
"title": "NoSQL Injection in orders.js via direct passing of request body as filter",
|
|
821
|
+
"candidate_id": "I-0022",
|
|
822
|
+
"location": {
|
|
823
|
+
"file": "node-express/routes/orders.js",
|
|
824
|
+
"start_line": 21,
|
|
825
|
+
"end_line": 22
|
|
826
|
+
},
|
|
827
|
+
"evidence": [
|
|
828
|
+
{
|
|
829
|
+
"file": "node-express/routes/orders.js",
|
|
830
|
+
"line": 20,
|
|
831
|
+
"code": "router.post('/login', function (req, res) {",
|
|
832
|
+
"why": "Source: User input from request"
|
|
833
|
+
},
|
|
834
|
+
{
|
|
835
|
+
"file": "node-express/routes/orders.js",
|
|
836
|
+
"line": 21,
|
|
837
|
+
"code": "db.collection('users').findOne(req.body, function (e, u) { res.json(!!u); });",
|
|
838
|
+
"why": "User input passed directly as filter object"
|
|
839
|
+
}
|
|
840
|
+
],
|
|
841
|
+
"data_flow": [
|
|
842
|
+
"req.body (orders.js:21)",
|
|
843
|
+
"$filter (orders.js:21)",
|
|
844
|
+
"db.collection.findOne (orders.js:21)"
|
|
845
|
+
],
|
|
846
|
+
"attack_scenario": "A crafted req.body alters the MongoDB query logic and bypasses intended filtering.",
|
|
847
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
848
|
+
"severity": "critical",
|
|
849
|
+
"confidence": "high",
|
|
850
|
+
"cwe": [
|
|
851
|
+
"CWE-89"
|
|
852
|
+
],
|
|
853
|
+
"owasp": [
|
|
854
|
+
"A03:2021"
|
|
855
|
+
],
|
|
856
|
+
"recommendation": "Use parameterized queries or explicit field mapping",
|
|
857
|
+
"status": "unverified"
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
"finding_id": "INJ-I-0023-1",
|
|
861
|
+
"category": "sql_injection",
|
|
862
|
+
"subtype": "string_concatenation",
|
|
863
|
+
"title": "NoSQL Injection in orders.js via direct concatenation of query parameter in $where",
|
|
864
|
+
"candidate_id": "I-0023",
|
|
865
|
+
"location": {
|
|
866
|
+
"file": "node-express/routes/orders.js",
|
|
867
|
+
"start_line": 25,
|
|
868
|
+
"end_line": 26
|
|
869
|
+
},
|
|
870
|
+
"evidence": [
|
|
871
|
+
{
|
|
872
|
+
"file": "node-express/routes/orders.js",
|
|
873
|
+
"line": 24,
|
|
874
|
+
"code": "router.get('/search', ensureLoggedIn, function (req, res) {",
|
|
875
|
+
"why": "Source: User input from request"
|
|
876
|
+
},
|
|
877
|
+
{
|
|
878
|
+
"file": "node-express/routes/orders.js",
|
|
879
|
+
"line": 25,
|
|
880
|
+
"code": "db.collection('items').find({ $where: \"this.name == '\" + req.query.n + \"'\" }).toArray(function (e, r) { res.json(r); });",
|
|
881
|
+
"why": "User input concatenated into SQL string"
|
|
882
|
+
}
|
|
883
|
+
],
|
|
884
|
+
"data_flow": [
|
|
885
|
+
"req.query.n (orders.js:25)",
|
|
886
|
+
"$sql (orders.js:25)",
|
|
887
|
+
"db.collection.find (orders.js:25)"
|
|
888
|
+
],
|
|
889
|
+
"attack_scenario": "A crafted name changes the WHERE clause and bypasses the intended filtering.",
|
|
890
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
891
|
+
"severity": "critical",
|
|
892
|
+
"confidence": "high",
|
|
893
|
+
"cwe": [
|
|
894
|
+
"CWE-89"
|
|
895
|
+
],
|
|
896
|
+
"owasp": [
|
|
897
|
+
"A03:2021"
|
|
898
|
+
],
|
|
899
|
+
"recommendation": "Use parameterized queries or explicit field mapping",
|
|
900
|
+
"status": "unverified"
|
|
901
|
+
},
|
|
902
|
+
{
|
|
903
|
+
"finding_id": "INJ-I-0024-1",
|
|
904
|
+
"category": "sql_injection",
|
|
905
|
+
"subtype": "template_placeholder",
|
|
906
|
+
"title": "SQL Injection in order.cgi via direct interpolation of CGI parameter",
|
|
907
|
+
"candidate_id": "I-0024",
|
|
908
|
+
"location": {
|
|
909
|
+
"file": "perl-cgi/cgi-bin/order.cgi",
|
|
910
|
+
"start_line": 13,
|
|
911
|
+
"end_line": 14
|
|
912
|
+
},
|
|
913
|
+
"evidence": [
|
|
914
|
+
{
|
|
915
|
+
"file": "perl-cgi/cgi-bin/order.cgi",
|
|
916
|
+
"line": 11,
|
|
917
|
+
"code": "my $id = $q->param('id');",
|
|
918
|
+
"why": "Source: User input from request"
|
|
919
|
+
},
|
|
920
|
+
{
|
|
921
|
+
"file": "perl-cgi/cgi-bin/order.cgi",
|
|
922
|
+
"line": 13,
|
|
923
|
+
"code": "my $sth = $dbh->prepare(\"SELECT * FROM orders WHERE id = $id\");",
|
|
924
|
+
"why": "User input interpolated into SQL string"
|
|
925
|
+
}
|
|
926
|
+
],
|
|
927
|
+
"data_flow": [
|
|
928
|
+
"$q->param('id') (order.cgi:11)",
|
|
929
|
+
"$id",
|
|
930
|
+
"$sql (order.cgi:13)"
|
|
931
|
+
],
|
|
932
|
+
"attack_scenario": "A crafted id changes the WHERE clause and bypasses the intended filtering.",
|
|
933
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
934
|
+
"severity": "critical",
|
|
935
|
+
"confidence": "high",
|
|
936
|
+
"cwe": [
|
|
937
|
+
"CWE-89"
|
|
938
|
+
],
|
|
939
|
+
"owasp": [
|
|
940
|
+
"A03:2021"
|
|
941
|
+
],
|
|
942
|
+
"recommendation": "Use parameterized queries",
|
|
943
|
+
"status": "unverified"
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
"finding_id": "INJ-I-0025-1",
|
|
947
|
+
"category": "sql_injection",
|
|
948
|
+
"subtype": "string_concatenation",
|
|
949
|
+
"title": "SQL Injection in order.cgi via direct concatenation of CGI parameter",
|
|
950
|
+
"candidate_id": "I-0025",
|
|
951
|
+
"location": {
|
|
952
|
+
"file": "perl-cgi/cgi-bin/order.cgi",
|
|
953
|
+
"start_line": 19,
|
|
954
|
+
"end_line": 20
|
|
955
|
+
},
|
|
956
|
+
"evidence": [
|
|
957
|
+
{
|
|
958
|
+
"file": "perl-cgi/cgi-bin/order.cgi",
|
|
959
|
+
"line": 11,
|
|
960
|
+
"code": "my $id = $q->param('id');",
|
|
961
|
+
"why": "Source: User input from request"
|
|
962
|
+
},
|
|
963
|
+
{
|
|
964
|
+
"file": "perl-cgi/cgi-bin/order.cgi",
|
|
965
|
+
"line": 19,
|
|
966
|
+
"code": "$dbh->do(\"DELETE FROM carts WHERE cart_id = \" . $q->param('cart'));",
|
|
967
|
+
"why": "User input concatenated into SQL string"
|
|
968
|
+
}
|
|
969
|
+
],
|
|
970
|
+
"data_flow": [
|
|
971
|
+
"$q->param('cart') (order.cgi:19)",
|
|
972
|
+
"$sql (order.cgi:19)",
|
|
973
|
+
"$dbh->do (order.cgi:19)"
|
|
974
|
+
],
|
|
975
|
+
"attack_scenario": "A crafted cart_id changes the WHERE clause and bypasses the intended filtering.",
|
|
976
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
977
|
+
"severity": "critical",
|
|
978
|
+
"confidence": "high",
|
|
979
|
+
"cwe": [
|
|
980
|
+
"CWE-89"
|
|
981
|
+
],
|
|
982
|
+
"owasp": [
|
|
983
|
+
"A03:2021"
|
|
984
|
+
],
|
|
985
|
+
"recommendation": "Use parameterized queries",
|
|
986
|
+
"status": "unverified"
|
|
987
|
+
},
|
|
988
|
+
{
|
|
989
|
+
"finding_id": "INJ-I-0026",
|
|
990
|
+
"category": "sql_injection",
|
|
991
|
+
"subtype": "string_interpolation",
|
|
992
|
+
"title": "SQL Injection in Invoice.php:10 via method parameter id (CodeIgniter Active Record)",
|
|
993
|
+
"candidate_id": "I-0026",
|
|
994
|
+
"location": {
|
|
995
|
+
"file": "php-codeigniter/application/controllers/Invoice.php",
|
|
996
|
+
"start_line": 9,
|
|
997
|
+
"end_line": 10
|
|
998
|
+
},
|
|
999
|
+
"evidence": [
|
|
1000
|
+
{
|
|
1001
|
+
"file": "php-codeigniter/application/controllers/Invoice.php",
|
|
1002
|
+
"line": 9,
|
|
1003
|
+
"code": "public function view($id)",
|
|
1004
|
+
"why": "source: method parameter"
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
"file": "php-codeigniter/application/controllers/Invoice.php",
|
|
1008
|
+
"line": 10,
|
|
1009
|
+
"code": "$q = $this->db->query(\"SELECT * FROM invoices WHERE id = $id\");",
|
|
1010
|
+
"why": "variable interpolation into SQL without parameterization"
|
|
1011
|
+
}
|
|
1012
|
+
],
|
|
1013
|
+
"data_flow": [
|
|
1014
|
+
"$id (Invoice.php:9)",
|
|
1015
|
+
"$q (Invoice.php:10)",
|
|
1016
|
+
"$this->db->query (Invoice.php:10)"
|
|
1017
|
+
],
|
|
1018
|
+
"attack_scenario": "A crafted id parameter changes the WHERE clause and bypasses the intended filter.",
|
|
1019
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
1020
|
+
"severity": "critical",
|
|
1021
|
+
"confidence": "high",
|
|
1022
|
+
"cwe": [
|
|
1023
|
+
"CWE-89"
|
|
1024
|
+
],
|
|
1025
|
+
"owasp": [
|
|
1026
|
+
"A03:2021"
|
|
1027
|
+
],
|
|
1028
|
+
"recommendation": "Use parameterized queries with CodeIgniter's query builder or raw SQL with parameters",
|
|
1029
|
+
"status": "unverified"
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
"finding_id": "INJ-I-0028-1",
|
|
1033
|
+
"category": "sql_injection",
|
|
1034
|
+
"subtype": "string_concatenation",
|
|
1035
|
+
"title": "SQL Injection in admin.php via direct concatenation of POST parameter",
|
|
1036
|
+
"candidate_id": "I-0028",
|
|
1037
|
+
"location": {
|
|
1038
|
+
"file": "php-plain/admin.php",
|
|
1039
|
+
"start_line": 12,
|
|
1040
|
+
"end_line": 13
|
|
1041
|
+
},
|
|
1042
|
+
"evidence": [
|
|
1043
|
+
{
|
|
1044
|
+
"file": "php-plain/admin.php",
|
|
1045
|
+
"line": 11,
|
|
1046
|
+
"code": "$id = $_POST['id'];",
|
|
1047
|
+
"why": "Source: User input from request"
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
"file": "php-plain/admin.php",
|
|
1051
|
+
"line": 12,
|
|
1052
|
+
"code": "mysql_query(\"DELETE FROM users WHERE id = $id\");",
|
|
1053
|
+
"why": "User input concatenated into SQL string"
|
|
1054
|
+
}
|
|
1055
|
+
],
|
|
1056
|
+
"data_flow": [
|
|
1057
|
+
"$_POST['id'] (admin.php:11)",
|
|
1058
|
+
"$id",
|
|
1059
|
+
"$sql (admin.php:12)"
|
|
1060
|
+
],
|
|
1061
|
+
"attack_scenario": "A crafted id changes the WHERE clause and bypasses the intended filtering.",
|
|
1062
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
1063
|
+
"severity": "critical",
|
|
1064
|
+
"confidence": "high",
|
|
1065
|
+
"cwe": [
|
|
1066
|
+
"CWE-89"
|
|
1067
|
+
],
|
|
1068
|
+
"owasp": [
|
|
1069
|
+
"A03:2021"
|
|
1070
|
+
],
|
|
1071
|
+
"recommendation": "Use parameterized queries",
|
|
1072
|
+
"status": "unverified"
|
|
1073
|
+
},
|
|
1074
|
+
{
|
|
1075
|
+
"finding_id": "INJ-I-0029-1",
|
|
1076
|
+
"category": "sql_injection",
|
|
1077
|
+
"subtype": "operator_injection",
|
|
1078
|
+
"title": "NoSQL Injection in mongo_find.php via direct passing of GET parameters as filter",
|
|
1079
|
+
"candidate_id": "I-0029",
|
|
1080
|
+
"location": {
|
|
1081
|
+
"file": "php-plain/mongo_find.php",
|
|
1082
|
+
"start_line": 5,
|
|
1083
|
+
"end_line": 6
|
|
1084
|
+
},
|
|
1085
|
+
"evidence": [
|
|
1086
|
+
{
|
|
1087
|
+
"file": "php-plain/mongo_find.php",
|
|
1088
|
+
"line": 4,
|
|
1089
|
+
"code": "$doc = $coll->findOne(array('login' => $_GET['login'], 'pass' => $_GET['pass']));",
|
|
1090
|
+
"why": "User input passed directly as filter object"
|
|
1091
|
+
}
|
|
1092
|
+
],
|
|
1093
|
+
"data_flow": [
|
|
1094
|
+
"$_GET['login'] (mongo_find.php:5)",
|
|
1095
|
+
"$filter (mongo_find.php:5)",
|
|
1096
|
+
"$coll->findOne (mongo_find.php:5)"
|
|
1097
|
+
],
|
|
1098
|
+
"attack_scenario": "A crafted login/pass changes the query logic and bypasses intended filtering.",
|
|
1099
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
1100
|
+
"severity": "critical",
|
|
1101
|
+
"confidence": "high",
|
|
1102
|
+
"cwe": [
|
|
1103
|
+
"CWE-89"
|
|
1104
|
+
],
|
|
1105
|
+
"owasp": [
|
|
1106
|
+
"A03:2021"
|
|
1107
|
+
],
|
|
1108
|
+
"recommendation": "Use parameterized queries or explicit field mapping",
|
|
1109
|
+
"status": "unverified"
|
|
1110
|
+
},
|
|
1111
|
+
{
|
|
1112
|
+
"finding_id": "INJ-I-0030-1",
|
|
1113
|
+
"category": "sql_injection",
|
|
1114
|
+
"subtype": "string_concatenation",
|
|
1115
|
+
"title": "NoSQL Injection in mongo_find.php via direct concatenation of GET parameter in $where",
|
|
1116
|
+
"candidate_id": "I-0030",
|
|
1117
|
+
"location": {
|
|
1118
|
+
"file": "php-plain/mongo_find.php",
|
|
1119
|
+
"start_line": 6,
|
|
1120
|
+
"end_line": 7
|
|
1121
|
+
},
|
|
1122
|
+
"evidence": [
|
|
1123
|
+
{
|
|
1124
|
+
"file": "php-plain/mongo_find.php",
|
|
1125
|
+
"line": 5,
|
|
1126
|
+
"code": "$docs = $coll->find(array('$where' => \"this.name == '\" . $_GET['n'] . \"'\"));",
|
|
1127
|
+
"why": "User input concatenated into SQL string"
|
|
1128
|
+
}
|
|
1129
|
+
],
|
|
1130
|
+
"data_flow": [
|
|
1131
|
+
"$_GET['n'] (mongo_find.php:6)",
|
|
1132
|
+
"$sql (mongo_find.php:6)",
|
|
1133
|
+
"$coll->find (mongo_find.php:6)"
|
|
1134
|
+
],
|
|
1135
|
+
"attack_scenario": "A crafted name changes the WHERE clause and bypasses the intended filtering.",
|
|
1136
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
1137
|
+
"severity": "critical",
|
|
1138
|
+
"confidence": "high",
|
|
1139
|
+
"cwe": [
|
|
1140
|
+
"CWE-89"
|
|
1141
|
+
],
|
|
1142
|
+
"owasp": [
|
|
1143
|
+
"A03:2021"
|
|
1144
|
+
],
|
|
1145
|
+
"recommendation": "Use parameterized queries or explicit field mapping",
|
|
1146
|
+
"status": "unverified"
|
|
1147
|
+
},
|
|
1148
|
+
{
|
|
1149
|
+
"finding_id": "INJ-I-0031",
|
|
1150
|
+
"category": "sql_injection",
|
|
1151
|
+
"subtype": "string_concatenation",
|
|
1152
|
+
"title": "SQL Injection in order_detail.php:6 via GET parameter id",
|
|
1153
|
+
"candidate_id": "I-0031",
|
|
1154
|
+
"location": {
|
|
1155
|
+
"file": "php-plain/order_detail.php",
|
|
1156
|
+
"start_line": 5,
|
|
1157
|
+
"end_line": 6
|
|
1158
|
+
},
|
|
1159
|
+
"evidence": [
|
|
1160
|
+
{
|
|
1161
|
+
"file": "php-plain/order_detail.php",
|
|
1162
|
+
"line": 5,
|
|
1163
|
+
"code": "$id = $_GET['id'];",
|
|
1164
|
+
"why": "source: GET parameter"
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
"file": "php-plain/order_detail.php",
|
|
1168
|
+
"line": 6,
|
|
1169
|
+
"code": "$res = mysql_query(\"SELECT * FROM orders WHERE id = \" . $id);",
|
|
1170
|
+
"why": "concatenated into query without sanitization"
|
|
1171
|
+
}
|
|
1172
|
+
],
|
|
1173
|
+
"data_flow": [
|
|
1174
|
+
"$_GET['id'] (order_detail.php:5)",
|
|
1175
|
+
"$id",
|
|
1176
|
+
"mysql_query (order_detail.php:6)"
|
|
1177
|
+
],
|
|
1178
|
+
"attack_scenario": "A crafted id parameter changes the WHERE clause and bypasses the intended filter.",
|
|
1179
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
1180
|
+
"severity": "critical",
|
|
1181
|
+
"confidence": "high",
|
|
1182
|
+
"cwe": [
|
|
1183
|
+
"CWE-89"
|
|
1184
|
+
],
|
|
1185
|
+
"owasp": [
|
|
1186
|
+
"A03:2021"
|
|
1187
|
+
],
|
|
1188
|
+
"recommendation": "Use parameterized queries with mysqli_prepare or PDO",
|
|
1189
|
+
"status": "unverified"
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
"finding_id": "INJ-I-0032",
|
|
1193
|
+
"category": "sql_injection",
|
|
1194
|
+
"subtype": "string_concatenation",
|
|
1195
|
+
"title": "SQL Injection in search.php:8 via GET parameter q (addslashes is insufficient)",
|
|
1196
|
+
"candidate_id": "I-0032",
|
|
1197
|
+
"location": {
|
|
1198
|
+
"file": "php-plain/search.php",
|
|
1199
|
+
"start_line": 5,
|
|
1200
|
+
"end_line": 9
|
|
1201
|
+
},
|
|
1202
|
+
"evidence": [
|
|
1203
|
+
{
|
|
1204
|
+
"file": "php-plain/search.php",
|
|
1205
|
+
"line": 5,
|
|
1206
|
+
"code": "$kw = addslashes($_GET['q']);",
|
|
1207
|
+
"why": "source: GET parameter"
|
|
1208
|
+
},
|
|
1209
|
+
{
|
|
1210
|
+
"file": "php-plain/search.php",
|
|
1211
|
+
"line": 8,
|
|
1212
|
+
"code": "$sql .= \" AND name LIKE '%\" . $kw . \"%'\";",
|
|
1213
|
+
"why": "addslashes is not sufficient for SQL injection prevention"
|
|
1214
|
+
}
|
|
1215
|
+
],
|
|
1216
|
+
"data_flow": [
|
|
1217
|
+
"$_GET['q'] (search.php:5)",
|
|
1218
|
+
"$kw",
|
|
1219
|
+
"$sql (search.php:8)",
|
|
1220
|
+
"mysql_query (search.php:11)"
|
|
1221
|
+
],
|
|
1222
|
+
"attack_scenario": "A crafted q parameter with special characters bypasses addslashes and injects SQL.",
|
|
1223
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
1224
|
+
"severity": "critical",
|
|
1225
|
+
"confidence": "high",
|
|
1226
|
+
"cwe": [
|
|
1227
|
+
"CWE-89"
|
|
1228
|
+
],
|
|
1229
|
+
"owasp": [
|
|
1230
|
+
"A03:2021"
|
|
1231
|
+
],
|
|
1232
|
+
"recommendation": "Use parameterized queries with mysqli_prepare or PDO",
|
|
1233
|
+
"status": "unverified"
|
|
1234
|
+
},
|
|
1235
|
+
{
|
|
1236
|
+
"finding_id": "INJ-I-0033",
|
|
1237
|
+
"category": "sql_injection",
|
|
1238
|
+
"subtype": "dynamic_identifier",
|
|
1239
|
+
"title": "SQL Injection in search.php:9 via GET parameter sort (ORDER BY without allow-list)",
|
|
1240
|
+
"candidate_id": "I-0033",
|
|
1241
|
+
"location": {
|
|
1242
|
+
"file": "php-plain/search.php",
|
|
1243
|
+
"start_line": 6,
|
|
1244
|
+
"end_line": 9
|
|
1245
|
+
},
|
|
1246
|
+
"evidence": [
|
|
1247
|
+
{
|
|
1248
|
+
"file": "php-plain/search.php",
|
|
1249
|
+
"line": 6,
|
|
1250
|
+
"code": "$sort = $_GET['sort'];",
|
|
1251
|
+
"why": "source: GET parameter"
|
|
1252
|
+
},
|
|
1253
|
+
{
|
|
1254
|
+
"file": "php-plain/search.php",
|
|
1255
|
+
"line": 9,
|
|
1256
|
+
"code": "$sql .= \" ORDER BY \" . $sort;",
|
|
1257
|
+
"why": "dynamic ORDER BY without allow-list validation"
|
|
1258
|
+
}
|
|
1259
|
+
],
|
|
1260
|
+
"data_flow": [
|
|
1261
|
+
"$_GET['sort'] (search.php:6)",
|
|
1262
|
+
"$sort",
|
|
1263
|
+
"$sql (search.php:9)",
|
|
1264
|
+
"mysql_query (search.php:11)"
|
|
1265
|
+
],
|
|
1266
|
+
"attack_scenario": "A crafted sort parameter changes the ORDER BY clause to extract data or bypass filters.",
|
|
1267
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
1268
|
+
"severity": "critical",
|
|
1269
|
+
"confidence": "high",
|
|
1270
|
+
"cwe": [
|
|
1271
|
+
"CWE-89"
|
|
1272
|
+
],
|
|
1273
|
+
"owasp": [
|
|
1274
|
+
"A03:2021"
|
|
1275
|
+
],
|
|
1276
|
+
"recommendation": "Use an explicit allow-list for allowed sort columns",
|
|
1277
|
+
"status": "unverified"
|
|
1278
|
+
},
|
|
1279
|
+
{
|
|
1280
|
+
"finding_id": "INJ-I-0034",
|
|
1281
|
+
"category": "sql_injection",
|
|
1282
|
+
"subtype": "string_interpolation",
|
|
1283
|
+
"title": "SQL Injection in search.php:14 via GET parameter cat (heredoc interpolation)",
|
|
1284
|
+
"candidate_id": "I-0034",
|
|
1285
|
+
"location": {
|
|
1286
|
+
"file": "php-plain/search.php",
|
|
1287
|
+
"start_line": 12,
|
|
1288
|
+
"end_line": 16
|
|
1289
|
+
},
|
|
1290
|
+
"evidence": [
|
|
1291
|
+
{
|
|
1292
|
+
"file": "php-plain/search.php",
|
|
1293
|
+
"line": 14,
|
|
1294
|
+
"code": "WHERE category = '{$_GET['cat']}'",
|
|
1295
|
+
"why": "direct interpolation of GET parameter into heredoc SQL"
|
|
1296
|
+
}
|
|
1297
|
+
],
|
|
1298
|
+
"data_flow": [
|
|
1299
|
+
"$_GET['cat'] (search.php:14)",
|
|
1300
|
+
"$q (search.php:12-16)",
|
|
1301
|
+
"mysql_query (search.php:16)"
|
|
1302
|
+
],
|
|
1303
|
+
"attack_scenario": "A crafted cat parameter changes the WHERE clause and bypasses the intended filter.",
|
|
1304
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
1305
|
+
"severity": "critical",
|
|
1306
|
+
"confidence": "high",
|
|
1307
|
+
"cwe": [
|
|
1308
|
+
"CWE-89"
|
|
1309
|
+
],
|
|
1310
|
+
"owasp": [
|
|
1311
|
+
"A03:2021"
|
|
1312
|
+
],
|
|
1313
|
+
"recommendation": "Use parameterized queries with mysqli_prepare or PDO",
|
|
1314
|
+
"status": "unverified"
|
|
1315
|
+
},
|
|
1316
|
+
{
|
|
1317
|
+
"finding_id": "INJ-I-0035",
|
|
1318
|
+
"category": "sql_injection",
|
|
1319
|
+
"subtype": "string_concatenation",
|
|
1320
|
+
"title": "SQL Injection in app.py:12 via route parameter order_id (string formatting)",
|
|
1321
|
+
"candidate_id": "I-0035",
|
|
1322
|
+
"location": {
|
|
1323
|
+
"file": "python-flask/app.py",
|
|
1324
|
+
"start_line": 8,
|
|
1325
|
+
"end_line": 12
|
|
1326
|
+
},
|
|
1327
|
+
"evidence": [
|
|
1328
|
+
{
|
|
1329
|
+
"file": "python-flask/app.py",
|
|
1330
|
+
"line": 8,
|
|
1331
|
+
"code": "@app.route('/orders/<int:order_id>')",
|
|
1332
|
+
"why": "source: route parameter"
|
|
1333
|
+
},
|
|
1334
|
+
{
|
|
1335
|
+
"file": "python-flask/app.py",
|
|
1336
|
+
"line": 12,
|
|
1337
|
+
"code": "cur.execute(\"SELECT * FROM orders WHERE id = %s\" % order_id)",
|
|
1338
|
+
"why": "string formatting with user input instead of parameterized query"
|
|
1339
|
+
}
|
|
1340
|
+
],
|
|
1341
|
+
"data_flow": [
|
|
1342
|
+
"route param order_id (app.py:8)",
|
|
1343
|
+
"$order_id",
|
|
1344
|
+
"cur.execute (app.py:12)"
|
|
1345
|
+
],
|
|
1346
|
+
"attack_scenario": "A crafted order_id parameter changes the WHERE clause and bypasses the intended filter.",
|
|
1347
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
1348
|
+
"severity": "critical",
|
|
1349
|
+
"confidence": "high",
|
|
1350
|
+
"cwe": [
|
|
1351
|
+
"CWE-89"
|
|
1352
|
+
],
|
|
1353
|
+
"owasp": [
|
|
1354
|
+
"A03:2021"
|
|
1355
|
+
],
|
|
1356
|
+
"recommendation": "Use parameterized queries with cursor.execute(query, params)",
|
|
1357
|
+
"status": "unverified"
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
"finding_id": "INJ-I-0036",
|
|
1361
|
+
"category": "sql_injection",
|
|
1362
|
+
"subtype": "string_interpolation",
|
|
1363
|
+
"title": "SQL Injection in app.py:29 via GET parameter q (f-string interpolation)",
|
|
1364
|
+
"candidate_id": "I-0036",
|
|
1365
|
+
"location": {
|
|
1366
|
+
"file": "python-flask/app.py",
|
|
1367
|
+
"start_line": 27,
|
|
1368
|
+
"end_line": 29
|
|
1369
|
+
},
|
|
1370
|
+
"evidence": [
|
|
1371
|
+
{
|
|
1372
|
+
"file": "python-flask/app.py",
|
|
1373
|
+
"line": 27,
|
|
1374
|
+
"code": "q = request.args.get('q', '')",
|
|
1375
|
+
"why": "source: GET parameter"
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
"file": "python-flask/app.py",
|
|
1379
|
+
"line": 29,
|
|
1380
|
+
"code": "cur.execute(f\"SELECT * FROM products WHERE name LIKE '%{q}%'\")",
|
|
1381
|
+
"why": "f-string interpolation into SQL without parameterization"
|
|
1382
|
+
}
|
|
1383
|
+
],
|
|
1384
|
+
"data_flow": [
|
|
1385
|
+
"request.args['q'] (app.py:27)",
|
|
1386
|
+
"$q",
|
|
1387
|
+
"cur.execute (app.py:29)"
|
|
1388
|
+
],
|
|
1389
|
+
"attack_scenario": "A crafted q parameter changes the WHERE clause and bypasses the intended filter.",
|
|
1390
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
1391
|
+
"severity": "critical",
|
|
1392
|
+
"confidence": "high",
|
|
1393
|
+
"cwe": [
|
|
1394
|
+
"CWE-89"
|
|
1395
|
+
],
|
|
1396
|
+
"owasp": [
|
|
1397
|
+
"A03:2021"
|
|
1398
|
+
],
|
|
1399
|
+
"recommendation": "Use parameterized queries with cursor.execute(query, params)",
|
|
1400
|
+
"status": "unverified"
|
|
1401
|
+
},
|
|
1402
|
+
{
|
|
1403
|
+
"finding_id": "INJ-I-0037",
|
|
1404
|
+
"category": "sql_injection",
|
|
1405
|
+
"subtype": "stored_procedure_dynamic_sql",
|
|
1406
|
+
"title": "SQL Injection in sp_search_orders.sql:5 via stored procedure parameters (dynamic SQL)",
|
|
1407
|
+
"candidate_id": "I-0037",
|
|
1408
|
+
"location": {
|
|
1409
|
+
"file": "sql-procs/sp_search_orders.sql",
|
|
1410
|
+
"start_line": 5,
|
|
1411
|
+
"end_line": 6
|
|
1412
|
+
},
|
|
1413
|
+
"evidence": [
|
|
1414
|
+
{
|
|
1415
|
+
"file": "sql-procs/sp_search_orders.sql",
|
|
1416
|
+
"line": 5,
|
|
1417
|
+
"code": "SET @sql = 'SELECT * FROM Orders WHERE Customer = ''' + @Customer + ''' ORDER BY ' + @Sort",
|
|
1418
|
+
"why": "string concatenation into dynamic SQL variable"
|
|
1419
|
+
},
|
|
1420
|
+
{
|
|
1421
|
+
"file": "sql-procs/sp_search_orders.sql",
|
|
1422
|
+
"line": 6,
|
|
1423
|
+
"code": "EXEC(@sql)",
|
|
1424
|
+
"why": "execution of dynamic SQL"
|
|
1425
|
+
}
|
|
1426
|
+
],
|
|
1427
|
+
"data_flow": [
|
|
1428
|
+
"@Customer (sp_search_orders.sql:1)",
|
|
1429
|
+
"@Sort (sp_search_orders.sql:1)",
|
|
1430
|
+
"@sql (sp_search_orders.sql:5)",
|
|
1431
|
+
"EXEC (@sql) (sp_search_orders.sql:6)"
|
|
1432
|
+
],
|
|
1433
|
+
"attack_scenario": "A crafted Customer or Sort parameter changes the WHERE or ORDER BY clause and bypasses filters.",
|
|
1434
|
+
"impact": "Authentication bypass; read/modify any table the DB user can access.",
|
|
1435
|
+
"severity": "critical",
|
|
1436
|
+
"confidence": "high",
|
|
1437
|
+
"cwe": [
|
|
1438
|
+
"CWE-89"
|
|
1439
|
+
],
|
|
1440
|
+
"owasp": [
|
|
1441
|
+
"A03:2021"
|
|
1442
|
+
],
|
|
1443
|
+
"recommendation": "Use parameterized queries instead of dynamic SQL execution",
|
|
1444
|
+
"status": "unverified"
|
|
1445
|
+
},
|
|
1446
|
+
{
|
|
1447
|
+
"finding_id": "BOLA-R-0001-1",
|
|
1448
|
+
"category": "bola",
|
|
1449
|
+
"subtype": "missing_ownership_check",
|
|
1450
|
+
"title": "User edit vulnerable to IDOR - no ownership verification on user id",
|
|
1451
|
+
"route_id": "R-0001",
|
|
1452
|
+
"endpoint": {
|
|
1453
|
+
"method": "ANY",
|
|
1454
|
+
"path": "/user/edit.do"
|
|
1455
|
+
},
|
|
1456
|
+
"location": {
|
|
1457
|
+
"file": "java-legacy/src/com/acme/struts/EditUserAction.java",
|
|
1458
|
+
"start_line": 13,
|
|
1459
|
+
"end_line": 14
|
|
1460
|
+
},
|
|
1461
|
+
"evidence": [
|
|
1462
|
+
{
|
|
1463
|
+
"file": "java-legacy/src/com/acme/struts/EditUserAction.java",
|
|
1464
|
+
"line": 10,
|
|
1465
|
+
"code": "String userId = request.getParameter(\"user_id\");",
|
|
1466
|
+
"why": "ID from request parameter (request-controlled)"
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
"file": "java-legacy/src/com/acme/struts/EditUserAction.java",
|
|
1470
|
+
"line": 13,
|
|
1471
|
+
"code": "UPDATE users SET email = '\" + email + \"' WHERE id = \" + userId;",
|
|
1472
|
+
"why": "No ownership check - updates any user by ID"
|
|
1473
|
+
}
|
|
1474
|
+
],
|
|
1475
|
+
"attack_scenario": "User A sends POST /edit.do?user_id=<user id of user B> and modifies user B's email.",
|
|
1476
|
+
"impact": "Modify another user's profile data (email, etc.).",
|
|
1477
|
+
"severity": "high",
|
|
1478
|
+
"confidence": "high",
|
|
1479
|
+
"cwe": [
|
|
1480
|
+
"CWE-284",
|
|
1481
|
+
"CWE-639"
|
|
1482
|
+
],
|
|
1483
|
+
"recommendation": "Bind query to current session user",
|
|
1484
|
+
"status": "confirmed",
|
|
1485
|
+
"verification": {
|
|
1486
|
+
"verdict": "confirmed",
|
|
1487
|
+
"reason_code": "traced",
|
|
1488
|
+
"checked": [
|
|
1489
|
+
"java-legacy/src/com/acme/struts/EditUserAction.java lines 10,13",
|
|
1490
|
+
"global filters in web.xml",
|
|
1491
|
+
"session user binding"
|
|
1492
|
+
],
|
|
1493
|
+
"control_found": null,
|
|
1494
|
+
"notes": "SQL UPDATE with direct concatenation of userId without ownership verification. No compensating control found.",
|
|
1495
|
+
"suggested_subtype": null,
|
|
1496
|
+
"suggested_cwe": null
|
|
1497
|
+
},
|
|
1498
|
+
"consensus": {
|
|
1499
|
+
"score": 0.95,
|
|
1500
|
+
"agreement_level": "unanimous",
|
|
1501
|
+
"voted_models": [
|
|
1502
|
+
"qwen3.5:9b"
|
|
1503
|
+
],
|
|
1504
|
+
"rationale": "Direct SQL construction with user-controlled userId parameter without ownership check."
|
|
1505
|
+
},
|
|
1506
|
+
"attack_vector": {
|
|
1507
|
+
"method": "POST",
|
|
1508
|
+
"path": "/user/edit.do?user_id={userId}",
|
|
1509
|
+
"target_param": "user_id"
|
|
1510
|
+
}
|
|
1511
|
+
},
|
|
1512
|
+
{
|
|
1513
|
+
"finding_id": "BOLA-R-0002-1",
|
|
1514
|
+
"category": "bola",
|
|
1515
|
+
"subtype": "missing_ownership_check",
|
|
1516
|
+
"title": "Member edit vulnerable to IDOR - no ownership verification on member_id",
|
|
1517
|
+
"route_id": "R-0002",
|
|
1518
|
+
"endpoint": {
|
|
1519
|
+
"method": "ANY",
|
|
1520
|
+
"path": "/classic-asp/shop/member.asp?mode=edit"
|
|
1521
|
+
},
|
|
1522
|
+
"location": {
|
|
1523
|
+
"file": "classic-asp/shop/member.asp",
|
|
1524
|
+
"start_line": 6,
|
|
1525
|
+
"end_line": 7
|
|
1526
|
+
},
|
|
1527
|
+
"evidence": [
|
|
1528
|
+
{
|
|
1529
|
+
"file": "classic-asp/shop/member.asp",
|
|
1530
|
+
"line": 6,
|
|
1531
|
+
"code": "sql = \"UPDATE members SET name = '\" & Replace(Request.Form(\"name\"), \"'\", \"''\") & \"'\"",
|
|
1532
|
+
"why": "ID from form (Request.Form) used directly"
|
|
1533
|
+
},
|
|
1534
|
+
{
|
|
1535
|
+
"file": "classic-asp/shop/member.asp",
|
|
1536
|
+
"line": 7,
|
|
1537
|
+
"code": "WHERE member_id = \" & Request.Form(\"member_id\")",
|
|
1538
|
+
"why": "No ownership check - updates any member by ID"
|
|
1539
|
+
}
|
|
1540
|
+
],
|
|
1541
|
+
"attack_scenario": "User A sends POST /member.asp?mode=edit with member_id=<user B's ID> and modifies user B's name.",
|
|
1542
|
+
"impact": "Modify another user's profile data (name, etc.).",
|
|
1543
|
+
"severity": "high",
|
|
1544
|
+
"confidence": "high",
|
|
1545
|
+
"cwe": [
|
|
1546
|
+
"CWE-284",
|
|
1547
|
+
"CWE-639"
|
|
1548
|
+
],
|
|
1549
|
+
"recommendation": "Bind query to current session user",
|
|
1550
|
+
"status": "confirmed",
|
|
1551
|
+
"verification": {
|
|
1552
|
+
"verdict": "confirmed",
|
|
1553
|
+
"reason_code": "traced",
|
|
1554
|
+
"checked": [
|
|
1555
|
+
"classic-asp/shop/member.asp lines 6-7",
|
|
1556
|
+
"Request.Form/QueryString usage pattern"
|
|
1557
|
+
],
|
|
1558
|
+
"control_found": null,
|
|
1559
|
+
"notes": "Classic ASP lacks global filter infrastructure. No session-based user ID binding found in evidence. Direct concatenation of member_id without ownership verification.",
|
|
1560
|
+
"suggested_subtype": null,
|
|
1561
|
+
"suggested_cwe": null
|
|
1562
|
+
},
|
|
1563
|
+
"consensus": {
|
|
1564
|
+
"score": 0.95,
|
|
1565
|
+
"agreement_level": "unanimous",
|
|
1566
|
+
"voted_models": [
|
|
1567
|
+
"qwen3.5:9b"
|
|
1568
|
+
],
|
|
1569
|
+
"rationale": "Classic ASP architecture lacks compensating controls; direct IDOR vulnerability confirmed."
|
|
1570
|
+
},
|
|
1571
|
+
"attack_vector": {
|
|
1572
|
+
"method": "POST",
|
|
1573
|
+
"path": "/classic-asp/shop/member.asp?mode=edit",
|
|
1574
|
+
"target_param": "member_id"
|
|
1575
|
+
}
|
|
1576
|
+
},
|
|
1577
|
+
{
|
|
1578
|
+
"finding_id": "BOLA-R-0003-1",
|
|
1579
|
+
"category": "bola",
|
|
1580
|
+
"subtype": "missing_ownership_check",
|
|
1581
|
+
"title": "Member delete vulnerable to IDOR - no ownership verification on member_id",
|
|
1582
|
+
"route_id": "R-0003",
|
|
1583
|
+
"endpoint": {
|
|
1584
|
+
"method": "ANY",
|
|
1585
|
+
"path": "/classic-asp/shop/member.asp?mode=delete"
|
|
1586
|
+
},
|
|
1587
|
+
"location": {
|
|
1588
|
+
"file": "classic-asp/shop/member.asp",
|
|
1589
|
+
"start_line": 10,
|
|
1590
|
+
"end_line": 10
|
|
1591
|
+
},
|
|
1592
|
+
"evidence": [
|
|
1593
|
+
{
|
|
1594
|
+
"file": "classic-asp/shop/member.asp",
|
|
1595
|
+
"line": 10,
|
|
1596
|
+
"code": "conn.Execute \"DELETE FROM members WHERE member_id = \" & Request.QueryString(\"member_id\")",
|
|
1597
|
+
"why": "No ownership check - deletes any member by ID"
|
|
1598
|
+
}
|
|
1599
|
+
],
|
|
1600
|
+
"attack_scenario": "User A sends GET /member.asp?mode=delete&member_id=<user B's ID> and deletes user B's account.",
|
|
1601
|
+
"impact": "Delete another user's account record.",
|
|
1602
|
+
"severity": "high",
|
|
1603
|
+
"confidence": "high",
|
|
1604
|
+
"cwe": [
|
|
1605
|
+
"CWE-284",
|
|
1606
|
+
"CWE-639"
|
|
1607
|
+
],
|
|
1608
|
+
"recommendation": "Bind query to current session user",
|
|
1609
|
+
"status": "confirmed",
|
|
1610
|
+
"verification": {
|
|
1611
|
+
"verdict": "confirmed",
|
|
1612
|
+
"reason_code": "traced",
|
|
1613
|
+
"checked": [
|
|
1614
|
+
"classic-asp/shop/member.asp line 10",
|
|
1615
|
+
"Request.QueryString usage pattern"
|
|
1616
|
+
],
|
|
1617
|
+
"control_found": null,
|
|
1618
|
+
"notes": "Classic ASP lacks global filter infrastructure. No session-based user ID binding found in evidence. Direct concatenation of member_id without ownership verification.",
|
|
1619
|
+
"suggested_subtype": null,
|
|
1620
|
+
"suggested_cwe": null
|
|
1621
|
+
},
|
|
1622
|
+
"consensus": {
|
|
1623
|
+
"score": 0.95,
|
|
1624
|
+
"agreement_level": "unanimous",
|
|
1625
|
+
"voted_models": [
|
|
1626
|
+
"qwen3.5:9b"
|
|
1627
|
+
],
|
|
1628
|
+
"rationale": "Classic ASP architecture lacks compensating controls; direct IDOR vulnerability confirmed."
|
|
1629
|
+
},
|
|
1630
|
+
"attack_vector": {
|
|
1631
|
+
"method": "GET",
|
|
1632
|
+
"path": "/classic-asp/shop/member.asp?mode=delete&member_id=",
|
|
1633
|
+
"target_param": "member_id"
|
|
1634
|
+
}
|
|
1635
|
+
},
|
|
1636
|
+
{
|
|
1637
|
+
"finding_id": "BOLA-R-0004-1",
|
|
1638
|
+
"category": "bola",
|
|
1639
|
+
"subtype": "missing_ownership_check",
|
|
1640
|
+
"title": "Order detail readable by any authenticated user via id - no ownership verification",
|
|
1641
|
+
"route_id": "R-0004",
|
|
1642
|
+
"endpoint": {
|
|
1643
|
+
"method": "ANY",
|
|
1644
|
+
"path": "/classic-asp/shop/order.asp"
|
|
1645
|
+
},
|
|
1646
|
+
"location": {
|
|
1647
|
+
"file": "classic-asp/shop/order.asp",
|
|
1648
|
+
"start_line": 6,
|
|
1649
|
+
"end_line": 6
|
|
1650
|
+
},
|
|
1651
|
+
"evidence": [
|
|
1652
|
+
{
|
|
1653
|
+
"file": "classic-asp/shop/order.asp",
|
|
1654
|
+
"line": 5,
|
|
1655
|
+
"code": "id = Request(\"id\")",
|
|
1656
|
+
"why": "ID from query string (request-controlled)"
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
"file": "classic-asp/shop/order.asp",
|
|
1660
|
+
"line": 6,
|
|
1661
|
+
"code": "SELECT * FROM orders WHERE id = \" & id",
|
|
1662
|
+
"why": "No ownership check - reads any order by ID"
|
|
1663
|
+
}
|
|
1664
|
+
],
|
|
1665
|
+
"attack_scenario": "User A sends GET /order.asp?id=<order id of user B> and receives user B's order details.",
|
|
1666
|
+
"impact": "Read other customers' orders (address, etc.).",
|
|
1667
|
+
"severity": "high",
|
|
1668
|
+
"confidence": "high",
|
|
1669
|
+
"cwe": [
|
|
1670
|
+
"CWE-284",
|
|
1671
|
+
"CWE-639"
|
|
1672
|
+
],
|
|
1673
|
+
"recommendation": "Bind query to current session user",
|
|
1674
|
+
"status": "confirmed",
|
|
1675
|
+
"verification": {
|
|
1676
|
+
"verdict": "confirmed",
|
|
1677
|
+
"reason_code": "traced",
|
|
1678
|
+
"checked": [
|
|
1679
|
+
"dotnet-legacy/Web/Controllers/AdminController.cs line 5",
|
|
1680
|
+
"Request id assignment pattern"
|
|
1681
|
+
],
|
|
1682
|
+
"control_found": null,
|
|
1683
|
+
"notes": ".NET controller lacks ownership verification. No session-based user ID binding found in evidence. Direct concatenation of order ID without ownership check.",
|
|
1684
|
+
"suggested_subtype": null,
|
|
1685
|
+
"suggested_cwe": null
|
|
1686
|
+
},
|
|
1687
|
+
"consensus": {
|
|
1688
|
+
"score": 0.95,
|
|
1689
|
+
"agreement_level": "unanimous",
|
|
1690
|
+
"voted_models": [
|
|
1691
|
+
"qwen3.5:9b"
|
|
1692
|
+
],
|
|
1693
|
+
"rationale": ".NET controller lacks compensating controls; direct IDOR vulnerability confirmed."
|
|
1694
|
+
},
|
|
1695
|
+
"attack_vector": {
|
|
1696
|
+
"method": "GET",
|
|
1697
|
+
"path": "/classic-asp/shop/order.asp?id=",
|
|
1698
|
+
"target_param": "id"
|
|
1699
|
+
}
|
|
1700
|
+
},
|
|
1701
|
+
{
|
|
1702
|
+
"finding_id": "BOLA-R-0005-1",
|
|
1703
|
+
"category": "bola",
|
|
1704
|
+
"subtype": "missing_ownership_check",
|
|
1705
|
+
"title": "Order service readable by any authenticated user via orderId - no ownership verification",
|
|
1706
|
+
"route_id": "R-0005",
|
|
1707
|
+
"endpoint": {
|
|
1708
|
+
"method": "ANY",
|
|
1709
|
+
"path": "/coldfusion/app/OrderService.cfc?method=getOrder"
|
|
1710
|
+
},
|
|
1711
|
+
"location": {
|
|
1712
|
+
"file": "coldfusion/app/OrderService.cfc",
|
|
1713
|
+
"start_line": 4,
|
|
1714
|
+
"end_line": 6
|
|
1715
|
+
},
|
|
1716
|
+
"evidence": [
|
|
1717
|
+
{
|
|
1718
|
+
"file": "coldfusion/app/OrderService.cfc",
|
|
1719
|
+
"line": 3,
|
|
1720
|
+
"code": "<cfargument name=\"orderId\" type=\"string\" required=\"true\">",
|
|
1721
|
+
"why": "ID from request argument (request-controlled)"
|
|
1722
|
+
},
|
|
1723
|
+
{
|
|
1724
|
+
"file": "coldfusion/app/OrderService.cfc",
|
|
1725
|
+
"line": 5,
|
|
1726
|
+
"code": "SELECT * FROM orders WHERE id = #arguments.orderId#",
|
|
1727
|
+
"why": "No ownership check - reads any order by ID"
|
|
1728
|
+
}
|
|
1729
|
+
],
|
|
1730
|
+
"attack_scenario": "User A sends GET /OrderService.cfc?method=getOrder&orderId=<order id of user B> and receives user B's order.",
|
|
1731
|
+
"impact": "Read other customers' orders (full details).",
|
|
1732
|
+
"severity": "high",
|
|
1733
|
+
"confidence": "high",
|
|
1734
|
+
"cwe": [
|
|
1735
|
+
"CWE-284",
|
|
1736
|
+
"CWE-639"
|
|
1737
|
+
],
|
|
1738
|
+
"recommendation": "Bind query to current session user",
|
|
1739
|
+
"status": "confirmed",
|
|
1740
|
+
"verification": {
|
|
1741
|
+
"verdict": "confirmed",
|
|
1742
|
+
"reason_code": "traced",
|
|
1743
|
+
"checked": [
|
|
1744
|
+
"coldfusion/app/OrderService.cfc lines 3-5",
|
|
1745
|
+
"cfargument usage pattern"
|
|
1746
|
+
],
|
|
1747
|
+
"control_found": null,
|
|
1748
|
+
"notes": "ColdFusion CFC lacks ownership verification. No session-based user ID binding found in evidence. Direct concatenation of orderId without ownership check.",
|
|
1749
|
+
"suggested_subtype": null,
|
|
1750
|
+
"suggested_cwe": null
|
|
1751
|
+
},
|
|
1752
|
+
"consensus": {
|
|
1753
|
+
"score": 0.95,
|
|
1754
|
+
"agreement_level": "unanimous",
|
|
1755
|
+
"voted_models": [
|
|
1756
|
+
"qwen3.5:9b"
|
|
1757
|
+
],
|
|
1758
|
+
"rationale": "ColdFusion CFC lacks compensating controls; direct IDOR vulnerability confirmed."
|
|
1759
|
+
},
|
|
1760
|
+
"attack_vector": {
|
|
1761
|
+
"method": "GET",
|
|
1762
|
+
"path": "/coldfusion/app/OrderService.cfc?method=getOrder&orderId=",
|
|
1763
|
+
"target_param": "orderId"
|
|
1764
|
+
}
|
|
1765
|
+
},
|
|
1766
|
+
{
|
|
1767
|
+
"finding_id": "BOLA-R-0006-1",
|
|
1768
|
+
"category": "bola",
|
|
1769
|
+
"subtype": "missing_ownership_check",
|
|
1770
|
+
"title": "Order detail readable by any authenticated user via url.id - no ownership verification on primary query",
|
|
1771
|
+
"route_id": "R-0006",
|
|
1772
|
+
"endpoint": {
|
|
1773
|
+
"method": "ANY",
|
|
1774
|
+
"path": "/coldfusion/app/order.cfm"
|
|
1775
|
+
},
|
|
1776
|
+
"location": {
|
|
1777
|
+
"file": "coldfusion/app/order.cfm",
|
|
1778
|
+
"start_line": 3,
|
|
1779
|
+
"end_line": 4
|
|
1780
|
+
},
|
|
1781
|
+
"evidence": [
|
|
1782
|
+
{
|
|
1783
|
+
"file": "coldfusion/app/order.cfm",
|
|
1784
|
+
"line": 2,
|
|
1785
|
+
"code": "<cfif NOT structKeyExists(session, \"userID\")><cflocation url=\"/login.cfm\"></cfif>",
|
|
1786
|
+
"why": "Only checks login, not ownership"
|
|
1787
|
+
},
|
|
1788
|
+
{
|
|
1789
|
+
"file": "coldfusion/app/order.cfm",
|
|
1790
|
+
"line": 3,
|
|
1791
|
+
"code": "SELECT * FROM orders WHERE id = #url.id#",
|
|
1792
|
+
"why": "No ownership check - reads any order by ID"
|
|
1793
|
+
}
|
|
1794
|
+
],
|
|
1795
|
+
"attack_scenario": "User A sends GET /order.cfm?url.id=<order id of user B> and receives user B's order details.",
|
|
1796
|
+
"impact": "Read other customers' orders (address, etc.).",
|
|
1797
|
+
"severity": "high",
|
|
1798
|
+
"confidence": "high",
|
|
1799
|
+
"cwe": [
|
|
1800
|
+
"CWE-284",
|
|
1801
|
+
"CWE-639"
|
|
1802
|
+
],
|
|
1803
|
+
"recommendation": "Bind query to current session user",
|
|
1804
|
+
"status": "confirmed",
|
|
1805
|
+
"verification": {
|
|
1806
|
+
"verdict": "confirmed",
|
|
1807
|
+
"reason_code": "traced",
|
|
1808
|
+
"checked": [
|
|
1809
|
+
"coldfusion/app/order.cfm lines 2-3",
|
|
1810
|
+
"session check pattern"
|
|
1811
|
+
],
|
|
1812
|
+
"control_found": null,
|
|
1813
|
+
"notes": "ColdFusion page only checks login status, not ownership. No session-based user ID binding found in evidence. Direct concatenation of url.id without ownership check.",
|
|
1814
|
+
"suggested_subtype": null,
|
|
1815
|
+
"suggested_cwe": null
|
|
1816
|
+
},
|
|
1817
|
+
"consensus": {
|
|
1818
|
+
"score": 0.95,
|
|
1819
|
+
"agreement_level": "unanimous",
|
|
1820
|
+
"voted_models": [
|
|
1821
|
+
"qwen3.5:9b"
|
|
1822
|
+
],
|
|
1823
|
+
"rationale": "ColdFusion page lacks compensating controls; login-only check does not prevent IDOR."
|
|
1824
|
+
},
|
|
1825
|
+
"attack_vector": {
|
|
1826
|
+
"method": "GET",
|
|
1827
|
+
"path": "/coldfusion/app/order.cfm?url.id=",
|
|
1828
|
+
"target_param": "url.id"
|
|
1829
|
+
}
|
|
1830
|
+
},
|
|
1831
|
+
{
|
|
1832
|
+
"finding_id": "BOLA-R-0007-1",
|
|
1833
|
+
"category": "bola",
|
|
1834
|
+
"subtype": "missing_ownership_check",
|
|
1835
|
+
"title": "User delete accessible to any authenticated user - no ownership verification on user id",
|
|
1836
|
+
"route_id": "R-0007",
|
|
1837
|
+
"endpoint": {
|
|
1838
|
+
"method": "POST",
|
|
1839
|
+
"path": "/Admin/DeleteUser"
|
|
1840
|
+
},
|
|
1841
|
+
"location": {
|
|
1842
|
+
"file": "dotnet-legacy/Web/Controllers/AdminController.cs",
|
|
1843
|
+
"start_line": 12,
|
|
1844
|
+
"end_line": 16
|
|
1845
|
+
},
|
|
1846
|
+
"evidence": [
|
|
1847
|
+
{
|
|
1848
|
+
"file": "dotnet-legacy/Web/Controllers/AdminController.cs",
|
|
1849
|
+
"line": 12,
|
|
1850
|
+
"code": "[HttpPost]",
|
|
1851
|
+
"why": "No ownership check on user being deleted"
|
|
1852
|
+
},
|
|
1853
|
+
{
|
|
1854
|
+
"file": "dotnet-legacy/Web/Controllers/AdminController.cs",
|
|
1855
|
+
"line": 14,
|
|
1856
|
+
"code": "var u = db.Users.Find(id);",
|
|
1857
|
+
"why": "Finds any user by ID without verifying current user owns it"
|
|
1858
|
+
}
|
|
1859
|
+
],
|
|
1860
|
+
"attack_scenario": "User A sends POST /Admin/DeleteUser?id=<user id of user B> and deletes user B's account.",
|
|
1861
|
+
"impact": "Delete another user's account record.",
|
|
1862
|
+
"severity": "high",
|
|
1863
|
+
"confidence": "high",
|
|
1864
|
+
"cwe": [
|
|
1865
|
+
"CWE-284",
|
|
1866
|
+
"CWE-639"
|
|
1867
|
+
],
|
|
1868
|
+
"recommendation": "Bind query to current session user",
|
|
1869
|
+
"status": "confirmed",
|
|
1870
|
+
"verification": {
|
|
1871
|
+
"verdict": "confirmed",
|
|
1872
|
+
"reason_code": "traced",
|
|
1873
|
+
"checked": [
|
|
1874
|
+
"dotnet-legacy/Web/Controllers/AdminController.cs lines 12-16",
|
|
1875
|
+
"HttpPost attribute pattern"
|
|
1876
|
+
],
|
|
1877
|
+
"control_found": null,
|
|
1878
|
+
"notes": ".NET controller lacks ownership verification. No session-based user ID binding found in evidence. Direct Find() without ownership check.",
|
|
1879
|
+
"suggested_subtype": null,
|
|
1880
|
+
"suggested_cwe": null
|
|
1881
|
+
},
|
|
1882
|
+
"consensus": {
|
|
1883
|
+
"score": 0.95,
|
|
1884
|
+
"agreement_level": "unanimous",
|
|
1885
|
+
"voted_models": [
|
|
1886
|
+
"qwen3.5:9b"
|
|
1887
|
+
],
|
|
1888
|
+
"rationale": ".NET controller lacks compensating controls; direct IDOR vulnerability confirmed."
|
|
1889
|
+
},
|
|
1890
|
+
"attack_vector": {
|
|
1891
|
+
"method": "POST",
|
|
1892
|
+
"path": "/Admin/DeleteUser?id=",
|
|
1893
|
+
"target_param": "id"
|
|
1894
|
+
}
|
|
1895
|
+
},
|
|
1896
|
+
{
|
|
1897
|
+
"finding_id": "BOLA-R-0008-1",
|
|
1898
|
+
"category": "bola",
|
|
1899
|
+
"subtype": "missing_ownership_check",
|
|
1900
|
+
"title": "User password reset accessible to any authenticated user - no ownership verification on user id",
|
|
1901
|
+
"route_id": "R-0008",
|
|
1902
|
+
"endpoint": {
|
|
1903
|
+
"method": "POST",
|
|
1904
|
+
"path": "/Admin/ResetPassword"
|
|
1905
|
+
},
|
|
1906
|
+
"location": {
|
|
1907
|
+
"file": "dotnet-legacy/Web/Controllers/AdminController.cs",
|
|
1908
|
+
"start_line": 22,
|
|
1909
|
+
"end_line": 26
|
|
1910
|
+
},
|
|
1911
|
+
"evidence": [
|
|
1912
|
+
{
|
|
1913
|
+
"file": "dotnet-legacy/Web/Controllers/AdminController.cs",
|
|
1914
|
+
"line": 22,
|
|
1915
|
+
"code": "[Authorize(Roles = \"Admin\")]",
|
|
1916
|
+
"why": "Only checks admin role, not ownership of user"
|
|
1917
|
+
},
|
|
1918
|
+
{
|
|
1919
|
+
"file": "dotnet-legacy/Web/Controllers/AdminController.cs",
|
|
1920
|
+
"line": 24,
|
|
1921
|
+
"code": "var u = db.Users.Find(id);",
|
|
1922
|
+
"why": "Finds any user by ID without verifying current user owns it"
|
|
1923
|
+
}
|
|
1924
|
+
],
|
|
1925
|
+
"attack_scenario": "User A sends POST /Admin/ResetPassword?id=<user id of user B> and resets user B's password.",
|
|
1926
|
+
"impact": "Reset another user's password (account takeover).",
|
|
1927
|
+
"severity": "high",
|
|
1928
|
+
"confidence": "high",
|
|
1929
|
+
"cwe": [
|
|
1930
|
+
"CWE-284",
|
|
1931
|
+
"CWE-639"
|
|
1932
|
+
],
|
|
1933
|
+
"recommendation": "Bind query to current session user",
|
|
1934
|
+
"status": "confirmed",
|
|
1935
|
+
"verification": {
|
|
1936
|
+
"verdict": "confirmed",
|
|
1937
|
+
"reason_code": "traced",
|
|
1938
|
+
"checked": [
|
|
1939
|
+
"dotnet-legacy/Web/Controllers/AdminController.cs lines 22-26",
|
|
1940
|
+
"Authorize attribute pattern"
|
|
1941
|
+
],
|
|
1942
|
+
"control_found": null,
|
|
1943
|
+
"notes": ".NET controller only checks Admin role, not ownership. No session-based user ID binding found in evidence. Direct Find() without ownership check.",
|
|
1944
|
+
"suggested_subtype": null,
|
|
1945
|
+
"suggested_cwe": null
|
|
1946
|
+
},
|
|
1947
|
+
"consensus": {
|
|
1948
|
+
"score": 0.95,
|
|
1949
|
+
"agreement_level": "unanimous",
|
|
1950
|
+
"voted_models": [
|
|
1951
|
+
"qwen3.5:9b"
|
|
1952
|
+
],
|
|
1953
|
+
"rationale": ".NET controller lacks compensating controls; role-only check does not prevent IDOR."
|
|
1954
|
+
},
|
|
1955
|
+
"attack_vector": {
|
|
1956
|
+
"method": "POST",
|
|
1957
|
+
"path": "/Admin/ResetPassword?id=",
|
|
1958
|
+
"target_param": "id"
|
|
1959
|
+
}
|
|
1960
|
+
},
|
|
1961
|
+
{
|
|
1962
|
+
"finding_id": "BOLA-R-0009-1",
|
|
1963
|
+
"category": "bola",
|
|
1964
|
+
"subtype": "missing_role_check",
|
|
1965
|
+
"title": "User export accessible to any authenticated user - no admin role verification on isAdmin parameter",
|
|
1966
|
+
"route_id": "R-0009",
|
|
1967
|
+
"endpoint": {
|
|
1968
|
+
"method": "POST",
|
|
1969
|
+
"path": "/Admin/Export"
|
|
1970
|
+
},
|
|
1971
|
+
"location": {
|
|
1972
|
+
"file": "dotnet-legacy/Web/Controllers/AdminController.cs",
|
|
1973
|
+
"start_line": 30,
|
|
1974
|
+
"end_line": 35
|
|
1975
|
+
},
|
|
1976
|
+
"evidence": [
|
|
1977
|
+
{
|
|
1978
|
+
"file": "dotnet-legacy/Web/Controllers/AdminController.cs",
|
|
1979
|
+
"line": 32,
|
|
1980
|
+
"code": "if (Request.QueryString[\"isAdmin\"] == \"true\")",
|
|
1981
|
+
"why": "Checks query parameter instead of actual admin role"
|
|
1982
|
+
},
|
|
1983
|
+
{
|
|
1984
|
+
"file": "dotnet-legacy/Web/Controllers/AdminController.cs",
|
|
1985
|
+
"line": 34,
|
|
1986
|
+
"code": "return Json(db.Users.ToList(), JsonRequestBehavior.AllowGet);",
|
|
1987
|
+
"why": "Returns all users without ownership verification"
|
|
1988
|
+
}
|
|
1989
|
+
],
|
|
1990
|
+
"attack_scenario": "User A sends POST /Admin/Export?isAdmin=true and receives list of all users.",
|
|
1991
|
+
"impact": "Read all user data (PII, credentials, etc.).",
|
|
1992
|
+
"severity": "high",
|
|
1993
|
+
"confidence": "high",
|
|
1994
|
+
"cwe": [
|
|
1995
|
+
"CWE-284",
|
|
1996
|
+
"CWE-639"
|
|
1997
|
+
],
|
|
1998
|
+
"recommendation": "Verify actual admin role instead of query parameter",
|
|
1999
|
+
"status": "confirmed",
|
|
2000
|
+
"verification": {
|
|
2001
|
+
"verdict": "confirmed",
|
|
2002
|
+
"reason_code": "traced",
|
|
2003
|
+
"checked": [
|
|
2004
|
+
"dotnet-legacy/Web/Controllers/AdminController.cs lines 30-35",
|
|
2005
|
+
"QueryString isAdmin check pattern"
|
|
2006
|
+
],
|
|
2007
|
+
"control_found": null,
|
|
2008
|
+
"notes": ".NET controller trusts query parameter instead of actual admin role. No session-based user ID binding found in evidence. Direct ToList() without ownership check.",
|
|
2009
|
+
"suggested_subtype": null,
|
|
2010
|
+
"suggested_cwe": null
|
|
2011
|
+
},
|
|
2012
|
+
"consensus": {
|
|
2013
|
+
"score": 0.95,
|
|
2014
|
+
"agreement_level": "unanimous",
|
|
2015
|
+
"voted_models": [
|
|
2016
|
+
"qwen3.5:9b"
|
|
2017
|
+
],
|
|
2018
|
+
"rationale": ".NET controller lacks compensating controls; query parameter trust does not prevent IDOR."
|
|
2019
|
+
},
|
|
2020
|
+
"attack_vector": {
|
|
2021
|
+
"method": "POST",
|
|
2022
|
+
"path": "/Admin/Export?isAdmin=true",
|
|
2023
|
+
"target_param": "isAdmin"
|
|
2024
|
+
}
|
|
2025
|
+
},
|
|
2026
|
+
{
|
|
2027
|
+
"finding_id": "BOLA-R-0013-1",
|
|
2028
|
+
"category": "bola",
|
|
2029
|
+
"subtype": "missing_ownership_check",
|
|
2030
|
+
"title": "Invoice detail readable by any authenticated user via id - no ownership verification on Details endpoint",
|
|
2031
|
+
"route_id": "R-0013",
|
|
2032
|
+
"endpoint": {
|
|
2033
|
+
"method": "ANY",
|
|
2034
|
+
"path": "/Invoice/Details"
|
|
2035
|
+
},
|
|
2036
|
+
"location": {
|
|
2037
|
+
"file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
|
|
2038
|
+
"start_line": 11,
|
|
2039
|
+
"end_line": 14
|
|
2040
|
+
},
|
|
2041
|
+
"evidence": [
|
|
2042
|
+
{
|
|
2043
|
+
"file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
|
|
2044
|
+
"line": 12,
|
|
2045
|
+
"code": "public ActionResult Details(int id)",
|
|
2046
|
+
"why": "No ownership check on invoice being read"
|
|
2047
|
+
},
|
|
2048
|
+
{
|
|
2049
|
+
"file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
|
|
2050
|
+
"line": 13,
|
|
2051
|
+
"code": "var inv = db.Invoices.Find(id);",
|
|
2052
|
+
"why": "Finds any invoice by ID without verifying current user owns it"
|
|
2053
|
+
}
|
|
2054
|
+
],
|
|
2055
|
+
"attack_scenario": "User A sends GET /Invoice/Details?id=<invoice id of user B> and receives user B's invoice.",
|
|
2056
|
+
"impact": "Read other customers' invoices (PII, amounts, etc.).",
|
|
2057
|
+
"severity": "high",
|
|
2058
|
+
"confidence": "high",
|
|
2059
|
+
"cwe": [
|
|
2060
|
+
"CWE-284",
|
|
2061
|
+
"CWE-639"
|
|
2062
|
+
],
|
|
2063
|
+
"recommendation": "Bind query to current session user",
|
|
2064
|
+
"status": "confirmed",
|
|
2065
|
+
"verification": {
|
|
2066
|
+
"verdict": "confirmed",
|
|
2067
|
+
"reason_code": "traced",
|
|
2068
|
+
"checked": [
|
|
2069
|
+
"dotnet-legacy/Web/Controllers/InvoiceController.cs lines 11-14",
|
|
2070
|
+
"Find() pattern"
|
|
2071
|
+
],
|
|
2072
|
+
"control_found": null,
|
|
2073
|
+
"notes": ".NET controller lacks ownership verification. No session-based user ID binding found in evidence. Direct Find() without ownership check.",
|
|
2074
|
+
"suggested_subtype": null,
|
|
2075
|
+
"suggested_cwe": null
|
|
2076
|
+
},
|
|
2077
|
+
"consensus": {
|
|
2078
|
+
"score": 0.95,
|
|
2079
|
+
"agreement_level": "unanimous",
|
|
2080
|
+
"voted_models": [
|
|
2081
|
+
"qwen3.5:9b"
|
|
2082
|
+
],
|
|
2083
|
+
"rationale": ".NET controller lacks compensating controls; direct IDOR vulnerability confirmed."
|
|
2084
|
+
},
|
|
2085
|
+
"attack_vector": {
|
|
2086
|
+
"method": "GET",
|
|
2087
|
+
"path": "/Invoice/Details?id=",
|
|
2088
|
+
"target_param": "id"
|
|
2089
|
+
}
|
|
2090
|
+
},
|
|
2091
|
+
{
|
|
2092
|
+
"finding_id": "BOLA-R-0015-1",
|
|
2093
|
+
"category": "bola",
|
|
2094
|
+
"subtype": "missing_ownership_check",
|
|
2095
|
+
"title": "Invoice search returns all invoices - no ownership verification on Search endpoint",
|
|
2096
|
+
"route_id": "R-0015",
|
|
2097
|
+
"endpoint": {
|
|
2098
|
+
"method": "POST",
|
|
2099
|
+
"path": "/Invoice/Search"
|
|
2100
|
+
},
|
|
2101
|
+
"location": {
|
|
2102
|
+
"file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
|
|
2103
|
+
"start_line": 26,
|
|
2104
|
+
"end_line": 30
|
|
2105
|
+
},
|
|
2106
|
+
"evidence": [
|
|
2107
|
+
{
|
|
2108
|
+
"file": "dotnet-legacy/Web/Controllers/InvoiceController.cs",
|
|
2109
|
+
"line": 28,
|
|
2110
|
+
"code": "SELECT * FROM Invoices WHERE Customer = '\" + customer + \"'",
|
|
2111
|
+
"why": "No ownership check - returns all invoices matching customer"
|
|
2112
|
+
}
|
|
2113
|
+
],
|
|
2114
|
+
"attack_scenario": "User A sends POST /Invoice/Search with customer=<any customer name> and receives all matching invoices.",
|
|
2115
|
+
"impact": "Read other customers' invoices (PII, amounts, etc.).",
|
|
2116
|
+
"severity": "high",
|
|
2117
|
+
"confidence": "high",
|
|
2118
|
+
"cwe": [
|
|
2119
|
+
"CWE-284",
|
|
2120
|
+
"CWE-639"
|
|
2121
|
+
],
|
|
2122
|
+
"recommendation": "Bind query to current session user",
|
|
2123
|
+
"status": "confirmed",
|
|
2124
|
+
"verification": {
|
|
2125
|
+
"verdict": "confirmed",
|
|
2126
|
+
"reason_code": "traced",
|
|
2127
|
+
"checked": [
|
|
2128
|
+
"dotnet-legacy/Web/Controllers/InvoiceController.cs lines 26-30",
|
|
2129
|
+
"Search pattern"
|
|
2130
|
+
],
|
|
2131
|
+
"control_found": null,
|
|
2132
|
+
"notes": ".NET controller lacks ownership verification. No session-based user ID binding found in evidence. Direct string concatenation without ownership check.",
|
|
2133
|
+
"suggested_subtype": null,
|
|
2134
|
+
"suggested_cwe": null
|
|
2135
|
+
},
|
|
2136
|
+
"consensus": {
|
|
2137
|
+
"score": 0.95,
|
|
2138
|
+
"agreement_level": "unanimous",
|
|
2139
|
+
"voted_models": [
|
|
2140
|
+
"qwen3.5:9b"
|
|
2141
|
+
],
|
|
2142
|
+
"rationale": ".NET controller lacks compensating controls; direct IDOR vulnerability confirmed."
|
|
2143
|
+
},
|
|
2144
|
+
"attack_vector": {
|
|
2145
|
+
"method": "POST",
|
|
2146
|
+
"path": "/Invoice/Search",
|
|
2147
|
+
"target_param": "customer"
|
|
2148
|
+
}
|
|
2149
|
+
},
|
|
2150
|
+
{
|
|
2151
|
+
"finding_id": "BOLA-R-0017-1",
|
|
2152
|
+
"category": "bola",
|
|
2153
|
+
"subtype": "missing_ownership_check",
|
|
2154
|
+
"title": "Order update vulnerable to IDOR - no ownership verification on OrderId",
|
|
2155
|
+
"route_id": "R-0017",
|
|
2156
|
+
"endpoint": {
|
|
2157
|
+
"method": "ANY",
|
|
2158
|
+
"path": "/dotnet-legacy/Web/Orders/EditOrder.aspx"
|
|
2159
|
+
},
|
|
2160
|
+
"location": {
|
|
2161
|
+
"file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
|
|
2162
|
+
"start_line": 8,
|
|
2163
|
+
"end_line": 9
|
|
2164
|
+
},
|
|
2165
|
+
"evidence": [
|
|
2166
|
+
{
|
|
2167
|
+
"file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
|
|
2168
|
+
"line": 8,
|
|
2169
|
+
"code": "Dim sql As String = \"UPDATE Orders SET Note = '\" & txtNote.Text & \"'\"",
|
|
2170
|
+
"why": "No ownership check on order being updated"
|
|
2171
|
+
},
|
|
2172
|
+
{
|
|
2173
|
+
"file": "dotnet-legacy/Web/Orders/EditOrder.aspx.vb",
|
|
2174
|
+
"line": 9,
|
|
2175
|
+
"code": "sql &= \" WHERE OrderId = \" & hfOrderId.Value",
|
|
2176
|
+
"why": "Updates any order by ID without verifying current user owns it"
|
|
2177
|
+
}
|
|
2178
|
+
],
|
|
2179
|
+
"attack_scenario": "User A sends POST /EditOrder.aspx with OrderId=<order id of user B> and modifies user B's order note.",
|
|
2180
|
+
"impact": "Modify another customer's order data.",
|
|
2181
|
+
"severity": "high",
|
|
2182
|
+
"confidence": "high",
|
|
2183
|
+
"cwe": [
|
|
2184
|
+
"CWE-284",
|
|
2185
|
+
"CWE-639"
|
|
2186
|
+
],
|
|
2187
|
+
"recommendation": "Bind query to current session user",
|
|
2188
|
+
"status": "confirmed",
|
|
2189
|
+
"verification": {
|
|
2190
|
+
"verdict": "confirmed",
|
|
2191
|
+
"reason_code": "traced",
|
|
2192
|
+
"checked": [
|
|
2193
|
+
"dotnet-legacy/Web/Orders/EditOrder.aspx.vb lines 8-9",
|
|
2194
|
+
"global filters in web.config",
|
|
2195
|
+
"session user binding"
|
|
2196
|
+
],
|
|
2197
|
+
"control_found": null,
|
|
2198
|
+
"notes": "SQL UPDATE with direct concatenation of OrderId without ownership verification. No compensating control found.",
|
|
2199
|
+
"suggested_subtype": null,
|
|
2200
|
+
"suggested_cwe": null
|
|
2201
|
+
},
|
|
2202
|
+
"consensus": {
|
|
2203
|
+
"score": 0.95,
|
|
2204
|
+
"agreement_level": "unanimous",
|
|
2205
|
+
"voted_models": [
|
|
2206
|
+
"qwen3.5:9b"
|
|
2207
|
+
],
|
|
2208
|
+
"rationale": "Direct SQL construction with user-controlled OrderId parameter without ownership check."
|
|
2209
|
+
},
|
|
2210
|
+
"attack_vector": {
|
|
2211
|
+
"method": "POST",
|
|
2212
|
+
"path": "/dotnet-legacy/Web/Orders/EditOrder.aspx",
|
|
2213
|
+
"target_param": "hfOrderId.Value"
|
|
2214
|
+
}
|
|
2215
|
+
},
|
|
2216
|
+
{
|
|
2217
|
+
"finding_id": "BOLA-R-0018-1",
|
|
2218
|
+
"category": "bola",
|
|
2219
|
+
"subtype": "missing_ownership_check",
|
|
2220
|
+
"title": "Order detail readable by any authenticated user via id - no ownership verification on primary query",
|
|
2221
|
+
"route_id": "R-0018",
|
|
2222
|
+
"endpoint": {
|
|
2223
|
+
"method": "ANY",
|
|
2224
|
+
"path": "/dotnet-legacy/Web/Orders/OrderDetail.aspx"
|
|
2225
|
+
},
|
|
2226
|
+
"location": {
|
|
2227
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
2228
|
+
"start_line": 13,
|
|
2229
|
+
"end_line": 14
|
|
2230
|
+
},
|
|
2231
|
+
"evidence": [
|
|
2232
|
+
{
|
|
2233
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
2234
|
+
"line": 10,
|
|
2235
|
+
"code": "string id = Request.QueryString[\"id\"];",
|
|
2236
|
+
"why": "ID from query string (request-controlled)"
|
|
2237
|
+
},
|
|
2238
|
+
{
|
|
2239
|
+
"file": "dotnet-legacy/Web/Orders/OrderDetail.aspx.cs",
|
|
2240
|
+
"line": 13,
|
|
2241
|
+
"code": "var cmd = new SqlCommand(\"SELECT * FROM Orders WHERE OrderId = \" + id, conn);",
|
|
2242
|
+
"why": "No ownership check - reads any order by ID"
|
|
2243
|
+
}
|
|
2244
|
+
],
|
|
2245
|
+
"attack_scenario": "User A sends GET /OrderDetail.aspx?id=<order id of user B> and receives user B's order details.",
|
|
2246
|
+
"impact": "Read other customers' orders (address, etc.).",
|
|
2247
|
+
"severity": "high",
|
|
2248
|
+
"confidence": "high",
|
|
2249
|
+
"cwe": [
|
|
2250
|
+
"CWE-284",
|
|
2251
|
+
"CWE-639"
|
|
2252
|
+
],
|
|
2253
|
+
"recommendation": "Bind query to current session user",
|
|
2254
|
+
"status": "confirmed",
|
|
2255
|
+
"verification": {
|
|
2256
|
+
"verdict": "confirmed",
|
|
2257
|
+
"reason_code": "traced",
|
|
2258
|
+
"checked": [
|
|
2259
|
+
"dotnet-legacy/Web/Orders/OrderDetail.aspx.cs lines 10,13",
|
|
2260
|
+
"global filters in web.config",
|
|
2261
|
+
"session user binding"
|
|
2262
|
+
],
|
|
2263
|
+
"control_found": null,
|
|
2264
|
+
"notes": "SQL SELECT with direct concatenation of OrderId without ownership verification. No compensating control found.",
|
|
2265
|
+
"suggested_subtype": null,
|
|
2266
|
+
"suggested_cwe": null
|
|
2267
|
+
},
|
|
2268
|
+
"consensus": {
|
|
2269
|
+
"score": 0.95,
|
|
2270
|
+
"agreement_level": "unanimous",
|
|
2271
|
+
"voted_models": [
|
|
2272
|
+
"qwen3.5:9b"
|
|
2273
|
+
],
|
|
2274
|
+
"rationale": "Direct SQL construction with user-controlled OrderId parameter without ownership check."
|
|
2275
|
+
},
|
|
2276
|
+
"attack_vector": {
|
|
2277
|
+
"method": "GET",
|
|
2278
|
+
"path": "/dotnet-legacy/Web/Orders/OrderDetail.aspx?id={id}",
|
|
2279
|
+
"target_param": "id"
|
|
2280
|
+
}
|
|
2281
|
+
},
|
|
2282
|
+
{
|
|
2283
|
+
"finding_id": "BOLA-R-0019-1",
|
|
2284
|
+
"category": "bola",
|
|
2285
|
+
"subtype": "missing_ownership_check",
|
|
2286
|
+
"title": "Order detail readable by any authenticated user via no - no ownership verification",
|
|
2287
|
+
"route_id": "R-0019",
|
|
2288
|
+
"endpoint": {
|
|
2289
|
+
"method": "ANY",
|
|
2290
|
+
"path": "/java-legacy/WebContent/order/detail.jsp"
|
|
2291
|
+
},
|
|
2292
|
+
"location": {
|
|
2293
|
+
"file": "java-legacy/WebContent/order/detail.jsp",
|
|
2294
|
+
"start_line": 3,
|
|
2295
|
+
"end_line": 5
|
|
2296
|
+
},
|
|
2297
|
+
"evidence": [
|
|
2298
|
+
{
|
|
2299
|
+
"file": "java-legacy/WebContent/order/detail.jsp",
|
|
2300
|
+
"line": 3,
|
|
2301
|
+
"code": "String no = request.getParameter(\"no\");",
|
|
2302
|
+
"why": "ID from request parameter (request-controlled)"
|
|
2303
|
+
},
|
|
2304
|
+
{
|
|
2305
|
+
"file": "java-legacy/WebContent/order/detail.jsp",
|
|
2306
|
+
"line": 5,
|
|
2307
|
+
"code": "SELECT * FROM orders WHERE order_no = '\" + no + \"'",
|
|
2308
|
+
"why": "No ownership check - reads any order by ID"
|
|
2309
|
+
}
|
|
2310
|
+
],
|
|
2311
|
+
"attack_scenario": "User A sends GET /detail.jsp?no=<order id of user B> and receives user B's order details.",
|
|
2312
|
+
"impact": "Read other customers' orders (address, etc.).",
|
|
2313
|
+
"severity": "high",
|
|
2314
|
+
"confidence": "high",
|
|
2315
|
+
"cwe": [
|
|
2316
|
+
"CWE-284",
|
|
2317
|
+
"CWE-639"
|
|
2318
|
+
],
|
|
2319
|
+
"recommendation": "Bind query to current session user",
|
|
2320
|
+
"status": "confirmed",
|
|
2321
|
+
"verification": {
|
|
2322
|
+
"verdict": "confirmed",
|
|
2323
|
+
"reason_code": "traced",
|
|
2324
|
+
"checked": [
|
|
2325
|
+
"java-legacy/WebContent/order/detail.jsp lines 3,5",
|
|
2326
|
+
"global filters in web.xml",
|
|
2327
|
+
"session user binding"
|
|
2328
|
+
],
|
|
2329
|
+
"control_found": null,
|
|
2330
|
+
"notes": "SQL SELECT with direct concatenation of order_no without ownership verification. No compensating control found.",
|
|
2331
|
+
"suggested_subtype": null,
|
|
2332
|
+
"suggested_cwe": null
|
|
2333
|
+
},
|
|
2334
|
+
"consensus": {
|
|
2335
|
+
"score": 0.95,
|
|
2336
|
+
"agreement_level": "unanimous",
|
|
2337
|
+
"voted_models": [
|
|
2338
|
+
"qwen3.5:9b"
|
|
2339
|
+
],
|
|
2340
|
+
"rationale": "Direct SQL construction with user-controlled order_no parameter without ownership check."
|
|
2341
|
+
},
|
|
2342
|
+
"attack_vector": {
|
|
2343
|
+
"method": "GET",
|
|
2344
|
+
"path": "/java-legacy/WebContent/order/detail.jsp?no={no}",
|
|
2345
|
+
"target_param": "no"
|
|
2346
|
+
}
|
|
2347
|
+
},
|
|
2348
|
+
{
|
|
2349
|
+
"finding_id": "BOLA-R-0020-1",
|
|
2350
|
+
"category": "bola",
|
|
2351
|
+
"subtype": "missing_ownership_check",
|
|
2352
|
+
"title": "Account save endpoint missing ownership verification - any user can modify another's account",
|
|
2353
|
+
"route_id": "R-0020",
|
|
2354
|
+
"endpoint": {
|
|
2355
|
+
"method": "POST",
|
|
2356
|
+
"path": "/account/save"
|
|
2357
|
+
},
|
|
2358
|
+
"location": {
|
|
2359
|
+
"file": "java-legacy/src/com/acme/web/AccountController.java",
|
|
2360
|
+
"start_line": 13,
|
|
2361
|
+
"end_line": 17
|
|
2362
|
+
},
|
|
2363
|
+
"evidence": [
|
|
2364
|
+
{
|
|
2365
|
+
"file": "java-legacy/src/com/acme/web/AccountController.java",
|
|
2366
|
+
"line": 14,
|
|
2367
|
+
"code": "public String save(@ModelAttribute Account account) {",
|
|
2368
|
+
"why": "Account object bound from request without user_id verification"
|
|
2369
|
+
},
|
|
2370
|
+
{
|
|
2371
|
+
"file": "java-legacy/src/com/acme/web/AccountController.java",
|
|
2372
|
+
"line": 15,
|
|
2373
|
+
"code": "accountDao.save(account);",
|
|
2374
|
+
"why": "Saves account without checking if current user owns this account"
|
|
2375
|
+
}
|
|
2376
|
+
],
|
|
2377
|
+
"attack_scenario": "User A sends POST /account/save with User B's account data and successfully saves it as their own.",
|
|
2378
|
+
"impact": "Users can modify any other user's account information (name, email, etc.).",
|
|
2379
|
+
"severity": "high",
|
|
2380
|
+
"confidence": "high",
|
|
2381
|
+
"cwe": [
|
|
2382
|
+
"CWE-284",
|
|
2383
|
+
"CWE-639"
|
|
2384
|
+
],
|
|
2385
|
+
"owasp": [
|
|
2386
|
+
"API1:2023"
|
|
2387
|
+
],
|
|
2388
|
+
"recommendation": "Bind query to current session user",
|
|
2389
|
+
"status": "confirmed",
|
|
2390
|
+
"verification": {
|
|
2391
|
+
"verdict": "confirmed",
|
|
2392
|
+
"reason_code": "traced",
|
|
2393
|
+
"checked": [
|
|
2394
|
+
"java-legacy/src/com/acme/web/AccountController.java lines 14-17",
|
|
2395
|
+
"global filters in web.xml",
|
|
2396
|
+
"session user binding"
|
|
2397
|
+
],
|
|
2398
|
+
"control_found": null,
|
|
2399
|
+
"notes": "DAO save() called with request-bound Account object without ownership verification. No compensating control found.",
|
|
2400
|
+
"suggested_subtype": null,
|
|
2401
|
+
"suggested_cwe": null
|
|
2402
|
+
},
|
|
2403
|
+
"consensus": {
|
|
2404
|
+
"score": 0.95,
|
|
2405
|
+
"agreement_level": "unanimous",
|
|
2406
|
+
"voted_models": [
|
|
2407
|
+
"qwen3.5:9b"
|
|
2408
|
+
],
|
|
2409
|
+
"rationale": "Direct DAO save with user-controlled Account object without ownership check."
|
|
2410
|
+
},
|
|
2411
|
+
"attack_vector": {
|
|
2412
|
+
"method": "POST",
|
|
2413
|
+
"path": "/account/save",
|
|
2414
|
+
"target_param": "Account object (request-bound)"
|
|
2415
|
+
}
|
|
2416
|
+
},
|
|
2417
|
+
{
|
|
2418
|
+
"finding_id": "BOLA-R-0021-1",
|
|
2419
|
+
"category": "bola",
|
|
2420
|
+
"subtype": "missing_ownership_check",
|
|
2421
|
+
"title": "Account rename endpoint missing ownership verification - any user can rename another's account",
|
|
2422
|
+
"route_id": "R-0021",
|
|
2423
|
+
"endpoint": {
|
|
2424
|
+
"method": "POST",
|
|
2425
|
+
"path": "/account/rename"
|
|
2426
|
+
},
|
|
2427
|
+
"location": {
|
|
2428
|
+
"file": "java-legacy/src/com/acme/web/AccountController.java",
|
|
2429
|
+
"start_line": 19,
|
|
2430
|
+
"end_line": 23
|
|
2431
|
+
},
|
|
2432
|
+
"evidence": [
|
|
2433
|
+
{
|
|
2434
|
+
"file": "java-legacy/src/com/acme/web/AccountController.java",
|
|
2435
|
+
"line": 20,
|
|
2436
|
+
"code": "public String rename(@ModelAttribute RenameForm form) {",
|
|
2437
|
+
"why": "RenameForm bound from request without user_id verification"
|
|
2438
|
+
},
|
|
2439
|
+
{
|
|
2440
|
+
"file": "java-legacy/src/com/acme/web/AccountController.java",
|
|
2441
|
+
"line": 21,
|
|
2442
|
+
"code": "accountDao.rename(form.getId(), form.getName());",
|
|
2443
|
+
"why": "Renames account without checking if current user owns this account"
|
|
2444
|
+
}
|
|
2445
|
+
],
|
|
2446
|
+
"attack_scenario": "User A sends POST /account/rename with User B's account ID and new name, successfully renaming User B's account.",
|
|
2447
|
+
"impact": "Users can rename any other user's account, potentially causing confusion or impersonation.",
|
|
2448
|
+
"severity": "high",
|
|
2449
|
+
"confidence": "high",
|
|
2450
|
+
"cwe": [
|
|
2451
|
+
"CWE-284",
|
|
2452
|
+
"CWE-639"
|
|
2453
|
+
],
|
|
2454
|
+
"owasp": [
|
|
2455
|
+
"API1:2023"
|
|
2456
|
+
],
|
|
2457
|
+
"recommendation": "Bind query to current session user",
|
|
2458
|
+
"status": "confirmed",
|
|
2459
|
+
"verification": {
|
|
2460
|
+
"verdict": "confirmed",
|
|
2461
|
+
"reason_code": "traced",
|
|
2462
|
+
"checked": [
|
|
2463
|
+
"java-legacy/src/com/acme/web/AccountController.java lines 19-23",
|
|
2464
|
+
"global filters in web.xml",
|
|
2465
|
+
"session user binding"
|
|
2466
|
+
],
|
|
2467
|
+
"control_found": null,
|
|
2468
|
+
"notes": "DAO rename() called with request-bound form without ownership verification. No compensating control found.",
|
|
2469
|
+
"suggested_subtype": null,
|
|
2470
|
+
"suggested_cwe": null
|
|
2471
|
+
},
|
|
2472
|
+
"consensus": {
|
|
2473
|
+
"score": 0.95,
|
|
2474
|
+
"agreement_level": "unanimous",
|
|
2475
|
+
"voted_models": [
|
|
2476
|
+
"qwen3.5:9b"
|
|
2477
|
+
],
|
|
2478
|
+
"rationale": "Direct DAO rename with user-controlled form object without ownership check."
|
|
2479
|
+
},
|
|
2480
|
+
"attack_vector": {
|
|
2481
|
+
"method": "POST",
|
|
2482
|
+
"path": "/account/rename",
|
|
2483
|
+
"target_param": "form.getId()"
|
|
2484
|
+
}
|
|
2485
|
+
},
|
|
2486
|
+
{
|
|
2487
|
+
"finding_id": "BOLA-R-0022-1",
|
|
2488
|
+
"category": "bola",
|
|
2489
|
+
"subtype": "missing_ownership_check",
|
|
2490
|
+
"title": "Account API update endpoint missing ownership verification - any user can modify another's account",
|
|
2491
|
+
"route_id": "R-0022",
|
|
2492
|
+
"endpoint": {
|
|
2493
|
+
"method": "PUT",
|
|
2494
|
+
"path": "/account/api"
|
|
2495
|
+
},
|
|
2496
|
+
"location": {
|
|
2497
|
+
"file": "java-legacy/src/com/acme/web/AccountController.java",
|
|
2498
|
+
"start_line": 25,
|
|
2499
|
+
"end_line": 29
|
|
2500
|
+
},
|
|
2501
|
+
"evidence": [
|
|
2502
|
+
{
|
|
2503
|
+
"file": "java-legacy/src/com/acme/web/AccountController.java",
|
|
2504
|
+
"line": 26,
|
|
2505
|
+
"code": "public Account update(@RequestBody Account account) {",
|
|
2506
|
+
"why": "Account object bound from request without user_id verification"
|
|
2507
|
+
},
|
|
2508
|
+
{
|
|
2509
|
+
"file": "java-legacy/src/com/acme/web/AccountController.java",
|
|
2510
|
+
"line": 27,
|
|
2511
|
+
"code": "return accountDao.save(account);",
|
|
2512
|
+
"why": "Saves account without checking if current user owns this account"
|
|
2513
|
+
}
|
|
2514
|
+
],
|
|
2515
|
+
"attack_scenario": "User A sends PUT /account/api with User B's account data and successfully updates it.",
|
|
2516
|
+
"impact": "Users can modify any other user's account information via API.",
|
|
2517
|
+
"severity": "high",
|
|
2518
|
+
"confidence": "high",
|
|
2519
|
+
"cwe": [
|
|
2520
|
+
"CWE-284",
|
|
2521
|
+
"CWE-639"
|
|
2522
|
+
],
|
|
2523
|
+
"owasp": [
|
|
2524
|
+
"API1:2023"
|
|
2525
|
+
],
|
|
2526
|
+
"recommendation": "Bind query to current session user",
|
|
2527
|
+
"status": "confirmed",
|
|
2528
|
+
"verification": {
|
|
2529
|
+
"verdict": "confirmed",
|
|
2530
|
+
"reason_code": "traced",
|
|
2531
|
+
"checked": [
|
|
2532
|
+
"java-legacy/src/com/acme/web/AccountController.java lines 25-29",
|
|
2533
|
+
"global filters in web.xml",
|
|
2534
|
+
"session user binding"
|
|
2535
|
+
],
|
|
2536
|
+
"control_found": null,
|
|
2537
|
+
"notes": "DAO save() called with @RequestBody Account without ownership verification. No compensating control found.",
|
|
2538
|
+
"suggested_subtype": null,
|
|
2539
|
+
"suggested_cwe": null
|
|
2540
|
+
},
|
|
2541
|
+
"consensus": {
|
|
2542
|
+
"score": 0.95,
|
|
2543
|
+
"agreement_level": "unanimous",
|
|
2544
|
+
"voted_models": [
|
|
2545
|
+
"qwen3.5:9b"
|
|
2546
|
+
],
|
|
2547
|
+
"rationale": "Direct DAO save with user-controlled @RequestBody object without ownership check."
|
|
2548
|
+
},
|
|
2549
|
+
"attack_vector": {
|
|
2550
|
+
"method": "PUT",
|
|
2551
|
+
"path": "/account/api",
|
|
2552
|
+
"target_param": "Account object (request-bound)"
|
|
2553
|
+
}
|
|
2554
|
+
},
|
|
2555
|
+
{
|
|
2556
|
+
"finding_id": "BOLA-R-0023-1",
|
|
2557
|
+
"category": "bola",
|
|
2558
|
+
"subtype": "missing_role_check",
|
|
2559
|
+
"title": "Admin user delete endpoint missing ownership verification - any admin can delete any user",
|
|
2560
|
+
"route_id": "R-0023",
|
|
2561
|
+
"endpoint": {
|
|
2562
|
+
"method": "POST",
|
|
2563
|
+
"path": "/admin/users/{id}/delete"
|
|
2564
|
+
},
|
|
2565
|
+
"location": {
|
|
2566
|
+
"file": "java-legacy/src/com/acme/web/AdminUserController.java",
|
|
2567
|
+
"start_line": 13,
|
|
2568
|
+
"end_line": 17
|
|
2569
|
+
},
|
|
2570
|
+
"evidence": [
|
|
2571
|
+
{
|
|
2572
|
+
"file": "java-legacy/src/com/acme/web/AdminUserController.java",
|
|
2573
|
+
"line": 14,
|
|
2574
|
+
"code": "public String delete(@PathVariable(\"id\") Long id) {",
|
|
2575
|
+
"why": "User ID from path parameter without ownership verification"
|
|
2576
|
+
},
|
|
2577
|
+
{
|
|
2578
|
+
"file": "java-legacy/src/com/acme/web/AdminUserController.java",
|
|
2579
|
+
"line": 15,
|
|
2580
|
+
"code": "userDao.deleteById(id);",
|
|
2581
|
+
"why": "Deletes user without checking if current admin owns this user"
|
|
2582
|
+
}
|
|
2583
|
+
],
|
|
2584
|
+
"attack_scenario": "Admin A sends POST /admin/users/{id}/delete with User B's ID and successfully deletes User B.",
|
|
2585
|
+
"impact": "Any logged-in admin can delete any other user, not just their own or assigned users.",
|
|
2586
|
+
"severity": "high",
|
|
2587
|
+
"confidence": "high",
|
|
2588
|
+
"cwe": [
|
|
2589
|
+
"CWE-284",
|
|
2590
|
+
"CWE-639"
|
|
2591
|
+
],
|
|
2592
|
+
"owasp": [
|
|
2593
|
+
"API5:2023"
|
|
2594
|
+
],
|
|
2595
|
+
"recommendation": "Add ownership verification before deletion",
|
|
2596
|
+
"status": "confirmed",
|
|
2597
|
+
"verification": {
|
|
2598
|
+
"verdict": "confirmed",
|
|
2599
|
+
"reason_code": "traced",
|
|
2600
|
+
"checked": [
|
|
2601
|
+
"java-legacy/src/com/acme/web/AdminUserController.java lines 13-17",
|
|
2602
|
+
"global filters in web.xml",
|
|
2603
|
+
"session user binding"
|
|
2604
|
+
],
|
|
2605
|
+
"control_found": null,
|
|
2606
|
+
"notes": "DAO deleteById() called with @PathVariable id without ownership verification. No compensating control found.",
|
|
2607
|
+
"suggested_subtype": null,
|
|
2608
|
+
"suggested_cwe": null
|
|
2609
|
+
},
|
|
2610
|
+
"consensus": {
|
|
2611
|
+
"score": 0.95,
|
|
2612
|
+
"agreement_level": "unanimous",
|
|
2613
|
+
"voted_models": [
|
|
2614
|
+
"qwen3.5:9b"
|
|
2615
|
+
],
|
|
2616
|
+
"rationale": "Direct DAO delete with user-controlled path parameter without ownership check."
|
|
2617
|
+
},
|
|
2618
|
+
"attack_vector": {
|
|
2619
|
+
"method": "POST",
|
|
2620
|
+
"path": "/admin/users/{id}/delete",
|
|
2621
|
+
"target_param": "id"
|
|
2622
|
+
}
|
|
2623
|
+
},
|
|
2624
|
+
{
|
|
2625
|
+
"finding_id": "BOLA-R-0024-1",
|
|
2626
|
+
"category": "bola",
|
|
2627
|
+
"subtype": "missing_role_check",
|
|
2628
|
+
"title": "Admin user role change endpoint missing ownership verification - any admin can change any user's role",
|
|
2629
|
+
"route_id": "R-0024",
|
|
2630
|
+
"endpoint": {
|
|
2631
|
+
"method": "POST",
|
|
2632
|
+
"path": "/admin/users/{id}/role"
|
|
2633
|
+
},
|
|
2634
|
+
"location": {
|
|
2635
|
+
"file": "java-legacy/src/com/acme/web/AdminUserController.java",
|
|
2636
|
+
"start_line": 19,
|
|
2637
|
+
"end_line": 24
|
|
2638
|
+
},
|
|
2639
|
+
"evidence": [
|
|
2640
|
+
{
|
|
2641
|
+
"file": "java-legacy/src/com/acme/web/AdminUserController.java",
|
|
2642
|
+
"line": 20,
|
|
2643
|
+
"code": "@PreAuthorize(\"hasRole('ADMIN')\")",
|
|
2644
|
+
"why": "Only checks admin role, not ownership of the user being modified"
|
|
2645
|
+
},
|
|
2646
|
+
{
|
|
2647
|
+
"file": "java-legacy/src/com/acme/web/AdminUserController.java",
|
|
2648
|
+
"line": 21,
|
|
2649
|
+
"code": "public String grant(@PathVariable(\"id\") Long id, @RequestParam(\"role\") String role) {",
|
|
2650
|
+
"why": "User ID from path parameter without ownership verification"
|
|
2651
|
+
},
|
|
2652
|
+
{
|
|
2653
|
+
"file": "java-legacy/src/com/acme/web/AdminUserController.java",
|
|
2654
|
+
"line": 22,
|
|
2655
|
+
"code": "userDao.updateRole(id, role);",
|
|
2656
|
+
"why": "Updates user role without checking if current admin owns this user"
|
|
2657
|
+
}
|
|
2658
|
+
],
|
|
2659
|
+
"attack_scenario": "Admin A sends POST /admin/users/{id}/role with User B's ID and a different role, successfully changing User B's role.",
|
|
2660
|
+
"impact": "Any logged-in admin can promote/demote any other user to any role.",
|
|
2661
|
+
"severity": "high",
|
|
2662
|
+
"confidence": "high",
|
|
2663
|
+
"cwe": [
|
|
2664
|
+
"CWE-284",
|
|
2665
|
+
"CWE-639"
|
|
2666
|
+
],
|
|
2667
|
+
"owasp": [
|
|
2668
|
+
"API5:2023"
|
|
2669
|
+
],
|
|
2670
|
+
"recommendation": "Add ownership verification before role change",
|
|
2671
|
+
"status": "confirmed",
|
|
2672
|
+
"verification": {
|
|
2673
|
+
"verdict": "confirmed",
|
|
2674
|
+
"reason_code": "traced",
|
|
2675
|
+
"checked": [
|
|
2676
|
+
"java-legacy/src/com/acme/web/AdminUserController.java lines 19-24",
|
|
2677
|
+
"global filters in web.xml",
|
|
2678
|
+
"session user binding"
|
|
2679
|
+
],
|
|
2680
|
+
"control_found": null,
|
|
2681
|
+
"notes": "DAO updateRole() called with @PathVariable id without ownership verification. Only role check present, not ownership.",
|
|
2682
|
+
"suggested_subtype": null,
|
|
2683
|
+
"suggested_cwe": null
|
|
2684
|
+
},
|
|
2685
|
+
"consensus": {
|
|
2686
|
+
"score": 0.95,
|
|
2687
|
+
"agreement_level": "unanimous",
|
|
2688
|
+
"voted_models": [
|
|
2689
|
+
"qwen3.5:9b"
|
|
2690
|
+
],
|
|
2691
|
+
"rationale": "Role-based authorization present but object-level ownership check missing."
|
|
2692
|
+
},
|
|
2693
|
+
"attack_vector": {
|
|
2694
|
+
"method": "POST",
|
|
2695
|
+
"path": "/admin/users/{id}/role",
|
|
2696
|
+
"target_param": "id"
|
|
2697
|
+
}
|
|
2698
|
+
},
|
|
2699
|
+
{
|
|
2700
|
+
"finding_id": "BOLA-R-0025-1",
|
|
2701
|
+
"category": "bola",
|
|
2702
|
+
"subtype": "missing_ownership_check",
|
|
2703
|
+
"title": "Invoice detail endpoint missing ownership verification - any user can view any invoice",
|
|
2704
|
+
"route_id": "R-0025",
|
|
2705
|
+
"endpoint": {
|
|
2706
|
+
"method": "GET",
|
|
2707
|
+
"path": "/invoices/{id}"
|
|
2708
|
+
},
|
|
2709
|
+
"location": {
|
|
2710
|
+
"file": "java-legacy/src/com/acme/web/InvoiceController.java",
|
|
2711
|
+
"start_line": 15,
|
|
2712
|
+
"end_line": 19
|
|
2713
|
+
},
|
|
2714
|
+
"evidence": [
|
|
2715
|
+
{
|
|
2716
|
+
"file": "java-legacy/src/com/acme/web/InvoiceController.java",
|
|
2717
|
+
"line": 16,
|
|
2718
|
+
"code": "public String show(@PathVariable(\"id\") Long id, Model model) {",
|
|
2719
|
+
"why": "Invoice ID from path parameter without ownership verification"
|
|
2720
|
+
},
|
|
2721
|
+
{
|
|
2722
|
+
"file": "java-legacy/src/com/acme/web/InvoiceController.java",
|
|
2723
|
+
"line": 17,
|
|
2724
|
+
"code": "model.addAttribute(\"invoice\", invoiceDao.findById(id));",
|
|
2725
|
+
"why": "Loads invoice without checking if current user owns this invoice"
|
|
2726
|
+
}
|
|
2727
|
+
],
|
|
2728
|
+
"attack_scenario": "User A sends GET /invoices/{id} with User B's invoice ID and successfully views User B's invoice.",
|
|
2729
|
+
"impact": "Users can view any other user's invoices (PII, payment info, etc.).",
|
|
2730
|
+
"severity": "high",
|
|
2731
|
+
"confidence": "high",
|
|
2732
|
+
"cwe": [
|
|
2733
|
+
"CWE-284",
|
|
2734
|
+
"CWE-639"
|
|
2735
|
+
],
|
|
2736
|
+
"owasp": [
|
|
2737
|
+
"API1:2023"
|
|
2738
|
+
],
|
|
2739
|
+
"recommendation": "Bind query to current session user",
|
|
2740
|
+
"status": "confirmed",
|
|
2741
|
+
"verification": {
|
|
2742
|
+
"verdict": "confirmed",
|
|
2743
|
+
"reason_code": "traced",
|
|
2744
|
+
"checked": [
|
|
2745
|
+
"java-legacy/src/com/acme/web/InvoiceController.java lines 15-19",
|
|
2746
|
+
"global filters in web.xml",
|
|
2747
|
+
"session user binding"
|
|
2748
|
+
],
|
|
2749
|
+
"control_found": null,
|
|
2750
|
+
"notes": "DAO findById() called with @PathVariable id without ownership verification. No compensating control found.",
|
|
2751
|
+
"suggested_subtype": null,
|
|
2752
|
+
"suggested_cwe": null
|
|
2753
|
+
},
|
|
2754
|
+
"consensus": {
|
|
2755
|
+
"score": 0.95,
|
|
2756
|
+
"agreement_level": "unanimous",
|
|
2757
|
+
"voted_models": [
|
|
2758
|
+
"qwen3.5:9b"
|
|
2759
|
+
],
|
|
2760
|
+
"rationale": "Direct DAO find with user-controlled path parameter without ownership check."
|
|
2761
|
+
},
|
|
2762
|
+
"attack_vector": {
|
|
2763
|
+
"method": "GET",
|
|
2764
|
+
"path": "/invoices/{id}",
|
|
2765
|
+
"target_param": "id"
|
|
2766
|
+
}
|
|
2767
|
+
},
|
|
2768
|
+
{
|
|
2769
|
+
"finding_id": "BOLA-R-0027-1",
|
|
2770
|
+
"category": "bola",
|
|
2771
|
+
"subtype": "missing_ownership_check",
|
|
2772
|
+
"title": "Invoice search endpoint missing ownership verification - any user can search for any customer's invoices",
|
|
2773
|
+
"route_id": "R-0027",
|
|
2774
|
+
"endpoint": {
|
|
2775
|
+
"method": "GET",
|
|
2776
|
+
"path": "/invoices/search"
|
|
2777
|
+
},
|
|
2778
|
+
"location": {
|
|
2779
|
+
"file": "java-legacy/src/com/acme/web/InvoiceController.java",
|
|
2780
|
+
"start_line": 27,
|
|
2781
|
+
"end_line": 31
|
|
2782
|
+
},
|
|
2783
|
+
"evidence": [
|
|
2784
|
+
{
|
|
2785
|
+
"file": "java-legacy/src/com/acme/web/InvoiceController.java",
|
|
2786
|
+
"line": 28,
|
|
2787
|
+
"code": "public String search(@RequestParam(\"customer\") String customer, Model model) {",
|
|
2788
|
+
"why": "Customer name from request parameter without ownership verification"
|
|
2789
|
+
},
|
|
2790
|
+
{
|
|
2791
|
+
"file": "java-legacy/src/com/acme/web/InvoiceController.java",
|
|
2792
|
+
"line": 29,
|
|
2793
|
+
"code": "model.addAttribute(\"list\", session.createQuery(\"from Invoice i where i.customer = '\" + customer + \"'\").list());",
|
|
2794
|
+
"why": "Searches invoices without checking if current user owns these invoices"
|
|
2795
|
+
}
|
|
2796
|
+
],
|
|
2797
|
+
"attack_scenario": "User A sends GET /invoices/search?customer=<any customer name> and successfully retrieves all invoices for that customer.",
|
|
2798
|
+
"impact": "Users can search for and view any other customer's invoices (PII, payment info, etc.).",
|
|
2799
|
+
"severity": "high",
|
|
2800
|
+
"confidence": "high",
|
|
2801
|
+
"cwe": [
|
|
2802
|
+
"CWE-284",
|
|
2803
|
+
"CWE-639"
|
|
2804
|
+
],
|
|
2805
|
+
"owasp": [
|
|
2806
|
+
"API1:2023"
|
|
2807
|
+
],
|
|
2808
|
+
"recommendation": "Bind query to current session user",
|
|
2809
|
+
"status": "unverified"
|
|
2810
|
+
},
|
|
2811
|
+
{
|
|
2812
|
+
"finding_id": "BOLA-R-0028-1",
|
|
2813
|
+
"category": "bola",
|
|
2814
|
+
"subtype": "missing_ownership_check",
|
|
2815
|
+
"title": "Order view endpoint missing ownership verification - any user can view any order",
|
|
2816
|
+
"route_id": "R-0028",
|
|
2817
|
+
"endpoint": {
|
|
2818
|
+
"method": "GET",
|
|
2819
|
+
"path": "/order/view"
|
|
2820
|
+
},
|
|
2821
|
+
"location": {
|
|
2822
|
+
"file": "java-legacy/src/com/acme/web/OrderServlet.java",
|
|
2823
|
+
"start_line": 8,
|
|
2824
|
+
"end_line": 21
|
|
2825
|
+
},
|
|
2826
|
+
"evidence": [
|
|
2827
|
+
{
|
|
2828
|
+
"file": "java-legacy/src/com/acme/web/OrderServlet.java",
|
|
2829
|
+
"line": 11,
|
|
2830
|
+
"code": "String id = request.getParameter(\"orderId\");",
|
|
2831
|
+
"why": "Order ID from request parameter without ownership verification"
|
|
2832
|
+
},
|
|
2833
|
+
{
|
|
2834
|
+
"file": "java-legacy/src/com/acme/web/OrderServlet.java",
|
|
2835
|
+
"line": 15,
|
|
2836
|
+
"code": "ResultSet rs = stmt.executeQuery(\"SELECT * FROM orders WHERE id = '\" + id + \"'\");",
|
|
2837
|
+
"why": "Loads order without checking if current user owns this order"
|
|
2838
|
+
},
|
|
2839
|
+
{
|
|
2840
|
+
"file": "java-legacy/src/com/acme/web/OrderServlet.java",
|
|
2841
|
+
"line": 17,
|
|
2842
|
+
"code": "sb.append(\" AND order_id = \").append(id);",
|
|
2843
|
+
"why": "Joins items table without ownership check"
|
|
2844
|
+
}
|
|
2845
|
+
],
|
|
2846
|
+
"attack_scenario": "User A sends GET /order/view?orderId=<any order id> and successfully views any order.",
|
|
2847
|
+
"impact": "Users can view any other user's orders (PII, shipping info, amounts, etc.).",
|
|
2848
|
+
"severity": "high",
|
|
2849
|
+
"confidence": "high",
|
|
2850
|
+
"cwe": [
|
|
2851
|
+
"CWE-284",
|
|
2852
|
+
"CWE-639"
|
|
2853
|
+
],
|
|
2854
|
+
"owasp": [
|
|
2855
|
+
"API1:2023"
|
|
2856
|
+
],
|
|
2857
|
+
"recommendation": "Bind query to current session user",
|
|
2858
|
+
"status": "unverified"
|
|
2859
|
+
},
|
|
2860
|
+
{
|
|
2861
|
+
"finding_id": "BOLA-R-0030-1",
|
|
2862
|
+
"category": "bola",
|
|
2863
|
+
"subtype": "missing_role_check",
|
|
2864
|
+
"title": "Admin user delete endpoint missing ownership verification - any logged-in user can delete any user",
|
|
2865
|
+
"route_id": "R-0030",
|
|
2866
|
+
"endpoint": {
|
|
2867
|
+
"method": "POST",
|
|
2868
|
+
"path": "/admin/users/:id/delete"
|
|
2869
|
+
},
|
|
2870
|
+
"location": {
|
|
2871
|
+
"file": "node-express/routes/admin.js",
|
|
2872
|
+
"start_line": 8,
|
|
2873
|
+
"end_line": 10
|
|
2874
|
+
},
|
|
2875
|
+
"evidence": [
|
|
2876
|
+
{
|
|
2877
|
+
"file": "node-express/routes/admin.js",
|
|
2878
|
+
"line": 5,
|
|
2879
|
+
"code": "function ensureLoggedIn(req, res, next) { if (!req.user) return res.redirect('/login'); next(); }",
|
|
2880
|
+
"why": "Only checks login status, not admin role or ownership"
|
|
2881
|
+
},
|
|
2882
|
+
{
|
|
2883
|
+
"file": "node-express/routes/admin.js",
|
|
2884
|
+
"line": 9,
|
|
2885
|
+
"code": "User.deleteOne({ _id: req.params.id }, function () { res.sendStatus(204); });",
|
|
2886
|
+
"why": "Deletes user without checking if current user owns this user"
|
|
2887
|
+
}
|
|
2888
|
+
],
|
|
2889
|
+
"attack_scenario": "Any logged-in user sends POST /admin/users/:id/delete with another user's ID and successfully deletes them.",
|
|
2890
|
+
"impact": "Any authenticated user can delete any other user account.",
|
|
2891
|
+
"severity": "high",
|
|
2892
|
+
"confidence": "high",
|
|
2893
|
+
"cwe": [
|
|
2894
|
+
"CWE-284",
|
|
2895
|
+
"CWE-639"
|
|
2896
|
+
],
|
|
2897
|
+
"owasp": [
|
|
2898
|
+
"API5:2023"
|
|
2899
|
+
],
|
|
2900
|
+
"recommendation": "Add admin role check and ownership verification",
|
|
2901
|
+
"status": "unverified"
|
|
2902
|
+
},
|
|
2903
|
+
{
|
|
2904
|
+
"finding_id": "BOLA-R-0031-1",
|
|
2905
|
+
"category": "bola",
|
|
2906
|
+
"subtype": "missing_role_check",
|
|
2907
|
+
"title": "Admin user ban endpoint missing ownership verification - any logged-in user can ban any user",
|
|
2908
|
+
"route_id": "R-0031",
|
|
2909
|
+
"endpoint": {
|
|
2910
|
+
"method": "POST",
|
|
2911
|
+
"path": "/admin/users/:id/ban"
|
|
2912
|
+
},
|
|
2913
|
+
"location": {
|
|
2914
|
+
"file": "node-express/routes/admin.js",
|
|
2915
|
+
"start_line": 12,
|
|
2916
|
+
"end_line": 14
|
|
2917
|
+
},
|
|
2918
|
+
"evidence": [
|
|
2919
|
+
{
|
|
2920
|
+
"file": "node-express/routes/admin.js",
|
|
2921
|
+
"line": 5,
|
|
2922
|
+
"code": "function ensureLoggedIn(req, res, next) { if (!req.user) return res.redirect('/login'); next(); }",
|
|
2923
|
+
"why": "Only checks login status, not admin role or ownership"
|
|
2924
|
+
},
|
|
2925
|
+
{
|
|
2926
|
+
"file": "node-express/routes/admin.js",
|
|
2927
|
+
"line": 13,
|
|
2928
|
+
"code": "User.updateOne({ _id: req.params.id }, { banned: true }, function () { res.sendStatus(204); });",
|
|
2929
|
+
"why": "Updates user without checking if current user owns this user"
|
|
2930
|
+
}
|
|
2931
|
+
],
|
|
2932
|
+
"attack_scenario": "Any logged-in user sends POST /admin/users/:id/ban with another user's ID and successfully bans them.",
|
|
2933
|
+
"impact": "Any authenticated user can ban any other user account.",
|
|
2934
|
+
"severity": "high",
|
|
2935
|
+
"confidence": "high",
|
|
2936
|
+
"cwe": [
|
|
2937
|
+
"CWE-284",
|
|
2938
|
+
"CWE-639"
|
|
2939
|
+
],
|
|
2940
|
+
"owasp": [
|
|
2941
|
+
"API5:2023"
|
|
2942
|
+
],
|
|
2943
|
+
"recommendation": "Add admin role check and ownership verification",
|
|
2944
|
+
"status": "unverified"
|
|
2945
|
+
},
|
|
2946
|
+
{
|
|
2947
|
+
"finding_id": "BOLA-R-0032-1",
|
|
2948
|
+
"category": "bola",
|
|
2949
|
+
"subtype": "missing_role_check",
|
|
2950
|
+
"title": "Reports export endpoint missing ownership verification - any logged-in user can export reports",
|
|
2951
|
+
"route_id": "R-0032",
|
|
2952
|
+
"endpoint": {
|
|
2953
|
+
"method": "GET",
|
|
2954
|
+
"path": "/reports/export"
|
|
2955
|
+
},
|
|
2956
|
+
"location": {
|
|
2957
|
+
"file": "node-express/routes/admin.js",
|
|
2958
|
+
"start_line": 16,
|
|
2959
|
+
"end_line": 18
|
|
2960
|
+
},
|
|
2961
|
+
"evidence": [
|
|
2962
|
+
{
|
|
2963
|
+
"file": "node-express/routes/admin.js",
|
|
2964
|
+
"line": 5,
|
|
2965
|
+
"code": "function ensureLoggedIn(req, res, next) { if (!req.user) return res.redirect('/login'); next(); }",
|
|
2966
|
+
"why": "Only checks login status, not admin role"
|
|
2967
|
+
},
|
|
2968
|
+
{
|
|
2969
|
+
"file": "node-express/routes/admin.js",
|
|
2970
|
+
"line": 17,
|
|
2971
|
+
"code": "if (req.query.isAdmin === 'true') { res.json([]); }",
|
|
2972
|
+
"why": "Trusts client input for isAdmin flag instead of checking server-side identity"
|
|
2973
|
+
}
|
|
2974
|
+
],
|
|
2975
|
+
"attack_scenario": "Any logged-in user sends GET /reports/export?isAdmin=true and receives empty response, but without this check they could export all data.",
|
|
2976
|
+
"impact": "Any authenticated user can access report export functionality by manipulating query parameter.",
|
|
2977
|
+
"severity": "high",
|
|
2978
|
+
"confidence": "high",
|
|
2979
|
+
"cwe": [
|
|
2980
|
+
"CWE-284",
|
|
2981
|
+
"CWE-639"
|
|
2982
|
+
],
|
|
2983
|
+
"owasp": [
|
|
2984
|
+
"API5:2023"
|
|
2985
|
+
],
|
|
2986
|
+
"recommendation": "Add admin role check instead of trusting client input",
|
|
2987
|
+
"status": "unverified"
|
|
2988
|
+
},
|
|
2989
|
+
{
|
|
2990
|
+
"finding_id": "BOLA-R-0033-1",
|
|
2991
|
+
"category": "bola",
|
|
2992
|
+
"subtype": "missing_ownership_check",
|
|
2993
|
+
"title": "Order detail readable by any logged-in user via id",
|
|
2994
|
+
"route_id": "R-0033",
|
|
2995
|
+
"endpoint": {
|
|
2996
|
+
"method": "GET",
|
|
2997
|
+
"path": "/orders/:id"
|
|
2998
|
+
},
|
|
2999
|
+
"location": {
|
|
3000
|
+
"file": "node-express/routes/orders.js",
|
|
3001
|
+
"start_line": 8,
|
|
3002
|
+
"end_line": 10
|
|
3003
|
+
},
|
|
3004
|
+
"evidence": [
|
|
3005
|
+
{
|
|
3006
|
+
"file": "node-express/routes/orders.js",
|
|
3007
|
+
"line": 8,
|
|
3008
|
+
"code": "router.get('/orders/:id', ensureLoggedIn, function (req, res) {",
|
|
3009
|
+
"why": "ID from path parameter"
|
|
3010
|
+
},
|
|
3011
|
+
{
|
|
3012
|
+
"file": "node-express/routes/orders.js",
|
|
3013
|
+
"line": 9,
|
|
3014
|
+
"code": "Order.findById(req.params.id, function (err, order) { res.json(order); });",
|
|
3015
|
+
"why": "No owner column in WHERE; no later comparison to session user"
|
|
3016
|
+
}
|
|
3017
|
+
],
|
|
3018
|
+
"attack_scenario": "User A sends GET /orders/:id?id=<order id of user B> and receives B's order.",
|
|
3019
|
+
"impact": "Read other customers' orders (name, address, amount).",
|
|
3020
|
+
"severity": "high",
|
|
3021
|
+
"confidence": "high",
|
|
3022
|
+
"cwe": [
|
|
3023
|
+
"CWE-284",
|
|
3024
|
+
"CWE-639"
|
|
3025
|
+
],
|
|
3026
|
+
"recommendation": "Bind query to current session user",
|
|
3027
|
+
"status": "unverified"
|
|
3028
|
+
},
|
|
3029
|
+
{
|
|
3030
|
+
"finding_id": "BOLA-R-0035-1",
|
|
3031
|
+
"category": "bola",
|
|
3032
|
+
"subtype": "missing_ownership_check",
|
|
3033
|
+
"title": "Reports endpoint may expose user-specific data to all logged-in users",
|
|
3034
|
+
"route_id": "R-0035",
|
|
3035
|
+
"endpoint": {
|
|
3036
|
+
"method": "GET",
|
|
3037
|
+
"path": "/reports"
|
|
3038
|
+
},
|
|
3039
|
+
"location": {
|
|
3040
|
+
"file": "node-express/routes/orders.js",
|
|
3041
|
+
"start_line": 16,
|
|
3042
|
+
"end_line": 18
|
|
3043
|
+
},
|
|
3044
|
+
"evidence": [
|
|
3045
|
+
{
|
|
3046
|
+
"file": "node-express/routes/orders.js",
|
|
3047
|
+
"line": 17,
|
|
3048
|
+
"code": "db.query(\"SELECT * FROM reports WHERE month = '\" + req.query.month + \"'\", function (e, rows) { res.json(rows); });",
|
|
3049
|
+
"why": "No user_id filter; query returns all matching reports without ownership check"
|
|
3050
|
+
}
|
|
3051
|
+
],
|
|
3052
|
+
"attack_scenario": "User A sends GET /reports?month=2024-01 and receives reports that may contain other users' data.",
|
|
3053
|
+
"impact": "Read other customers' report data if reports are per-user.",
|
|
3054
|
+
"severity": "high",
|
|
3055
|
+
"confidence": "medium",
|
|
3056
|
+
"cwe": [
|
|
3057
|
+
"CWE-284"
|
|
3058
|
+
],
|
|
3059
|
+
"recommendation": "Bind query to current session user",
|
|
3060
|
+
"status": "unverified"
|
|
3061
|
+
},
|
|
3062
|
+
{
|
|
3063
|
+
"finding_id": "BOLA-R-0037-1",
|
|
3064
|
+
"category": "bola",
|
|
3065
|
+
"subtype": "missing_ownership_check",
|
|
3066
|
+
"title": "Item search may return other users' items without ownership filter",
|
|
3067
|
+
"route_id": "R-0037",
|
|
3068
|
+
"endpoint": {
|
|
3069
|
+
"method": "GET",
|
|
3070
|
+
"path": "/search"
|
|
3071
|
+
},
|
|
3072
|
+
"location": {
|
|
3073
|
+
"file": "node-express/routes/orders.js",
|
|
3074
|
+
"start_line": 24,
|
|
3075
|
+
"end_line": 26
|
|
3076
|
+
},
|
|
3077
|
+
"evidence": [
|
|
3078
|
+
{
|
|
3079
|
+
"file": "node-express/routes/orders.js",
|
|
3080
|
+
"line": 25,
|
|
3081
|
+
"code": "db.collection('items').find({ $where: \"this.name == '\" + req.query.n + \"'\" }).toArray(function (e, r) { res.json(r); });",
|
|
3082
|
+
"why": "No user_id filter; query returns all matching items without ownership check"
|
|
3083
|
+
}
|
|
3084
|
+
],
|
|
3085
|
+
"attack_scenario": "User A sends GET /search?n=<item name> and receives items that belong to other users.",
|
|
3086
|
+
"impact": "Read other customers' item data if items are per-user.",
|
|
3087
|
+
"severity": "high",
|
|
3088
|
+
"confidence": "medium",
|
|
3089
|
+
"cwe": [
|
|
3090
|
+
"CWE-284"
|
|
3091
|
+
],
|
|
3092
|
+
"recommendation": "Bind query to current session user",
|
|
3093
|
+
"status": "unverified"
|
|
3094
|
+
},
|
|
3095
|
+
{
|
|
3096
|
+
"finding_id": "BOLA-R-0038-1",
|
|
3097
|
+
"category": "bola",
|
|
3098
|
+
"subtype": "missing_ownership_check",
|
|
3099
|
+
"title": "Stats endpoint uses user-controlled shop_id without ownership verification",
|
|
3100
|
+
"route_id": "R-0038",
|
|
3101
|
+
"endpoint": {
|
|
3102
|
+
"method": "GET",
|
|
3103
|
+
"path": "/stats"
|
|
3104
|
+
},
|
|
3105
|
+
"location": {
|
|
3106
|
+
"file": "node-express/routes/orders.js",
|
|
3107
|
+
"start_line": 28,
|
|
3108
|
+
"end_line": 30
|
|
3109
|
+
},
|
|
3110
|
+
"evidence": [
|
|
3111
|
+
{
|
|
3112
|
+
"file": "node-express/routes/orders.js",
|
|
3113
|
+
"line": 29,
|
|
3114
|
+
"code": "db.query(`SELECT COUNT(*) AS n FROM orders WHERE shop_id = ${req.query.shop}`, function (e, r) { res.json(r); });",
|
|
3115
|
+
"why": "shop_id from query string used directly without ownership check; SQL injection risk"
|
|
3116
|
+
}
|
|
3117
|
+
],
|
|
3118
|
+
"attack_scenario": "User A sends GET /stats?shop=<other_shop_id> and receives stats for another tenant's orders.",
|
|
3119
|
+
"impact": "Read other tenants' order statistics; also SQL injection vulnerability.",
|
|
3120
|
+
"severity": "high",
|
|
3121
|
+
"confidence": "high",
|
|
3122
|
+
"cwe": [
|
|
3123
|
+
"CWE-284",
|
|
3124
|
+
"CWE-89"
|
|
3125
|
+
],
|
|
3126
|
+
"recommendation": "Bind query to current session user and use parameterized queries",
|
|
3127
|
+
"status": "unverified"
|
|
3128
|
+
},
|
|
3129
|
+
{
|
|
3130
|
+
"finding_id": "BOLA-R-0043-1",
|
|
3131
|
+
"category": "bola",
|
|
3132
|
+
"subtype": "missing_ownership_check",
|
|
3133
|
+
"title": "Perl CGI order endpoint missing ownership check in SQL query",
|
|
3134
|
+
"route_id": "R-0043",
|
|
3135
|
+
"endpoint": {
|
|
3136
|
+
"method": "ANY",
|
|
3137
|
+
"path": "/perl-cgi/cgi-bin/order.cgi"
|
|
3138
|
+
},
|
|
3139
|
+
"location": {
|
|
3140
|
+
"file": "perl-cgi/cgi-bin/order.cgi",
|
|
3141
|
+
"start_line": 13,
|
|
3142
|
+
"end_line": 15
|
|
3143
|
+
},
|
|
3144
|
+
"evidence": [
|
|
3145
|
+
{
|
|
3146
|
+
"file": "perl-cgi/cgi-bin/order.cgi",
|
|
3147
|
+
"line": 11,
|
|
3148
|
+
"code": "my $id = $q->param('id');",
|
|
3149
|
+
"why": "ID from request parameter"
|
|
3150
|
+
},
|
|
3151
|
+
{
|
|
3152
|
+
"file": "perl-cgi/cgi-bin/order.cgi",
|
|
3153
|
+
"line": 13,
|
|
3154
|
+
"code": "my $sth = $dbh->prepare(\"SELECT * FROM orders WHERE id = $id\");",
|
|
3155
|
+
"why": "No user_id filter; query returns all matching orders without ownership check"
|
|
3156
|
+
}
|
|
3157
|
+
],
|
|
3158
|
+
"attack_scenario": "User A sends GET /perl-cgi/cgi-bin/order.cgi?id=<order id of user B> and receives B's order.",
|
|
3159
|
+
"impact": "Read other customers' orders (name, address, amount).",
|
|
3160
|
+
"severity": "high",
|
|
3161
|
+
"confidence": "high",
|
|
3162
|
+
"cwe": [
|
|
3163
|
+
"CWE-284",
|
|
3164
|
+
"CWE-639"
|
|
3165
|
+
],
|
|
3166
|
+
"recommendation": "Bind query to current session user",
|
|
3167
|
+
"status": "unverified"
|
|
3168
|
+
},
|
|
3169
|
+
{
|
|
3170
|
+
"finding_id": "BOLA-R-0044-1",
|
|
3171
|
+
"category": "bola",
|
|
3172
|
+
"subtype": "missing_ownership_check",
|
|
3173
|
+
"title": "Invoice view missing ownership check in SQL query",
|
|
3174
|
+
"route_id": "R-0044",
|
|
3175
|
+
"endpoint": {
|
|
3176
|
+
"method": "ANY",
|
|
3177
|
+
"path": "/invoice/view/{id}"
|
|
3178
|
+
},
|
|
3179
|
+
"location": {
|
|
3180
|
+
"file": "php-codeigniter/application/controllers/Invoice.php",
|
|
3181
|
+
"start_line": 9,
|
|
3182
|
+
"end_line": 12
|
|
3183
|
+
},
|
|
3184
|
+
"evidence": [
|
|
3185
|
+
{
|
|
3186
|
+
"file": "php-codeigniter/application/controllers/Invoice.php",
|
|
3187
|
+
"line": 9,
|
|
3188
|
+
"code": "public function view($id)",
|
|
3189
|
+
"why": "ID from path parameter"
|
|
3190
|
+
},
|
|
3191
|
+
{
|
|
3192
|
+
"file": "php-codeigniter/application/controllers/Invoice.php",
|
|
3193
|
+
"line": 10,
|
|
3194
|
+
"code": "$q = $this->db->query(\"SELECT * FROM invoices WHERE id = $id\");",
|
|
3195
|
+
"why": "No user_id filter; query returns all matching invoices without ownership check"
|
|
3196
|
+
}
|
|
3197
|
+
],
|
|
3198
|
+
"attack_scenario": "User A sends GET /invoice/view/{id}?id=<invoice id of user B> and receives B's invoice.",
|
|
3199
|
+
"impact": "Read other customers' invoices (amount, customer info).",
|
|
3200
|
+
"severity": "high",
|
|
3201
|
+
"confidence": "high",
|
|
3202
|
+
"cwe": [
|
|
3203
|
+
"CWE-284",
|
|
3204
|
+
"CWE-639"
|
|
3205
|
+
],
|
|
3206
|
+
"recommendation": "Bind query to current session user",
|
|
3207
|
+
"status": "unverified"
|
|
3208
|
+
},
|
|
3209
|
+
{
|
|
3210
|
+
"finding_id": "BOLA-R-0046-1",
|
|
3211
|
+
"category": "bola",
|
|
3212
|
+
"subtype": "missing_ownership_check",
|
|
3213
|
+
"title": "Invoice update missing ownership check in SQL query",
|
|
3214
|
+
"route_id": "R-0046",
|
|
3215
|
+
"endpoint": {
|
|
3216
|
+
"method": "ANY",
|
|
3217
|
+
"path": "/invoice/update"
|
|
3218
|
+
},
|
|
3219
|
+
"location": {
|
|
3220
|
+
"file": "php-codeigniter/application/controllers/Invoice.php",
|
|
3221
|
+
"start_line": 20,
|
|
3222
|
+
"end_line": 24
|
|
3223
|
+
},
|
|
3224
|
+
"evidence": [
|
|
3225
|
+
{
|
|
3226
|
+
"file": "php-codeigniter/application/controllers/Invoice.php",
|
|
3227
|
+
"line": 21,
|
|
3228
|
+
"code": "$id = $this->input->post('invoice_id');",
|
|
3229
|
+
"why": "ID from POST parameter"
|
|
3230
|
+
},
|
|
3231
|
+
{
|
|
3232
|
+
"file": "php-codeigniter/application/controllers/Invoice.php",
|
|
3233
|
+
"line": 23,
|
|
3234
|
+
"code": "$this->db->query(\"UPDATE invoices SET note = ? WHERE id = ?\", array($note, $id));",
|
|
3235
|
+
"why": "No user_id filter; query updates any matching invoice without ownership check"
|
|
3236
|
+
}
|
|
3237
|
+
],
|
|
3238
|
+
"attack_scenario": "User A sends POST /invoice/update with invoice_id=<invoice id of user B> and modifies B's invoice.",
|
|
3239
|
+
"impact": "Modify other customers' invoices (notes, amounts).",
|
|
3240
|
+
"severity": "high",
|
|
3241
|
+
"confidence": "high",
|
|
3242
|
+
"cwe": [
|
|
3243
|
+
"CWE-284",
|
|
3244
|
+
"CWE-639"
|
|
3245
|
+
],
|
|
3246
|
+
"recommendation": "Bind query to current session user",
|
|
3247
|
+
"status": "unverified"
|
|
3248
|
+
},
|
|
3249
|
+
{
|
|
3250
|
+
"finding_id": "BOLA-R-0057-1",
|
|
3251
|
+
"category": "bola",
|
|
3252
|
+
"subtype": "missing_ownership_check",
|
|
3253
|
+
"title": "Admin user view endpoint allows viewing any user's record via uid parameter",
|
|
3254
|
+
"route_id": "R-0057",
|
|
3255
|
+
"endpoint": {
|
|
3256
|
+
"method": "GET|POST",
|
|
3257
|
+
"path": "/php-plain/admin.php?action=view"
|
|
3258
|
+
},
|
|
3259
|
+
"location": {
|
|
3260
|
+
"file": "php-plain/admin.php",
|
|
3261
|
+
"start_line": 6,
|
|
3262
|
+
"end_line": 9
|
|
3263
|
+
},
|
|
3264
|
+
"evidence": [
|
|
3265
|
+
{
|
|
3266
|
+
"file": "php-plain/admin.php",
|
|
3267
|
+
"start_line": 7,
|
|
3268
|
+
"code": "$uid = (int)$_GET['uid'];",
|
|
3269
|
+
"why": "ID from query string",
|
|
3270
|
+
"line": 7
|
|
3271
|
+
},
|
|
3272
|
+
{
|
|
3273
|
+
"file": "php-plain/admin.php",
|
|
3274
|
+
"start_line": 8,
|
|
3275
|
+
"code": "mysql_query(\"SELECT * FROM users WHERE id = \" . $uid);",
|
|
3276
|
+
"why": "No owner column in WHERE; no later comparison to session user",
|
|
3277
|
+
"line": 8
|
|
3278
|
+
}
|
|
3279
|
+
],
|
|
3280
|
+
"attack_scenario": "User A sends GET /php-plain/admin.php?action=view&uid=<user id of user B> and receives B's user record.",
|
|
3281
|
+
"impact": "Read other users' personal data (name, email, role).",
|
|
3282
|
+
"severity": "high",
|
|
3283
|
+
"confidence": "high",
|
|
3284
|
+
"cwe": [
|
|
3285
|
+
"CWE-284",
|
|
3286
|
+
"CWE-639"
|
|
3287
|
+
],
|
|
3288
|
+
"recommendation": "Bind query to current session user",
|
|
3289
|
+
"status": "unverified"
|
|
3290
|
+
},
|
|
3291
|
+
{
|
|
3292
|
+
"finding_id": "BOLA-R-0058-1",
|
|
3293
|
+
"category": "bola",
|
|
3294
|
+
"subtype": "missing_ownership_check",
|
|
3295
|
+
"title": "Admin user delete endpoint allows deleting any user's record via id parameter",
|
|
3296
|
+
"route_id": "R-0058",
|
|
3297
|
+
"endpoint": {
|
|
3298
|
+
"method": "GET|POST",
|
|
3299
|
+
"path": "/php-plain/admin.php?action=delete"
|
|
3300
|
+
},
|
|
3301
|
+
"location": {
|
|
3302
|
+
"file": "php-plain/admin.php",
|
|
3303
|
+
"start_line": 10,
|
|
3304
|
+
"end_line": 12
|
|
3305
|
+
},
|
|
3306
|
+
"evidence": [
|
|
3307
|
+
{
|
|
3308
|
+
"file": "php-plain/admin.php",
|
|
3309
|
+
"start_line": 11,
|
|
3310
|
+
"code": "$id = $_POST['id'];",
|
|
3311
|
+
"why": "ID from POST body",
|
|
3312
|
+
"line": 11
|
|
3313
|
+
},
|
|
3314
|
+
{
|
|
3315
|
+
"file": "php-plain/admin.php",
|
|
3316
|
+
"start_line": 12,
|
|
3317
|
+
"code": "mysql_query(\"DELETE FROM users WHERE id = $id\");",
|
|
3318
|
+
"why": "No owner column in WHERE; no later comparison to session user",
|
|
3319
|
+
"line": 12
|
|
3320
|
+
}
|
|
3321
|
+
],
|
|
3322
|
+
"attack_scenario": "User A sends POST /php-plain/admin.php?action=delete&id=<user id of user B> and deletes B's account.",
|
|
3323
|
+
"impact": "Delete other users' accounts (account takeover, data loss).",
|
|
3324
|
+
"severity": "high",
|
|
3325
|
+
"confidence": "high",
|
|
3326
|
+
"cwe": [
|
|
3327
|
+
"CWE-284",
|
|
3328
|
+
"CWE-639"
|
|
3329
|
+
],
|
|
3330
|
+
"recommendation": "Bind query to current session user",
|
|
3331
|
+
"status": "unverified"
|
|
3332
|
+
},
|
|
3333
|
+
{
|
|
3334
|
+
"finding_id": "BOLA-R-0059-1",
|
|
3335
|
+
"category": "bola",
|
|
3336
|
+
"subtype": "missing_ownership_check",
|
|
3337
|
+
"title": "User management delete endpoint allows deleting any user's record via id parameter",
|
|
3338
|
+
"route_id": "R-0059",
|
|
3339
|
+
"endpoint": {
|
|
3340
|
+
"method": "POST",
|
|
3341
|
+
"path": "/php-plain/manage_users.php"
|
|
3342
|
+
},
|
|
3343
|
+
"location": {
|
|
3344
|
+
"file": "php-plain/manage_users.php",
|
|
3345
|
+
"start_line": 6,
|
|
3346
|
+
"end_line": 7
|
|
3347
|
+
},
|
|
3348
|
+
"evidence": [
|
|
3349
|
+
{
|
|
3350
|
+
"file": "php-plain/manage_users.php",
|
|
3351
|
+
"start_line": 6,
|
|
3352
|
+
"code": "$stmt = $db->prepare(\"DELETE FROM users WHERE id = ?\");",
|
|
3353
|
+
"why": "No owner column in WHERE; no later comparison to session user",
|
|
3354
|
+
"line": 6
|
|
3355
|
+
},
|
|
3356
|
+
{
|
|
3357
|
+
"file": "php-plain/manage_users.php",
|
|
3358
|
+
"start_line": 7,
|
|
3359
|
+
"code": "$stmt->execute(array((int)$_POST['id']));",
|
|
3360
|
+
"why": "ID from POST body used directly",
|
|
3361
|
+
"line": 7
|
|
3362
|
+
}
|
|
3363
|
+
],
|
|
3364
|
+
"attack_scenario": "User A sends POST /php-plain/manage_users.php with id=<user id of user B> and deletes B's account.",
|
|
3365
|
+
"impact": "Delete other users' accounts (account takeover, data loss).",
|
|
3366
|
+
"severity": "high",
|
|
3367
|
+
"confidence": "high",
|
|
3368
|
+
"cwe": [
|
|
3369
|
+
"CWE-284",
|
|
3370
|
+
"CWE-639"
|
|
3371
|
+
],
|
|
3372
|
+
"recommendation": "Bind query to current session user",
|
|
3373
|
+
"status": "unverified"
|
|
3374
|
+
},
|
|
3375
|
+
{
|
|
3376
|
+
"finding_id": "BOLA-R-0061-1",
|
|
3377
|
+
"category": "bola",
|
|
3378
|
+
"subtype": "missing_ownership_check",
|
|
3379
|
+
"title": "Order detail endpoint allows viewing any order via id parameter",
|
|
3380
|
+
"route_id": "R-0061",
|
|
3381
|
+
"endpoint": {
|
|
3382
|
+
"method": "GET",
|
|
3383
|
+
"path": "/php-plain/order_detail.php"
|
|
3384
|
+
},
|
|
3385
|
+
"location": {
|
|
3386
|
+
"file": "php-plain/order_detail.php",
|
|
3387
|
+
"start_line": 5,
|
|
3388
|
+
"end_line": 8
|
|
3389
|
+
},
|
|
3390
|
+
"evidence": [
|
|
3391
|
+
{
|
|
3392
|
+
"file": "php-plain/order_detail.php",
|
|
3393
|
+
"start_line": 5,
|
|
3394
|
+
"code": "$id = $_GET['id'];",
|
|
3395
|
+
"why": "ID from query string",
|
|
3396
|
+
"line": 5
|
|
3397
|
+
},
|
|
3398
|
+
{
|
|
3399
|
+
"file": "php-plain/order_detail.php",
|
|
3400
|
+
"start_line": 6,
|
|
3401
|
+
"code": "mysql_query(\"SELECT * FROM orders WHERE id = \" . $id);",
|
|
3402
|
+
"why": "No owner column in WHERE; no later comparison to session user",
|
|
3403
|
+
"line": 6
|
|
3404
|
+
}
|
|
3405
|
+
],
|
|
3406
|
+
"attack_scenario": "User A sends GET /php-plain/order_detail.php?id=<order id of user B> and receives B's order details.",
|
|
3407
|
+
"impact": "Read other customers' orders (name, address, amount).",
|
|
3408
|
+
"severity": "high",
|
|
3409
|
+
"confidence": "high",
|
|
3410
|
+
"cwe": [
|
|
3411
|
+
"CWE-284",
|
|
3412
|
+
"CWE-639"
|
|
3413
|
+
],
|
|
3414
|
+
"recommendation": "Bind query to current session user",
|
|
3415
|
+
"status": "unverified"
|
|
3416
|
+
},
|
|
3417
|
+
{
|
|
3418
|
+
"finding_id": "BOLA-R-0066-1",
|
|
3419
|
+
"category": "bola",
|
|
3420
|
+
"subtype": "missing_ownership_check",
|
|
3421
|
+
"title": "Admin delete user endpoint allows deleting any user's record via id parameter",
|
|
3422
|
+
"route_id": "R-0066",
|
|
3423
|
+
"endpoint": {
|
|
3424
|
+
"method": "POST",
|
|
3425
|
+
"path": "/php-plain/admin/delete_user.php"
|
|
3426
|
+
},
|
|
3427
|
+
"location": {
|
|
3428
|
+
"file": "php-plain/admin/delete_user.php",
|
|
3429
|
+
"start_line": 5,
|
|
3430
|
+
"end_line": 7
|
|
3431
|
+
},
|
|
3432
|
+
"evidence": [
|
|
3433
|
+
{
|
|
3434
|
+
"file": "php-plain/admin/delete_user.php",
|
|
3435
|
+
"start_line": 5,
|
|
3436
|
+
"code": "$id = (int)$_POST['id'];",
|
|
3437
|
+
"why": "ID from POST body",
|
|
3438
|
+
"line": 5
|
|
3439
|
+
},
|
|
3440
|
+
{
|
|
3441
|
+
"file": "php-plain/admin/delete_user.php",
|
|
3442
|
+
"start_line": 6,
|
|
3443
|
+
"code": "$stmt = $db->prepare(\"DELETE FROM users WHERE id = ?\");",
|
|
3444
|
+
"why": "No owner column in WHERE; no later comparison to session user",
|
|
3445
|
+
"line": 6
|
|
3446
|
+
}
|
|
3447
|
+
],
|
|
3448
|
+
"attack_scenario": "User A sends POST /php-plain/admin/delete_user.php with id=<user id of user B> and deletes B's account.",
|
|
3449
|
+
"impact": "Delete other users' accounts (account takeover, data loss).",
|
|
3450
|
+
"severity": "high",
|
|
3451
|
+
"confidence": "high",
|
|
3452
|
+
"cwe": [
|
|
3453
|
+
"CWE-284",
|
|
3454
|
+
"CWE-639"
|
|
3455
|
+
],
|
|
3456
|
+
"recommendation": "Bind query to current session user",
|
|
3457
|
+
"status": "unverified"
|
|
3458
|
+
},
|
|
3459
|
+
{
|
|
3460
|
+
"finding_id": "BOLA-R-0067-1",
|
|
3461
|
+
"category": "bola",
|
|
3462
|
+
"subtype": "missing_ownership_check",
|
|
3463
|
+
"title": "User promotion endpoint allows promoting any user's record via uid parameter",
|
|
3464
|
+
"route_id": "R-0067",
|
|
3465
|
+
"endpoint": {
|
|
3466
|
+
"method": "POST",
|
|
3467
|
+
"path": "/php-plain/admin/promote.php"
|
|
3468
|
+
},
|
|
3469
|
+
"location": {
|
|
3470
|
+
"file": "php-plain/admin/promote.php",
|
|
3471
|
+
"start_line": 5,
|
|
3472
|
+
"end_line": 7
|
|
3473
|
+
},
|
|
3474
|
+
"evidence": [
|
|
3475
|
+
{
|
|
3476
|
+
"file": "php-plain/admin/promote.php",
|
|
3477
|
+
"start_line": 6,
|
|
3478
|
+
"code": "$stmt = $db->prepare(\"UPDATE users SET role = 'admin' WHERE id = ?\");",
|
|
3479
|
+
"why": "No owner column in WHERE; no later comparison to session user",
|
|
3480
|
+
"line": 6
|
|
3481
|
+
},
|
|
3482
|
+
{
|
|
3483
|
+
"file": "php-plain/admin/promote.php",
|
|
3484
|
+
"start_line": 7,
|
|
3485
|
+
"code": "$stmt->execute(array((int)$_POST['uid']));",
|
|
3486
|
+
"why": "ID from POST body used directly",
|
|
3487
|
+
"line": 7
|
|
3488
|
+
}
|
|
3489
|
+
],
|
|
3490
|
+
"attack_scenario": "User A sends POST /php-plain/admin/promote.php with uid=<user id of user B> and promotes B to admin.",
|
|
3491
|
+
"impact": "Elevate privileges for other users (privilege escalation).",
|
|
3492
|
+
"severity": "high",
|
|
3493
|
+
"confidence": "high",
|
|
3494
|
+
"cwe": [
|
|
3495
|
+
"CWE-284",
|
|
3496
|
+
"CWE-639"
|
|
3497
|
+
],
|
|
3498
|
+
"recommendation": "Bind query to current session user",
|
|
3499
|
+
"status": "unverified"
|
|
3500
|
+
},
|
|
3501
|
+
{
|
|
3502
|
+
"finding_id": "BOLA-R-0069-1",
|
|
3503
|
+
"category": "bola",
|
|
3504
|
+
"subtype": "missing_ownership_check",
|
|
3505
|
+
"title": "Order endpoint allows viewing any order via order_id parameter",
|
|
3506
|
+
"route_id": "R-0069",
|
|
3507
|
+
"endpoint": {
|
|
3508
|
+
"method": "GET",
|
|
3509
|
+
"path": "/orders/<int:order_id>"
|
|
3510
|
+
},
|
|
3511
|
+
"location": {
|
|
3512
|
+
"file": "python-flask/app.py",
|
|
3513
|
+
"start_line": 11,
|
|
3514
|
+
"end_line": 13
|
|
3515
|
+
},
|
|
3516
|
+
"evidence": [
|
|
3517
|
+
{
|
|
3518
|
+
"file": "python-flask/app.py",
|
|
3519
|
+
"start_line": 9,
|
|
3520
|
+
"code": "@login_required",
|
|
3521
|
+
"why": "Only verifies login, not ownership",
|
|
3522
|
+
"line": 9
|
|
3523
|
+
},
|
|
3524
|
+
{
|
|
3525
|
+
"file": "python-flask/app.py",
|
|
3526
|
+
"start_line": 12,
|
|
3527
|
+
"code": "cur.execute(\"SELECT * FROM orders WHERE id = %s\" % order_id)",
|
|
3528
|
+
"why": "No user_id check in query; no later comparison to current_user.id",
|
|
3529
|
+
"line": 12
|
|
3530
|
+
}
|
|
3531
|
+
],
|
|
3532
|
+
"attack_scenario": "User A sends GET /orders/<int:order_id> with order_id=<order id of user B> and receives B's order details.",
|
|
3533
|
+
"impact": "Read other customers' orders (name, address, amount).",
|
|
3534
|
+
"severity": "high",
|
|
3535
|
+
"confidence": "high",
|
|
3536
|
+
"cwe": [
|
|
3537
|
+
"CWE-284",
|
|
3538
|
+
"CWE-639"
|
|
3539
|
+
],
|
|
3540
|
+
"recommendation": "Bind query to current session user",
|
|
3541
|
+
"status": "unverified"
|
|
3542
|
+
}
|
|
3543
|
+
]
|
|
3544
|
+
}
|