WEBVERSE

Loading...

mediumSSTIPro

Foundry Comics

Brooklyn indie micro-press with an open submissions portal. Would-be contributors paste a pitch into the submit-preview tool and see how the editor will read it — folded into the house style and rendered server-side. The portal kept the previous owner's stack alongside the rebuild, so different sessions hit different code paths.

The Scenario

Foundry Comics is a three-person riso-print outfit in Gowanus that

publishes single-issue runs by emerging cartoonists. In 2022 they

picked up a defunct submission portal off another small press that

was winding down — quicker than rebuilding from scratch. The portal

works, but the migration was never finished: returning creators get

routed through whichever bucket the cookie says they belong to,

and the two buckets were never fully merged.

Challenge Intel

Synopsis

The /submit/preview tool renders the submitter's pitch through one of two templating engines — Jinja2 (Python/Flask) or Twig (PHP) — chosen by a cookie bucket assigned on first visit. Fingerprint the engine, then fire the engine-specific payload.

What It Is

Foundry Comics' submission preview folds the contributor's pitch body into a template before rendering. The submission portal is polyglot under the hood — an Apache/PHP/Twig backend serves some sessions, a Python/Flask/Jinja2 backend serves others. The choice is locked at first visit via the `fc_portal_v` cookie (set server-side, two buckets), and an nginx reverse proxy routes /submit/preview to whichever backend the bucket points at. Both backends concatenate the pitch body into a template body before rendering. Both are unsandboxed. Solve path: probe with `{{7*7}}` to confirm template echo (both engines render to `49`), then fingerprint by trying a Python-style string subscript like `{{ "abc"[0] }}` — Jinja2 returns `a`, Twig returns empty, because Twig doesn't index strings by integer key. Once the engine is identified, fire: Twig: {{ "cat /flag.txt"|passthru }} (passthru is a legacy filter the acquired SubsDirect codebase shipped — exposes shell_exec to template authors) Jinja2: {{ self.__init__.__globals__.__builtins__.__import__("os") .popen("cat /flag.txt").read() }} Clearing cookies re-rolls the bucket; both surfaces must be exercised across sessions to solve in both buckets.

Who It's For

Players who have done one unsandboxed Jinja2 SSTI and want to learn engine fingerprinting and the Twig RCE primitive. Useful prep for polyglot-stack assessments where the engine isn't advertised in the response.

Skills You'll Practice

  • Reading response differential to detect a server-side templating layer
  • Distinguishing Twig from Jinja2 by string-subscript semantics
  • Recognising and chaining a legacy custom Twig filter (`passthru`) into RCE
  • Jinja2 `__init__.__globals__` → `os.popen` RCE primitive
  • Recognising cookie-driven engine routing and re-rolling buckets

What You'll Gain

  • Engine-fingerprint cheat sheet you can re-use against real polyglot stacks
  • Both Twig and Jinja2 SSTI RCE payloads memorised
  • Mental model for why a feature-flag cookie can hide a whole separate runtime

Ready to hack Foundry Comics?

Upgrade to Pro to unlock this challenge and the full library.