$ cat writeup.md…
$ cat writeup.md…
ASIS CTF Quals 2026
Task: Node.js link-in-bio app whose theme API deep-merges JSON, enabling prototype pollution of the reserved ogImage preview field. Solution: SSRF via polluted ogImage, readback via /api/preview diagnostics, IPv6-mapped-IPv4 allowlist bypass, find media-metadata on 172.18.0.3:9001, flag from /flag.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
Paraphrased task text: Rick built "Portalis", a link-in-bio site that creates a polished preview whenever a profile is shared. The outdated system only uses approved profile details; "anything behind the portal is none of your business... Just be careful where your profile points."
English summary: a Node/Express application behind nginx serves a profile/theme editor at http://91.107.189.166:3000. The theme API accepts arbitrary JSON and deep-merges it, the preview pipeline server-side fetches a reserved ogImage field, and a diagnostics endpoint echoes fetch results. Goal: reach the internal network and retrieve the real flag. The instance was a single shared box, unstable under load — every step had to be paced slowly.
Recon. Public pages: /, /dashboard, /explore, /help, /about, /u/me (public portal keyed by the sid session cookie). /help documents the API:
PUT /api/theme — takes JSON and deep-merges it into the stored theme (no key filtering).GET /api/preview — returns plain-text diagnostics of the preview/media pipeline.GET /api/schema — machine-readable schema.GET /u/me — public profile view.robots.txt contains Disallow: /internal-metadata — later proven to be a pure decoy on the public app (404 via every host form, including loopback over SSRF).
GET /api/schema returns the decisive hint:
{ "settable": ["name", "bio", "accent", "avatarUrl", "links"], "rendered_context": ["name", "bio", "accent", "avatarUrl", "links", "ogImage"], "note": "ogImage is reserved and populated by the media pipeline; it is not accepted from theme input" }
Vulnerability 1 — prototype pollution. PUT /api/theme deep-merges user JSON without filtering dangerous keys, so {"constructor":{"prototype":{...}}} (plain __proto__ works too) writes arbitrary properties onto Object.prototype.
...
$ grep --similar