Cyberpunk Terminal
A synthetic network drawn from a seed: six hosts with addresses, open ports, service banners, accounts and files, all generated. You explore it from a prompt with a bounded set of 14 commands, and three missions, chosen by that same seed from a pool of six, ask you to work something out from what you find, by taking the evidence to this site's own tools and chaining them.
Nothing here touches the network. The in-game dig resolves against the generated topology and nothing else, so a name you type is never sent to a resolver, and every input is fictional.
Rendered when the site was built, by the same core the browser runs, with seed pah.moi. With JavaScript this becomes a live prompt that keeps a session, unlocks files and checks your answers. Without it, the command reference and all three briefings are still here, evidence and all, and every mission can be solved by hand with the tools it names.
The command set
| Command | What it does |
|---|---|
| help | List every command this terminal understands. |
| man <command> | Show one command's usage and what it does. |
| net | The site prefix, the segment size it is cut into, and the gateway. |
| scan | List every host: name, address and role. |
| dig <name> | Resolve a name in the synthetic zone. Nothing leaves your browser. |
| ports <host> | Open ports on a host, with the banner each service announces. |
| head <host> | The HTTP response headers a host serves, ready to paste into /headers. |
| users <host> | Local accounts on a host, with their comment fields. |
| login <host> <user> <password> | Authenticate to a host for this session, unlocking its restricted files. |
| ls <host> | Files on a host; restricted ones are marked. |
| cat <host>:<file> | Print a file. Restricted files need a login first. |
| missions | List the missions and which are solved. |
| mission [n] | Show the current mission's briefing, or switch to mission n. |
| submit <answer> | Check an answer against the current mission. |
The network at seed pah.moi
Site prefix 10.37.98.0/24, cut into /29 segments, gateway 10.37.98.1.
| Host | Address | Role | Open ports |
|---|---|---|---|
| gw01 | 10.37.98.1 | gateway | 22, 179, 11211 |
| front01 | 10.37.98.170 | web | 22, 80, 443, 6379 |
| api01 | 10.37.98.122 | app | 22, 80, 443, 8080, 9200 |
| pg01 | 10.37.98.234 | db | 22, 5432 |
| hop01 | 10.37.98.93 | jump | 22 |
| bak01 | 10.37.98.58 | backup | 22, 80, 443, 873 |
Mission 1: A credential nobody had to steal
The backup host carries a second signed credential, sitting in a file that needs no login at all: anyone who can list the host can read it. Decode it, and submit the whole number of days between its issue and its expiry.
- Chains
- /jwt then /epoch
- Route
- cat <backup host>:rotation-token.txt
- Answer
- a whole number of days, e.g. 42 days
# backup rotation credential: rotated automatically, no login required to read ROTATION_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJhY2t1cC0yMDI0In0.eyJpc3MiOiJncmlkLWJhY2t1cCIsInN1YiI6InN2Yy1yZXBvcnQiLCJhdWQiOiJpbnRlcm5hbC1hcGkiLCJpYXQiOjE3MTAwMDAwMDAsImV4cCI6MTcxNjQ3NDA5N30.4ExUWK6_vQ_18S_7bm5861gVVxqpCS7JZDxqwEBLiR4 ROTATION_TOKEN_SIGNING_KEY=grid-backup-rotation-signing-key
Mission 2: The file that lies about what it is
One file left this network overnight. The transfer log records a great many ordinary session events and exactly one file actually sent. Find that line, read the file it names, and identify what the bytes really are. Submit the media type, not the extension: the extension is the claim being tested.
- Chains
- /grep then /magic-bytes
- Route
- cat <db host>:transfer.log → cat <db host>:<the file the log names>
- Answer
- a media type, e.g. application/zip
2024-03-11T02:14:07Z sftp[2291]: session opened for local user svc-report 2024-03-11T02:14:07Z sftp[2291]: opendir "/srv/reports" 2024-03-11T02:14:08Z sftp[2291]: closedir "/srv/reports" 2024-03-11T02:14:19Z sftp[2291]: open "/srv/reports/index.csv" flags READ mode 0666 2024-03-11T02:14:19Z sftp[2291]: close "/srv/reports/index.csv" 2024-03-11T02:15:02Z sftp[2291]: stat "/srv/reports/archive" 2024-03-11T02:15:03Z sftp[2291]: opendir "/srv/reports/archive" 2024-03-11T02:15:04Z sftp[2291]: closedir "/srv/reports/archive" 2024-03-11T02:16:41Z sftp[2291]: open "/srv/reports/notes.txt" flags READ mode 0666 2024-03-11T02:16:41Z sftp[2291]: close "/srv/reports/notes.txt" 2024-03-11T02:17:12Z sshd[2288]: subsystem request for sftp by user svc-report 2024-03-11T02:17:44Z sftp[2291]: sent "/srv/reports/board-minutes.pdf" bytes 4718592 2024-03-11T02:17:45Z sftp[2291]: close "/srv/reports" 2024-03-11T02:17:46Z sftp[2291]: session closed for local user svc-report --- board-minutes.pdf --- first 32 bytes of board-minutes.pdf, as spaced hex: 7F 45 4C 46 02 01 01 00 00 00 00 00 00 00 00 00 02 00 3E 00 01 00 00 00 50 10 40 00 00 00 00 00
Mission 3: The door that was left open
Three hosts on this network answer HTTP. Exactly one of them serves a response with no Strict-Transport-Security header, so its traffic can be downgraded to plain HTTP. Find that host, then work out which /29 segment its address falls in and submit that segment's network address. Note that the weak block also pairs `Access-Control-Allow-Origin: *` with `Access-Control-Allow-Credentials: true`, which the Fetch standard rejects, but the HTTP Response Header Analyzer does not check CORS, that one is yours to spot.
- Chains
- /headers then /subnet
- Route
- scan → head <host> (for each host that answers HTTP)
- Answer
- a dotted-quad network address, e.g. 10.0.0.8
--- front01 (10.37.98.170) --- HTTP/1.1 200 OK Server: nginx/1.24.0 Content-Type: text/html; charset=utf-8 Content-Security-Policy: default-src 'self' X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=() Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true --- api01 (10.37.98.122) --- HTTP/1.1 200 OK Server: Caddy Content-Type: application/json Strict-Transport-Security: max-age=31536000 Content-Security-Policy: default-src 'none' X-Content-Type-Options: nosniff X-Frame-Options: DENY Referrer-Policy: no-referrer Permissions-Policy: interest-cohort=() --- bak01 (10.37.98.58) --- HTTP/1.1 200 OK Server: nginx/1.24.0 Content-Type: text/html; charset=utf-8 Strict-Transport-Security: max-age=63072000; includeSubDomains; preload Content-Security-Policy: default-src 'self'; frame-ancestors 'none' X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=(), geolocation=(), microphone=()
About this toy1 paragraph
The pool of six missions chains six tools between them, two per mission: the HTTP Response Header Analyzer, the Subnet Calculator, the JWT Decoder, the Epoch Converter, Client-Side Grep, and File Header Forensics. Every answer is genuinely what it claims to be: the digests, header layouts and file signatures here are computed by the same code those tools run, not written out by hand: true of the three missions a seed happens to draw, and of the three it doesn't.
hack-terminal-cli from the site's source with:
cargo build --release --bin hack-terminal-cliSource and licence terms