Skip to content
Security
Skill

/client-reverse

Client-side request-signing and anti-bot token reversal for bug bounty — when a request carries a sign/sig/hmac/token/nonce/timestamp/X-Sensor header that Burp Repeater cannot replay, recover the signer just enough to reproduce the request outside the client. Packet-first

From plugin
claude-bug-bounty
4.2k15 skills9 agents33 commands
Install
$ npx -y skills add shuvonsec/claude-bug-bounty --skill client-reverse --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/client-reverse

Context preview

The summary Claude sees to decide when to auto-load this skill.

Client-side request-signing and anti-bot token reversal for bug bounty — when a request carries a sign/sig/hmac/token/nonce/timestamp/X-Sensor header that Burp Repeater cannot replay, recover the signer just enough to reproduce the request outside the client. Packet-first

SKILL.md

client-reverse.SKILL.md
name: client-reverse
description: 'Client-side request-signing and anti-bot token reversal for bug bounty — when a request carries a sign/sig/hmac/token/nonce/timestamp/X-Sensor header that Burp Repeater cannot replay, recover the signer just enough to reproduce the request outside the client. Packet-first staging (capture real request → prove replay works → only reverse if replay fails) across the locate→recover→runtime→validation→replay spine. Covers tracing backward from the signature field (writer→builder→entry→source), isolating user-mutable sign inputs (timestamp/nonce/deviceId/body) vs constants (secret key), hooking fetch/XHR in DevTools, JS deobfuscation basics (webpack/wasm/JSVMP), and the bounty payoff: reach the protected API to then hunt IDOR/auth/business-logic. Use when Burp/mitmproxy replay of a signed or anti-bot-gated request fails and you suspect a client-computed field is blocking you.'

CLIENT-SIDE REQUEST-SIGNING / ANTI-BOT TOKEN REVERSAL

You hit a request you cannot replay. Burp Repeater returns `401 invalid signature` or `403 bot detected` even though the browser/app does it fine. There is a `sign`, `sig`, `X-Signature`, `_token`, `nonce`, `X-Acf-Sensor-Data`, or encrypted body the client computes. This skill recovers **just enough** of that signer to reproduce the request **outside** the client.

> **Why a bug bounty hunter cares:** the signature is not the bug. The signature is the **lock on the door**. Behind it is an API the program assumed only their own client would ever reach — so that API is often under-tested for IDOR, BOLA, mass assignment, and business logic. Reversing the signer is the cost of admission; the **payout** comes from what you fuzz once you're inside. Never report "I reversed your sign algorithm" as a finding on its own — that is N/A. Report the IDOR/auth bug you reached **through** it.

---

THE CORE PRINCIPLE: PACKET-FIRST

> Reverse engineering is a **blocker-resolution step, not the default entrypoint.** Capture the real request first. Prove whether it already replays. Only reverse the signer if replay actually fails.

Most hunters waste hours decompiling JS for a "signature" that turns out to be replayable as-is, or gated by a timestamp that's valid for 5 minutes. Run this gate **before** opening DevTools Sources:

1. Capture the real request    (Burp/mitmproxy proxy, or DevTools → Network → Copy as cURL)
2. Replay it UNCHANGED          (paste cURL into terminal, or Burp Repeater)
   → 200 / works?  → IT'S NOT SIGNED. Skip all reversing. Go fuzz it.
3. Replay it again 5 min later  → still 200?  → no freshness check (replay window is wide/infinite)
4. Mutate ONE non-signed field  (e.g. change an `id` in the body, keep sign as-is)
   → 200?  → the sign does NOT cover that field → tamper freely, no reversing needed
   → 401?  → the sign covers it → NOW you reverse (continue to STAGES below)

Steps 2–4 alone kill ~half of "I need to reverse this" assumptions. A signature that omits the payload or endpoint, or never expires, is itself the bug — see the CoinMate pattern in **Real Paid Examples**.

---

STAGE SPINE: locate → recover → runtime → validation → replay

Pick the stage from **engineering state**, not from clue words. "I see the word `sign`" does not mean you're in `recover`. You are in `locate` until you can point at the exact line that writes the signature.

intake → evidence → locate → recover → runtime → validation → replay

| Stage | Enter when... | Goal | Exit when... | |---|---|---|---| | **locate** | the signing function / write boundary is unproven | find where the sign field is written and what feeds it | you can point at writer ← builder ← entry ← source | | **recover** | boundary is real but the code is obfuscated/opaque | de-shell only the layer blocking you (webpack/wasm/JSVMP) | you have a readable or callable signer contract | | **runtime** | code is clear but browser-exec ≠ your-exec diverge | find the first divergence (missing object/state/anti-debug) | local run reproduces browser sign output | | **validation** | remaining work is equivalence proof | match checkpoints, not just final output | sign(input) == observed for fresh inputs | | **replay** | sign reproduces outside the client | Burp/Python baseline request you can fuzz | a stable request you can mutate for IDOR/auth |

Carry a one-line handoff between stages. Do not promote a guess to a fact:

--- Stage Handoff ---
From: locate  To: recover
Proven: sign written at app.min.js:1, line ~4021; inputs = ts, nonce, JSON body, deviceId
Open:   builder is inside webpack module 5f3 behind a string-table — need to de-shell that one module
Invalid: assumption that deviceId was constant (it rotates per session)

---

STAGE 1 — LOCATE: trace backward from the signature field

You know the **output** (the `sign` value on the wire). Walk backward to the **source**. Keep each layer distinct:

writer  <-  builder  <-  entry  <-  source
  • **writer** — the line that finally puts `sign` into the body/header/query/cookie/WS frame
  • **builder** — the transform: `HMAC`, `MD5`, `AES`, sort-then-concat, `JSON.stringify` ordering
  • **entry** — the UI action / callback / response that kicks off the chain
  • **source** — what feeds the inputs: upstream response, localStorage, cookie, `Date.now()`, `crypto.getRandomValues`, user input

Browser: find the writer in Chrome DevTools

# 1. XHR/fetch breakpoint — break the moment the signed request fires
DevTools → Sources → XHR/fetch Breakpoints → + → paste the endpoint path (e.g. /api/order)
   trigger the action → execution pauses inside the request stack
   → walk UP the Call Stack panel: the frame that mutates headers/body is your writer

# 2. Search the bundle for the field name (catches the writer fast)
DevTools → Sources → Ctrl+Shift+F (search all loaded scripts)
   search: "sign"  "X-Signature"  ".sign ="  "headers["  "signature"
   click {} (pretty-print) on t
Read more
Ships withclaude-bug-bounty

AI-powered bug bounty hunting from your terminal - recon, 20 vuln classes, autonomous hunting, and report generation. All inside Claude Code.

Get the whole plugin

Other skills on claude-bug-bounty.