← Back to Writeups
HTBN/AWeb

Hydroadmin

XESXOR8/23/20264 min read
#web#htb#n/a

Hydroadmin

Platform: HackTheBox | Category: Web | Type: Challenge | Difficulty: Medium | OS: NA | Author: D3v0o0Nu11 | Date: 2026-02-01 | Status: Solved Techniques: file_manipulation, graphql_batching, pin_bruteforce, server_restart

Summary

Task: Fix a GraphQL batching vulnerability that allows PIN brute-force bypass of rate limiting. Solution: Use the online code editor API to read index.js, change allowBatchedHttpRequests from true to false in Apollo Server config, save the patched file, restart the server, and verify the fix to receive the flag.

Recon

Port scan

nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<PORT><SVC><VER><notes>

Enumeration highlights

  • Event: hackthebox | ID: 20260201_htb_hydroadmin
  • Tags: graphql, api_exploitation, batching_attack, rate_limit_bypass, code_editor, patch_challenge
  • Indicators: allowBatchedHttpRequests: true, Apollo GraphQL server, rate limiting, 4-digit PIN, online code editor
  • Source: 20260201_htb_hydroadmin.md

Foothold

Vulnerability / Misconfiguration

  1. File_manipulation
  2. Graphql_batching
  3. Pin_bruteforce
  4. Server_restart
<command>

Exploitation

  • See original writeup content for detailed exploitation.

Privilege Escalation

Enumeration

sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
ps aux

Exploitation

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
<command>

Flags

FlagLocationValue
flagREDACTED

Key Takeaways / Lessons

  • file_manipulation
  • graphql_batching
  • pin_bruteforce
  • server_restart
  • Tags: graphql, api_exploitation, batching_attack, rate_limit_bypass, code_editor, patch_challenge

Original Writeup

<details><summary>Click to expand original content</summary>

Hydroadmin - HackTheBox Challenge

Description

"With reservoirs sealed and cities teetering on thirst, our heroes storm the HydroAdmin control room to reopen valves and restore the flow of water."

Target: 83.136.249.164:45091

Analysis

Initial Reconnaissance

Discovered an online code editor called "HTB Editor" at the target with the following API endpoints:

  • /api/directory - list files
  • /api/file?path=X - read file content
  • /api/create-file - create new file
  • /api/rename - rename files
  • /api/restart - restart the server
  • /api/verify - verify if vulnerability is patched

Source Code Analysis

Key files discovered:

index.js - Main server with Apollo GraphQL server configured with vulnerable batching:

const server = new ApolloServer({
  ...armor.protect(),
  introspection: false,
  typeDefs,
  allowBatchedHttpRequests: true,  // VULNERABLE!
  resolvers
});

models/ControlPin.js - PIN generation (4-digit, 1000-9999) and verification logic

schema/resolvers.js - GraphQL resolvers including PIN verification

exploit/solver.py - A script showing how to exploit GraphQL batching to brute-force PINs

The Vulnerability

The GraphQL server had allowBatchedHttpRequests: true which allows:

  • Sending multiple GraphQL queries in a single HTTP request
  • Bypassing rate limiting (10 requests per minute)
  • Brute-forcing all 9000 possible PINs (1000-9999) in just a few batch requests

The Twist

This challenge required PATCHING the vulnerability, not exploiting it! The /api/verify endpoint checked if the vulnerability was fixed.

Solution

Exploitation Steps

  1. Rename original file: Backup the vulnerable index.js
  2. Read backup: Get the original content
  3. Patch content: Change allowBatchedHttpRequests: true to allowBatchedHttpRequests: false
  4. Create patched file: Write the fixed version
  5. Restart server: Apply changes
  6. Get flag: Verify the patch

Exploit Script

#!/usr/bin/env python3
"""
Hydroadmin - GraphQL Batching Patch Challenge
HackTheBox Challenge
"""
import requests

BASE_URL = "http://83.136.249.164:45091"

# Step 1: Rename original file to backup
print("[*] Renaming index.js to index.js.bak...")
resp = requests.post(f"{BASE_URL}/api/rename", json={
    "oldPath": "index.js",
    "newPath": "index.js.bak"
})
print(f"    Response: {resp.json()}")

# Step 2: Read the backup file
print("[*] Reading index.js.bak...")
resp = requests.get(f"{BASE_URL}/api/file?path=index.js.bak")
content = resp.json()['content']
print(f"    Got {len(content)} bytes")

# Step 3: Patch the vulnerability
print("[*] Patching allowBatchedHttpRequests...")
patched = content.replace(
    'allowBatchedHttpRequests: true',
    'allowBatchedHttpRequests: false'
)

# Step 4: Create patched file
print("[*] Creating patched index.js...")
resp = requests.post(f"{BASE_URL}/api/create-file", json={
    "path": "index.js",
    "content": patched
})
print(f"    Response: {resp.json()}")

# Step 5: Restart server
print("[*] Restarting server...")
resp = requests.post(f"{BASE_URL}/api/restart")
print(f"    Response: {resp.json()}")

# Step 6: Wait and verify
import time
print("[*] Waiting for server restart...")
time.sleep(2)

print("[*] Verifying patch...")
resp = requests.get(f"{BASE_URL}/api/verify")
result = resp.json()
print(f"    Result: {result}")

if 'flag' in result:
    print(f"\n[+] FLAG: {result['flag']}")

Vulnerable vs Fixed Code

Vulnerable (original):

const server = new ApolloServer({
  ...armor.protect(),
  introspection: false,
  typeDefs,
  allowBatchedHttpRequests: true,  // VULNERABLE
  resolvers
});

Fixed (patched):

const server = new ApolloServer({
  ...armor.protect(),
  introspection: false,
  typeDefs,
  allowBatchedHttpRequests: false,  // FIXED
  resolvers
});

Lessons Learned

  1. GraphQL Batching Bypass: GraphQL batching can bypass rate limiting - always set allowBatchedHttpRequests: false in production Apollo servers

  2. Defense in Depth: Even with @escape.tech/graphql-armor library, batching was still enabled manually - security libraries don't protect against misconfigurations

  3. Patch Challenges: Sometimes CTF challenges require fixing vulnerabilities, not just exploiting them - read the challenge description carefully

  4. Code Editor Attack Surface: Online code editors with file manipulation APIs (create, rename, restart) can be powerful attack vectors

  5. PIN Bruteforce Math:

  • 4-digit PIN (1000-9999) = 9000 possibilities
  • Rate limit: 10 requests/minute
  • Without batching: 900 minutes = 15 hours
  • With batching (1000 queries/request): 9 requests = instant

References

</details>

Auto-tracked: saved to WriteUps; run /xesor-revise to fold lessons into XESXor_Methodology.md.

signed by XESXOR