WEBVERSE

Loading...

VexVary
mediumFree

VexVary

VexVary is a multi-tenant e-signature platform. Teams send agreements, collect signatures, and get a certificate of completion sealed by the VexVary signing authority. An internal signing key turned up somewhere it should not have. Find the way in.

web-cache-poisoningunkeyed-headercache-key-discrepancyssrfinternal-api-exposureinformation-disclosure
pythonflaskgunicornnginxpostgres

The Scenario

VexVary seals every completed agreement with its verification mark, and the team is

quietly proud of it. Last week the master signing key behind that seal surfaced

somewhere no customer should be able to reach. Start as an ordinary customer and find

the way in.

Lab Intel

Synopsis

An nginx proxy_cache tier fronts a Flask e-signature app and caches only /api/branding/system, keyed on scheme+host+path and not varying on X-Forwarded-Host. The app builds the system seal URL from that forwarded host (a white-label asset-host feature) and reflects it unvalidated. Poisoning the cached manifest injects an attacker-chosen seal URL. The Certificate of Completion render reads the manifest back through the same cache (for cache-consistency with the on-screen preview) and inlines the fetched seal, so a poisoned seal URL of 127.0.0.1:8080 turns the render into a readable SSRF that embeds the internal Signing Authority console's page, which displays the master seal-signing key (the flag). Fully in-band, no callback server.

Architecture

A medium multi-service lab on three containers (nginx proxy_cache gateway, a Flask app that also runs an internal console on its own loopback, and postgres). The primary vulnerability is web cache poisoning: 1. Recon. Responses carry X-Cache-Status. GET /api/branding/system is cached (MISS then HIT). Its JSON sealUrl reflects the request host, and the cache key does not vary on X-Forwarded-Host. 2. Poison. Send GET /api/branding/system with X-Forwarded-Host: 127.0.0.1:8080/? The cached sealUrl becomes http://127.0.0.1:8080/?/brand/system-seal.svg. The trailing "?" pushes the fixed /brand/system-seal.svg suffix into the query string, so the fetched path collapses to "/". The poisoned entry is served to every consumer for the cache TTL (30s). 3. Readable SSRF. Completing a document renders a Certificate of Completion server-side. The render reads the branding manifest through the edge cache (cache-consistency with the browser preview), so it gets the poisoned sealUrl, fetches it with redirects disabled, and inlines the returned markup as the seal. The fetch reaches the internal Signing Authority console on 127.0.0.1:8080, whose "/" renders the master seal-signing key. Its HTML is inlined into the attacker's own certificate. Read WEBVERSE{...}. Hardened-except-here: the tenant workspace-logo fetch is https-only, resolves and blocks private/loopback IPs, and disables redirects, so you cannot SSRF via your own branding. The system seal is not tenant-editable and the render exposes no seal-URL parameter, so the cache poison is the only way to control the fetched URL. The console is loopback-only and never routed by the gateway. Only /api/branding/system is cached (authenticated pages never are). The flag lives only in the console process memory (captured from FLAG at startup then scrubbed from the environment) and is reachable only through the poisoned render.

Who It's For

Built for testers who want a real web-cache-poisoning chain rather than a header trick in isolation. You should be comfortable observing cache behaviour with a proxy, reasoning about cache keys and unkeyed inputs, and recognising a server-side fetch you can only influence indirectly. No out-of-band listener is required.

Skills You'll Practice

  • Fingerprinting an edge cache and its cached endpoints from X-Cache-Status
  • Finding an unkeyed request header that is reflected into a response
  • Priming a shared cache key within its TTL to serve a poisoned response
  • Collapsing a fixed URL suffix to a target path with a query separator
  • Chaining a poisoned config into a readable server-side request forgery

What You'll Gain

  • Why an unkeyed header reflected into a URL is a cache-poisoning primitive, not a cosmetic bug
  • How a poisoned edge cache reaches a server-side consumer, not just other browsers
  • Recognising that an internal service bound to loopback is not safe when a co-located render fetches attacker-influenced URLs
  • The habit of checking what a cache keys on before trusting a reflected value

Ready to hack VexVary?

This lab is free. Sign up and start hacking.