4 min readTOOLING · METHODOLOGY · INFRASTRUCTURE

Disposable out-of-band infrastructure on Cloudflare Workers

How I build throwaway OOB callback sinks, attacker JWKS hosts, OAuth redirect receivers, and smuggling relays on the edge, and why Workers beat a single VPS for blind-vuln confirmation.

Half of interesting bug classes produce no response you can read. Blind SSRF, blind injection, server-side request forgery into an auth flow, request smuggling that desyncs another user’s connection: the signal lands out of band. To turn “the server processed something” into a confirmed finding you need a place for that signal to arrive, and that place has to be controlled by you and trusted enough by the target to be reached at all.

Why the edge beats a VPS for this

The default answer is a VPS running an interaction server, and for a single long-running collector that’s fine. But OOB work wants many small, short-lived, differently-shaped endpoints, and that is where a single box gets in the way.

The mental model is the opposite of a server. A VPS is a pet you maintain; Workers are cattle you spawn for one request shape and delete.

The four endpoints I keep

I treat these as templates, not running services. I deploy the one I need and tear it down after.

The callback sink. The workhorse. It logs every inbound request, full, with a unique token per test, and gives the inbound side a canonical body so I can tell a real hit from a scanner. A blind SSRF, a webhook that forgot to validate its URL, or an injection that triggers a fetch all land here.

js
export default {
  async fetch(req, env) {
    const u = new URL(req.url);
    const record = {
      id: u.pathname,
      ts: Date.now(),
      ip: req.headers.get('cf-connecting-ip'),
      ua: req.headers.get('user-agent'),
      method: req.method,
      headers: Object.fromEntries(req.headers),
    };
    await env.HITS.put(`${u.pathname}-${Date.now()}`, JSON.stringify(record));
    return new Response('ok');
  },
};

The per-path token is what makes attribution honest: each payload carries a distinct path, so when a hit arrives I know exactly which injection point fired, not just that something did.

The attacker JWKS / OIDC discovery host. For JWT and OIDC work where the target will fetch a signing key or discovery document from a URL it was handed. The Worker serves a valid /.well-known/openid-configuration and a jwks.json whose public key matches a private key I hold, so a token I sign verifies against my key set if the target can be pointed at my issuer. It is a static-response host with the right content types and CORS, nothing more.

The OAuth redirect receiver. A redirect_uri catcher. When I’m testing whether an authorization flow will redirect a code or token to a host it shouldn’t, this endpoint captures the full query and fragment and stores it. The fragment matters: implicit-flow tokens arrive after the #, which servers never log, so the receiver echoes a tiny script that posts location.hash back to itself. Catching a real authorization code or access token here is the proof the flow’s allowlist failed.

The smuggling relay. For request-smuggling and client-side desync work, a Worker that records the next request to arrive on a connection with timing, so I can see when a desynced or poisoned request gets attributed to my collector instead of its intended backend. It is a minimal relay that captures raw-ish request data and flags anything that arrives out of the expected shape.

The discipline around it

Disposable infrastructure is only an asset if you treat it as disposable. Each test gets its own hostname and its own token; I never reuse a sink across unrelated engagements, because correlating hits is impossible once two tests share a collector. The clean IP and the instant deploy are the headline features, but the real reason this works is that it makes one-endpoint-per-question cheap, and one question per endpoint is how you keep blind-vuln confirmation honest.

The platform isn’t the point. The point is that confirmation infrastructure should be spun up for a single shaped question, trusted by the target on its own merits, and gone afterward. The edge just happens to make that the path of least resistance.