# Format reference

> The .txt grammar for the implemented protocols on one page: every line, every field, every assert. Written to be read by a language model.

This page is the complete grammar for the protocols the runtime implements today. It is written so a language model can produce a correct file from this page alone. Nothing else is needed for the implemented set; the vocabulary is open and grows toward every protocol in the [Identity list](/docs/solution/identity/).

## The file

- A solution is a text file with a `.txt` extension.
- Lines starting with `#` are comments and are ignored.
- Blank lines separate sections. Two-space indentation marks a block.
- A step is one or more English sentence directives, one per line. The directive starts with a verb word (a method like `get`/`post`, or `send`, `receive`, `run`, `read`, `write`, `expect`, `capture`, `with`, `timeout`, `port`, `close`); the rest of the line is the value. Quoted values use `"..."`.
- The header lines and the `vars:`, `set:`, `headers:`, `cookies:`, and `oob:` blocks are the only `name: value` lines. There is no other syntax.

## The header

Lines before the first step:

| line | meaning |
| --- | --- |
| `id: <name>` | required, names the attack, in `vendor/product` style |
| `summary: <text>` | optional, one sentence about the attack |
| `ref: <ref>` | optional, repeat the line for more, for example `CVE-2014-0160` |
| `param: <name>` | optional, the HTTP input parameter name |
| `vars:` | optional block, one `name: value` per line, starting values |
| `oob:` | optional block, `host:` and `port:` lines; `port: 0` picks a free one |

The names `flag_path`, `oob_host`, and `oob_port` are reserved and cannot be declared.

## A step

A step is a block. The first line is `<identity> <step-name>`. Indented lines below it are directives, one per line.

```text
<identity> <step-name>
  <directive>
  expect ...
```

The `identity` and the step name are always required. A step name is one word, unique within the file, and cannot be a directive keyword (a sentence verb such as `get`, `post`, `send`, `expect`, or `capture`).

## Identity

The runtime implements eighteen protocols today, the ones that appear most often in real exploits. Use only these to produce a valid file; the vocabulary is open and grows toward every protocol in the [Identity list](/docs/solution/identity/):

| identity | required (English directive) | other directives |
| --- | --- | --- |
| `http`, `https` | a method directive (`get`/`post`/`put`/`patch`/`delete`/`head`/`options`) | `with the header`, `with the cookie`, `set`, `capture`, `expect` |
| `websocket` | `send` | `receive`, `receive until`, `timeout`, `close the connection`, `capture`, `expect` |
| `tcp`, `tls`, `ssh`, `ftp`, `smtp`, `ldap`, `redis`, `mysql`, `postgresql`, `smb`, `telnet` | `send` | `receive`, `receive until`, `port`, `timeout`, `close the connection`, `capture`, `expect` |
| `dns`, `snmp` | `send` | `receive`, `receive until`, `timeout`, `close the connection`, `capture`, `expect` |
| `process` | `run` | `expect` |
| `file` | `read` or `write` | `expect` |

The "required" column is the directive that opens the step. The "other directives" column lists the sub-lines that follow.

### http, https

An http step is a set of directives; the program does the heavy lifting. The directives are:

| directive | meaning |
| --- | --- |
| `get <path>` | GET the path; the path may be quoted (`get "/login"`) |
| `get with <value>` | GET the target itself with the value in a query parameter |
| `get <path> with <value>` | GET the path with the value in a query parameter |
| `put`, `patch`, `delete`, `head`, `options` `<path>` | the verb is the HTTP method; the same forms as `get` apply |
| `post "<path>" [as <shape>] [with <value>]` | POST the path (quote it like `get`). `as a form field <name>` posts one form field, `as json` a JSON object with the value, `as a raw form` the body verbatim with form content-type (the default), `as a raw json` the body verbatim with JSON content-type, `as a raw multipart` the body verbatim as a multipart/form-data body (the runtime reads the boundary from the body's first line) |
| `with the header "<name>" set to <value>` | set one request header |
| `with the cookie "<name>" set to <value>` | add one cookie, joined into the `Cookie` header |
| `expect ...` | the assert: `expect "<text>" in the response`, `expect the response to match "<regex>"`, `expect the target to call back "<expect>"`, `expect a flag`, `expect no response` |

A value may be written `base64 of "<text>"` to base64-encode it after `{var}` substitution. Captures use the English forms above. The example solution reads entirely as sentences:

```text
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 target is a base URL; `https` is the same exchange over TLS, and the `http` identity also covers both schemes.

A solution-level `headers:` or `cookies:` block (before the first step, like `vars:`) sets defaults applied to every http step. A step's own `with the header`/`with the cookie` lines, or its own `headers:`/`cookies:` block, add to or override them. Values accept `{var}` references, filled when each step runs. This is how a multi-step session replays the same cookies on every request without repeating them:

```text
cookies:
  AUTH_SESSION_ID: "{sid}"
  KC_AUTH_SESSION_HASH: "\"{hash}\""
```

The response an assert or capture sees is the full exchange: the status line and headers of every response in the redirect chain (so a redirect `Location` is assertable), then the final body.

### websocket

A websocket step reads as sentences: `send "<text>"` sends the payload, `receive <n> bytes` reads at most that many bytes, `receive until "<hex>"` stops at a hex marker, `timeout <n> seconds` bounds the read, `close the connection` ends the session, and `expect ...` checks the answer:

```text
websocket ws-rpc
  send "{\"jsonrpc\":\"2.0\",\"id\":1}"
  receive 65536 bytes
  timeout 5 seconds
  expect "226b22" in the response
```

The payload is sent as-is (text, wrapped into a masked frame by the runtime); the `send hex "..."` form does not apply. The connection stays open across steps.

### tcp, tls, and the byte-family services

A byte step reads as sentences: `send hex "<hex>"` sends raw bytes (or `send hex of "<text>"` hex-encodes readable text), `receive <n> bytes` reads at most that many, `receive until "<hex>"` stops at a marker, `port <n>` reconnects the session to another port, `timeout <n> seconds` bounds the read, `close the connection` ends the session, and `expect ...` checks the hex response:

```text
tls malformed-heartbeat
  send hex "1803030003014000"
  receive 70000 bytes
  timeout 5 seconds
  expect the response to match "18030[123]40"
```

The `send hex of "<text>"` form interprets the usual `\r`, `\n`, `\t`, `\"`, `\\` escapes plus `\xHH` for one arbitrary byte, so control characters stay visible (`send hex of "HELO x\r\n"`). `tls` sends and receives raw TLS record bytes, so a solution authors the handshake itself. Each service (ssh, ftp, smtp, ldap, redis, mysql, postgresql, smb, telnet) has a default port when the target omits one.

### dns, snmp

`dns` and `snmp` speak raw datagrams over UDP; the fields are the byte-session fields above. The target is `host:port`; the default port is 53 for `dns` and 161 for `snmp`.

### process

A process step reads as `run "<command>"`, with an optional `in "<directory>"` clause for the working directory:

```text
process enum
  run "python ssh_enum.py 127.0.0.1" in "C:\\Temp\\prototest"
  expect the response to match "root is a valid user"
```

### file

A file step reads as `read "<path>"` or `write "<path>" with "<data>"`:

```text
file write-it
  write "C:\\Temp\\exploit.mvg" with "push graphic-context\nfill 'url(x|id)'\npop graphic-context\n"
```

## Capture

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 and encodings, so the author names the value and the runtime finds the bytes.

The shape form names a thing the page holds, and the runtime finds it by shape:

```text
capture the <shape> "<name>" [of <source>] [base64] as <var>
```

| shape | what the runtime finds |
| --- | --- |
| `form field` | the `value` of an HTML input whose `name` is `<name>` |
| `attribute` | the value of an XML or HTML attribute `<name>="..."` |
| `json field` | the value of a JSON string field `"<name>": "..."`, raw or HTML-escaped (`&quot;<name>&quot;:&quot;...&quot;` inside an attribute value) |
| `query parameter` | the value of a URL query parameter `<name>=<value>` (raw `?`/`&` or HTML-escaped `&amp;`) |
| `cookie` | the value of the `<name>` cookie from the response's `Set-Cookie` headers |
| `value after` | on a byte-family step, the run of hex after the hex `<name>` marker, hex-decoded (how a server-assigned port id is pulled out of the hex stream) |
| `input name after` | the tail of an HTML input's name after the prefix `<name>` (how the Next.js Server Action reference index `$ACTION_REF_1` is read from the input's name attribute) |

For a SAML page that hands its request over base64-encoded:

```text
http page
  get "/sso"
  capture the form field "SAMLRequest" as saml_b64
  capture the attribute "ID" of saml_b64 base64 as reqid
  capture the attribute "AssertionConsumerServiceURL" of saml_b64 base64 as acs
```

Line by line: capture the `SAMLRequest` form value as `saml_b64`; then, from its base64-decoded value, capture the `ID` attribute as `reqid` and the `AssertionConsumerServiceURL` attribute as `acs`. `of <source>` names the variable to search (the response by default), and `base64` decodes it first. The author never writes the markup; the shape defines it.

The template form matches literal text with one `<name>` placeholder where the value appears:

```text
capture <var> [from <source>] [base64] where "<text with one <name> placeholder>"
```

The literal text is matched exactly and `<name>` captures whatever appears there. The same example in this form:

```text
capture saml_b64 where "name=\"SAMLRequest\" value=\"<value>\""
capture reqid from saml_b64 base64 where "ID=\"<value>\""
```

The regex form is the same capture with a pattern instead of a template:

```text
capture <var>: <regex> [from <var>] [base64]
```

On `http` the regex runs over the full response text (status line, headers, and body); on `tcp`/`tls`/`websocket` over the received hex, and group 1 is hex-decoded. Later steps use `{var}`. A step may carry several `capture` lines, one value each, applied in order.

All three forms share the source clauses: `from`/`of <var>` runs the pattern over an earlier variable's value instead of this step's response, and `base64` decodes the source first. The source names 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).

## Set

`set:` is a block, one `name: value` per line. The values go to later steps. A later write on the same name wins. A step's own `set` applies to later steps only.

A `set` line can also compute a value from a deterministic data operation, the transform sentence:

```text
set <var> = aesgcm of "<plaintext>" with "<base64-key>"
```

It runs after the step's captures (so the plaintext may reference a value the step just learned, like `{action_id}`), and its result is stored in `<var>` for later steps, exactly like a `set:` value. `aesgcm` seals the plaintext with AES-GCM under the base64 key and stores `base64(iv + ciphertext)` with a 16-byte iv, the encoding Next.js Server Actions use for bound arguments. If a referenced value never arrived (the target did not leak it), the transform is skipped and a later step's assert reports not verified.

## Assert

The assert reads as `expect ...`; each form is one of the five test kinds:

| directive | type | passes when |
| --- | --- | --- |
| `expect "<text>" in the response` | contains | the response contains the exact text |
| `expect the response to match "<regex>"` | regex | the response matches the pattern |
| `expect a flag` | flag | the response contains a flag-shaped string such as `FLAG{...}` |
| `expect the target to call back "<expect>"` | oob | the target contacted the out-of-band listener |
| `expect no response` | empty | the response is empty: the target never answered |

## Target

The target argument is shaped by the solution's protocols: `tcp`, `tls`, and the byte-family services (including `dns` and `snmp`) take `host:port`, `http`, `https`, and `websocket` take a base URL, `process` and `file` ignore it.

## Full example

```text
id: openssl/heartbleed-mem-leak
summary: CVE-2014-0160 Heartbleed
ref: CVE-2014-0160

tls clienthello-heartbeat
  send hex "1603030125010001210303..."
  receive until "0e000000"
  receive 65536 bytes
  expect "0e000000" in the response
  # server completes the handshake

tls malformed-heartbeat
  send hex "1803030003014000"
  receive 70000 bytes
  expect the response to match "18030[123]40"
```

## Result

The runtime prints one line per step (`name PASS|FAIL test`) and a result line: verified when every test passes, not verified when one fails. Parse errors name the line number. The runtime never interprets: the file is what runs.