Curiosidades De Hackers
OWASP TOP 10

Inyecciones GraphQL: Explicación y PoC


Las inyecciones GraphQL son vulnerabilidades de seguridad que ocurren cuando un atacante manipula consultas o mutaciones de GraphQL para realizar acciones no autorizadas. GraphQL es un lenguaje de consulta para APIs que permite a los clientes pedir exactamente los datos que necesitan de manera flexible. Esta flexibilidad, sin embargo, puede ser explotada si no se implementan las medidas de seguridad adecuadas.

Funcionamiento de las inyecciones GraphQL

Las inyecciones GraphQL funcionan de manera similar a las inyecciones SQL. Un atacante puede insertar código malicioso en una consulta o mutación para alterar su comportamiento. Esto puede llevar a la exposición de datos sensibles, modificación de datos o ejecución de comandos no autorizados en el servidor.

Ejemplo básico de una inyección GraphQL

Supongamos que tenemos una consulta GraphQL que obtiene información de un usuario basado en un ID:

query getUser($id: ID!) {
  user(id: $id) {
    id
    name
    email
  }
}

Si la validación de entrada no es adecuada, un atacante podría inyectar un valor malicioso en el ID:

 

{

 «id»: «1 OR 1=1» 
}

Esto podría alterar la consulta en el backend para que devuelva todos los usuarios en lugar de uno solo, similar a cómo funcionan las inyecciones SQL.

GraphQL permite a los clientes especificar exactamente qué datos necesitan a través de consultas y mutaciones. Esta capacidad de personalizar las consultas es poderosa, pero también puede ser peligrosa si no se manejan adecuadamente las entradas del usuario.

Ejemplo de consulta GraphQL estándar:

 
query getUser($id: ID!) {
  user(id: $id) {
    id
    name
    email
  }
}

En esta consulta, el cliente solicita los campos id, name y email del usuario con el id especificado.

Tipos de Inyección en GraphQL

 

   • Inyección de argumentos: Si los argumentos no se validan correctamente, un atacante puede manipular los parámetros para alterar la consulta.

Consulta legítima:


query {
  user(id: «1») {
    id
    name
  }
}

Consulta manipulada:

 
query {
  user(id: «1 OR 1=1») {
    id
    name
  }
}

   • Inyección de campos: Los atacantes pueden intentar acceder a campos no autorizados al manipular los campos solicitados.

Consulta legítima:
query {
  user(id: «1») {
    id
    name
  }
}

Consulta manipulada:

query {
  user(id: «1») {
    id
    name
    password
  }
}

Si el backend no restringe el acceso a ciertos campos, el atacante podría obtener datos sensibles como contraseñas.

   • Manipulación de mutaciones: Los atacantes pueden alterar mutaciones para cambiar datos en formas no deseadas.

Mutación legítima:

mutation {
  updateUser(id: «1», input: { name: «John», role: «user» }) {
    id
    name
    role
  }
}

Mutación manipulada:

mutation {
  updateUser(id: «1», input: { name: «attacker», role: «admin» }) {
    id
    name
    role
  }
}

Si no se validan adecuadamente los permisos, un atacante podría cambiar su rol a «admin».

Iniciemos sesión con: admin/admin

Si fuzzeamos nos encontramos con el directorio admin, en el cual podemos ver el estado del servidor y consultar detalles de los usuarios

    Si capturamos con burpsuite la consulta sobre el estado del servidor vemos que esta levantado, en esta caso es en el puerto 3000
    Nuestra entrada pasa por el comando exec() del sistema, el cual hace mas difícil que veamos el output, para asegurarnos haremos un Blind OS Command Injection, colándole el comando de «espera de 5seg». Por lo que nos a funcionando
    Pasemos al lado de consultar detalles sobre los usuarios;
Nuevamente lo capturamos con Burpsuite
    Le inyectaremos un payload típico de inyecciones SQL, en este caso este paylaod está diseñado para manipular una consulta SQL existente al cerrar la consulta original con una comilla simple, seguida de la palabra clave UNION, que permite combinar los resultados de la consulta original con otra consulta adicional. En este caso, la consulta adicional selecciona los campos id y username de la tabla users. El uso del doble guion (–) al final de la cadena permite comentar el resto de la consulta original, asegurando que la consulta maliciosa se ejecute correctamente sin errores. Este tipo de inyección SQL se aprovecha de vulnerabilidades en aplicaciones web para obtener información adicional de la base de datos o realizar acciones no autorizadas.

    Vamos a intentar buscar información de otros administradores mediante este codigo GraphQL, intentamos realizar una inyección SQL utilizando el parámetro username para la consulta User. El valor «‘ UNION SELECT id, username, null, null FROM users WHERE isAdmin = true –«  combinar resultados de consultas y recuperar información de usuarios de la base de datos donde el campo isAdmin es verdadero. El uso de UNION SELECT permite agregar resultados de una consulta adicional a la consulta original. La consulta solicita los campos id y username de los usuarios que cumplen con la condición especificada.
    Vamos a intentar conseguir la contraseña del usuario.  Aquí,» ‘ UNION SELECT id, username, password, null FROM users WHERE username = ‘johndoe'» — se utiliza como valor del parámetro username. Esta cadena está diseñada para combinar los resultados de una consulta adicional que selecciona el id, username y password de la tabla users donde el nombre de usuario es ‘johndoe’.

Tenemos dos obstáculos, solo nos devuelve la primera fila, y no podemos ver el valor de la contraseña

const UserType = new graphql.GraphQLObjectType({
  name: «User»,
  fields: {
    id: { type: graphql.GraphQLID },
    username: { type: graphql.GraphQLString },
    isAdmin: { type: graphql.GraphQLBoolean },
  },
});
    Esto significa que necesitamos extraer la contraseña del UNION SELECT en otra variable que se devuelva.    El enfoque más directo, si nuestro objetivo es obtener la contraseña, es devolver todos los campos:
    Utilizamos «‘ UNION SELECT password, password, password, password FROM users WHERE username = ‘johndoe’ — » como valor del parámetro username. Esta cadena está diseñada para combinar resultados de cuatro consultas de contraseña (password) de la tabla users, donde el nombre de usuario es ‘johndoe’.

Medidas de seguridad para prevenir inyecciones GraphQL

    Validación y sanitización de entradas:
    Utilizar bibliotecas de validación para asegurar que los parámetros sean del tipo y formato esperados.

Ejemplo en JavaScript utilizando validator.js:

const { isUUID } = require(‘validator’);
 
const resolvers = {
  Query: {
    user: async (_, { id }) => {
      if (!isUUID(id, 4)) {
        throw new Error(‘Invalid ID format’);
      }
      return await getUserById(id);
    }
  }
};

    Uso de esquemas de tipos estrictos:

    Definir esquemas de tipos estrictos en GraphQL para asegurar que las consultas y mutaciones sigan un formato específico.
type User {
  id: ID!
  name: String!
  email: String!
}
 
type Query {
  user(id: ID!): User
}
 
type Mutation {
  updateUser(id: ID!, input: UpdateUserInput!): User
}
 
input UpdateUserInput {
  name: String
  role: String
}

Autenticación y autorización:

    Implementar un sistema robusto de autenticación y autorización para asegurarse de que solo los usuarios autorizados puedan realizar ciertas operaciones.    Ejemplo de implementación en Apollo Server:
const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => {
    const token = req.headers.authorization || »;
    const user = getUserFromToken(token);
    if (!user) throw new AuthenticationError(‘You must be logged in’);
    return { user };
  }
});

Rate limiting y logging:

    Implementar limitación de tasa para prevenir abusos y monitorear las actividades sospechosas mediante registros detallados.    Ejemplo de rate limiting usando express-rate-limit:
const rateLimit = require(‘express-rate-limit’);
 
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutos
  max: 100 // límite de 100 solicitudes por IP
});
app.use(‘/graphql’, apiLimiter);

Uso de herramientas de seguridad:

    Utilizar herramientas y bibliotecas de seguridad que puedan ayudar a detectar y prevenir inyecciones GraphQL.    Ejemplo de uso de helmet para añadir cabeceras de seguridad:
const helmet = require(‘helmet’);
app.use(helmet());

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *