the_open_door
café
ESEN
← Volver al índice

GraphQL Abuse

ATT&CKT1190

GraphQL es un lenguaje de consulta para APIs: en vez de endpoints fijos, expone un único endpoint (/graphql) donde el cliente pide exactamente los campos que quiere. Esa flexibilidad, mal protegida, abre tres vías de abuso: introspection (descubrir todo el esquema), IDOR (leer objetos ajenos cambiando un id) y mutations (modificar datos sin autorización).

La introspection es el equivalente a volcar el information_schema de una base de datos: una consulta estándar devuelve todos los tipos, campos y relaciones del esquema. Si el servidor la tiene habilitada (lo normal fuera de producción), revela la superficie completa de la API:

{ __schema { types { name fields { name type { name kind } } } } }

Con el esquema en la mano, el IDOR es inmediato: si una query acepta un id, se itera para leer datos de otros usuarios que la autorización debería bloquear. Y las mutations permiten escribir: crear, actualizar o borrar registros ajenos si el resolver no valida al dueño.

query { user(id: 2) { username email token } }          # IDOR: datos de otro usuario
mutation { updatePost(id: 5, body: "pwned") { id body } } # escribir sobre un objeto ajeno

Herramientas como GraphQL Voyager pintan el esquema devuelto por la introspection para entenderlo de un vistazo antes de construir las consultas de ataque.

# lanzar la introspection por curl (si el IDE GraphiQL no esta expuesto)
curl -s http://<ip>/graphql -H 'Content-Type: application/json' \
  -d '{"query":"{__schema{types{name fields{name}}}}"}' | jq .
# clairvoyance reconstruye el esquema aunque la introspection este desactivada
python3 -m clairvoyance -o schema.json http://<ip>/graphql

Batching por alias. Un mismo documento puede repetir un campo con alias distintos y ejecutarlos en una sola petición: sirve para fuerza bruta (probar muchas contraseñas/OTP saltándose el rate limit, que suele contar peticiones HTTP, no operaciones):

mutation {
  a: login(user:"admin", pass:"1234"){ token }
  b: login(user:"admin", pass:"12345"){ token }
  c: login(user:"admin", pass:"password"){ token }
}

Requisitos. El endpoint debe (1) tener la introspection habilitada (o el esquema conocido por otra vía) y (2) delegar la autorización en el cliente, es decir, no comprobar en cada resolver que el objeto pertenece al usuario autenticado. Sin ese fallo de autorización por objeto, conocer el esquema no basta para leer o escribir datos ajenos.

Cómo detectarlo. En el log de la API la firma es una IntrospectionQuery (__schema, __type) desde un cliente que no es la propia aplicación, seguida de consultas que iteran el mismo campo id con valores crecientes (el barrido IDOR) o mutations desde cuentas sin privilegio sobre ese objeto. Un pico de consultas a /graphql con __schema es señal directa de reconocimiento.

Defensa. Deshabilitar la introspection en producción, aplicar autorización a nivel de campo y de objeto en cada resolver (no confiar en que el cliente solo pide lo suyo), limitar la profundidad y complejidad de las consultas para evitar DoS, y no exponer campos sensibles (tokens, hashes) en el esquema. Se solapa con el IDOR clásico y con la fuga de información por API.