Steps
A step is one action. What a step is, every field on a step, and how the runtime runs it.
A solution is a list of steps, and each step is one action. Think of a step as one line in a checklist: do this, then look for that. The runtime works through the list in order and checks each response as it goes.
A step, annotated
http reflect
get with "REFLECTED_7x7"
# send a unique marker in the template param and confirm it comes back
expect "REFLECTED_7x7" in the response
What this step does:
httpis the protocol andreflectis the step nameget with "REFLECTED_7x7": send the valueREFLECTED_7x7in a GET parameter on the targetexpect "REFLECTED_7x7" in the response: pass only if the response contains the marker
The whole step is one idea: send a marker, confirm the target reflects it. This is the classic test for a reflection point. A # comment explains what a step does; the runtime skips comment lines, so they never affect the run.
Steps in English
An http step is written as English sentences; the program does the heavy lifting. get fetches a page, post sends to one, and expect checks the response:
http sso
get "/login"
expect "SAMLRequest" in the response
capture the form field "SAMLRequest" as saml_b64
http deliver
post /acs as a form field SAMLResponse with base64 of "<samlp:Response InResponseTo=\"{reqid}\"/>"
expect "accepted" in the response
The get and post sentences set the method, path, and payload; the expect sentence is the assert. get with <value> gets the target itself with the value in a query parameter, the form a template-renderer target expects. See the http identity for the full directive list.
How a step runs
When the runtime reaches a step, four things happen in order:
- It fills in any values the file references (see Variables).
- It talks to the target: sends what the step says to send, then reads the response.
- If the step has a test, it checks the response against it.
- It hands any new values from the step to the steps that follow.
One conversation or many
For tcp, tls, and websocket steps the connection stays open across steps. This matters for attacks that need a handshake first: stage the handshake in one step, send the exploit in the next, on the same connection. A close the connection sentence ends the session when a step is done.
For http steps each step is its own request. Nothing carries over from the previous HTTP step.
On the byte-session identities (tcp, tls, and the rest) a step reads as sentences: send hex "<hex>" (or send hex of "<text>"), receive <n> bytes, receive until "<hex>", port <n>, timeout <n> seconds, close the connection, and expect .... On websocket, send is plain text. On process and file, run "<command>", read "<path>", and write "<path>" with "<data>" are the directives.
Pass, fail, and the result
A step without a test never fails. When every step with a test passes, the run is verified. When any step fails, the run is not verified.
The step fields
Every step has two required fields, the identity and the step name. The directives that follow depend on the protocol in identity; the full detail for each protocol is on its identity page. A step can also carry set, capture, and expect whatever the protocol.
identity field
Required on every step. The protocol this step speaks, and it decides which other fields apply. The runtime implements eighteen protocols today: http, https, websocket, tls, tcp, ssh, ftp, smtp, dns, snmp, ldap, redis, mysql, postgresql, smb, telnet, process, and file; the vocabulary is open and grows with the runtime. Each protocol has its own page with the full field detail (see Fields per identity below).
set field
Optional. Values to hand to later steps, in the same form as the solution-level vars. A later set on the same name wins. Reserved names are rejected. See Variables.
A set line can also compute a value: set <var> = aesgcm of "<plaintext>" with "<base64-key>" runs after the step’s captures and stores the sealed bound argument (base64(iv + ciphertext), 16-byte iv) for later steps. This is the primitive used to forge Next.js Server Action bound arguments from a leaked encryption key.
capture field
Optional on http, tcp, tls, and websocket steps. Pulls a value out of the response for later steps: on http steps from the full response text (status line, headers, and body), on tcp/tls/websocket steps from the received hex. See the capture form below.
expect directive
Optional on any step. The pass or fail test on the response. A step without an expect never fails. See the assert form below and Asserts.
The assert form
expect ...
The pass or fail test on the response. There are five forms:
expect "<text>" in the response: the response contains the exact textexpect the response to match "<regex>": the response matches the patternexpect a flag: the response contains a flag-shaped string such asFLAG{...}expect the target to call back "<expect>": the target contacted the out-of-band listener; when the callback is an HTTP POST carrying exfiltrated data, the report line shows the callback bodyexpect no response: the target never answered
The contains, regex, and oob forms take a value. The flag and empty forms take none.
note field
Optional. A plain-language explanation of what passing means, printed with the step result.
The capture form
A capture pulls a value out of the response for later steps, and it reads as English. The program does the heavy lifting: it knows page shapes, so you name the thing and it finds the bytes.
The shape form names a thing the page holds:
capture the <shape> "<name>" [of <source>] [base64] as <var>
Shapes are form field (an HTML input’s value), attribute (an XML or HTML attribute), json field (a JSON string field, raw or HTML-escaped), query parameter (a URL query value, raw or HTML-escaped), cookie (a Set-Cookie value), value after (on a byte-family step, the hex after a hex marker), and input name after (the tail of an HTML input’s name after a prefix). <var> is the name later steps use as {var}. of <source> (optional) searches an earlier variable instead of this step’s response. base64 (optional) decodes the source first.
capture the attribute "ID" of saml_b64 base64 as reqid
Reads: capture the ID attribute, from saml_b64, base64-decoded, as reqid. No markup, no pattern.
The template form matches literal text with one <name> placeholder where the value appears:
capture <var> [from <source>] [base64] where "<text with one <name> placeholder>"
The regex form is the same with a pattern instead of a template:
capture <var>: <regex> [from <var>] [base64]
The first capture group is the value that is stored. The source (from or of) must name a variable declared before the step: a solution var, a built-in, an earlier step’s set or capture, or an earlier capture on the same step.
Fields per identity
Which fields apply depends on the identity:
| identity | directives |
|---|---|
http | a method line (required), with the header, with the cookie, capture, expect |
websocket | send (required), receive, receive until, timeout, close the connection, capture, expect |
tls | send (required), receive, receive until, timeout, close the connection, capture, expect |
tcp | send (required), receive, receive until, timeout, close the connection, capture, expect |
process | run (required), expect |
file | read or write (required), expect |
Each identity has its own page with the full detail: http, websocket, tls, tcp, process, and file.
Determinism
Keep steps deterministic: fixed bytes, fixed paths, no randomness. The same solution against the same target should give the same result, so a run can be repeated and compared any time.
See Variables for how values flow between steps and Asserts for the pass or fail tests.