OutOfBits
// out-of-band

OutOfBits

The OAST listener that talks back.

DNS & HTTP callbacks captured. Mutate the response with Python. Sandboxed. Audited. Replayable against any past interaction.

Platform vital signs · last 30 days

150,774
callbacks captured
143,399 dns
queries answered
7,375 http
requests served

What you can do

01

Confirm a blind callback

Generate a one-shot host, plant the URL, watch it land.

# in any vulnerable parameter:
http://a8x2.ooast.net/probe

# on /interactions (live, ~1s after firing):
GET /probe         200 OK
DNS A query        NOERROR
02

Mutate the response

Sandboxed Python that runs on every callback. Change status, add headers, rewrite the body.

def handle_http(ctx):
    if ctx.request.path == "/admin":
        ctx.response.status_code = 401
        ctx.response.headers["WWW-Authenticate"] = \
            'Basic realm="x"'
    return ctx
03

Pipeline & audit

Compose modifiers in order. Per-stage input/output snapshots — see exactly what each step did.

pipeline "tarpit" · http
  ├─ add-trace-header     ok 12 ms
  ├─ canned-401            ok  3 ms
  └─ log-source-ip         ok  5 ms
  on_error: stop      total: 20 ms
04

Persist state between callbacks

Per-modifier ctx.state and per-user ctx.shared. DNS rebinding in four lines — nothing else in the OAST space does this.

def handle_dns(ctx):
    n = ctx.state.get("count", 0) + 1
    ctx.state["count"] = n
    ip = "127.0.0.1" if n % 2 else "127.0.0.2"
    ctx.response.answers = [{
        "name": ctx.request.qname, "type": "A",
        "ttl": 0, "data": ip,
    }]
    return ctx
06

Triage with tags new

Confirmed vs. dupe vs. background noise — tag interactions and filter the list down to what matters. Free-text notes per row for the story you'll forget by tomorrow.

# on /interactions:
17:42:03 dns a8x2  a8x2.ooast.net  [confirmed][ssrf]
17:42:11 http a8x2 GET /probe      [dupe] [note]
17:43:01 dns a8x2  noise.a8x2...   [background]
05

Webhooks new

Skip the polling. Point a webhook at your own URL; we push a signed JSON envelope on every captured callback. Per-host or firehose, with HMAC + retries + auto-disable.

# headers on every delivery:
X-OOB-Event:     interaction.captured
X-OOB-Delivery:  42           # idempotency key
X-OOB-Timestamp: 1748817225
X-OOB-Signature: sha256=a8d2...  # HMAC over "<ts>.<body>"
// defense in depth

Five layers of sandbox.

Modifiers are arbitrary user code. So they run in a fresh subprocess, blocked from doing anything they shouldn't.

  1. AST allowlist — no import, dunders, open, eval.
  2. Restricted builtins — only a curated subset of __builtins__.
  3. rlimits — 1s CPU · 100 MiB · NPROC=0 · 32 FDs.
  4. seccomp — networking, ptrace, mount, kexec, modules — all EPERM.
  5. Landlock — kernel-enforced filesystem isolation. No paths.

Want the full picture? User guide.

Built by Carl Sampson.