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.
The file
- A solution is a text file with a
.txtextension. - Lines starting with
#are comments and are ignored. - Blank lines separate sections. Two-space indentation marks a block.
- A value is the rest of the line after
name:; surrounding quotes are stripped. Quotes are optional, and are only needed when a value contains#or leading or trailing spaces. - Field names are lowercase with underscores. 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 fields.
<identity> <step-name>
<field>: <value>
assert: <type> "<expect>"
The identity and the step name are always required. Step names are unique within the file.
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:
| identity | required fields | other fields |
|---|---|---|
http, https | encoding | payload, path, headers, capture |
websocket | send | recv, recv_until, timeout, close, capture |
tcp, tls, ssh, ftp, smtp, ldap, redis, mysql, postgresql, smb, telnet | send | recv, recv_until, timeout, close, capture |
dns, snmp | send | recv, recv_until, timeout, close, capture |
process | command | cwd |
file | op, path | data |
http, https
encoding is one of query, form, json, raw-json, plain. payload is the value to send. path appends to the target URL. headers is a block, one name: value per line. The target is a base URL; https is the same exchange over TLS, and the http identity also covers both schemes.
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
send is the text payload, wrapped into a masked text frame with the length computed by the runtime. The hex "..." form does not apply here; the payload is sent as-is. recv is the most bytes to read. recv_until stops the read at a hex marker. timeout is seconds. close: true ends the session after the step. The connection stays open across steps.
tcp, tls, and the byte-family services
send is the bytes to send, written as hex; it may be empty when a step only reads. Instead of hand-computed hex you can write the payload as text: send: hex "HELO x\r\n" hex-encodes the quoted text, with the usual \r, \n, \t, \", \\ escapes plus \xHH for one arbitrary byte, so control characters stay visible. recv is the most bytes to read. recv_until stops the read at a hex marker. timeout is seconds. close: true ends the session after the step. The connection stays open across steps. 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
command is the command, words separated by spaces. cwd sets the working directory.
file
op is read or write. path is the file path. data is the bytes to write, required when op is write.
Capture
capture <var>: <regex> pulls a value out of the response for later steps. 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}.
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.
Assert
assert: <type> "<expect>" is the pass or fail test on the response:
| type | expect | passes when |
|---|---|---|
contains | required | the response contains the exact text |
regex | required | the response matches the pattern |
flag | none | the response contains a flag-shaped string such as FLAG{...} |
oob | required | the target contacted the out-of-band listener |
empty | none | the response is empty: the target never answered |
An optional note: line explains what passing means.
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
id: openssl/heartbleed-mem-leak
summary: CVE-2014-0160 Heartbleed
ref: CVE-2014-0160
tls clienthello-heartbeat
send: 1603030125010001210303...
recv_until: 0e000000
recv: 65536
assert: contains "0e000000"
meaning: server completes the handshake
tls malformed-heartbeat
send: 1803030003014000
recv: 70000
assert: regex "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.