RESEARCHNODE / WRITEUP
dungeon dctf 2026 writeup
DefCamp D-CTF Dungeon writeup: Drupal 10 JSON:API SQL injection, PostgreSQL file read with pg_read_file, admin login token forgery, and remote code execution through a custom ArcaneLoad module.
dungeon writeup
recon
Drupal 10 + PostgreSQL 15.19. /platform/ is a client-side decoy. the useful endpoint is the public JSON:API collection for node/article tried xss at first, after seeing there is no admin bot script, moved on
sql injection
PostgreSQL EntityQuery code uses array keys as part of parameter names. the key is injectable through filter [...][condition][value][key]
error-based extraction was enough: forced a cast to fail so PostgreSQL puts the result into the error message:
one request returns one complete value from the database. I wasted 8 hours on this moment , as was trying to escalate to admin by bruteforcing hash or searchiing for LFI or smth that allows to read settings.php , FINALLY:
postgres file read
checking only the usual pg_read_file(text) overload gives permission denied. enumerate pg_proc and check every overload. dungeon_app can execute the four-argument version directly:
pg_read_file(/opt/dungeon/web/sites/default/settings.php, 0, 20000, true) this gives hash_salt. the user table gives warden’s uid, email, password hash and current login timestamp.
admin login
drupal one-time login token is an hmac over:
timestamp:last_login:uid:email
the hmac key is hash_salt + password_hash forged a fresh token immediately after reading login, then open /user/reset/uid/timestamp/hash/login. this logs in as warden without cracking the bcrypt hash.
getting the flag
this point where i wasted 3 hours , checking plugins, content, trying to find something that isn’t obvious at first, not understanding for to escalate from file read. in the end, my teammate found this: phpinfo showed a loaded arcane_loader module, its source was readable and showed that ArcaneLoad absolute ppath loads a native library and calls its exported arcane_handle() function
/opt/dungeon/web/sites/default/files/.htaccess was writable by www-data, the entrypoint also creates arcane-gate, so the remaining path is:
1. compile a small .so exporting arcane_handle() and searching 1/opt/dungeon/web for flag*.php 2. upload it to /tmp with lo_from_bytea/lo_put/lo_export through the sqli side effect 3. write ArcaneLoad /tmp/arcane_payload.so to .htaccess 4. request /sites/default/files/arcane-gate
the handler returned the root-owned flag file:
dctf{78fc6232a8613153a5183e41bc391fb2a2588b2f549476f06ba37699a18d7042}