发布于3小时前3小时 背景在一次已授权的内网渗透测试中,目标系统采用 Nginx 反向代理加后端 Java 服务的部署方式。外部请求经过统一认证网关后,由网关注入用户身份头,后端再根据来源地址和请求头判断是否允许访问管理接口。这类架构本身没有问题,风险通常出在两个信任假设:后端端口不会被直接访问,以及客户端无法伪造网关注入的请求头。一旦网络边界或代理配置不符合假设,就可能形成“直连后端—伪造身份—访问管理接口”的完整攻防链路。以下操作仅用于明确授权范围内的漏洞验证。验证目标应使用测试账号,并优先采用只读接口,避免调用配置修改、任务执行和数据删除功能。问题定位扫描内网网段时发现,同一台主机同时开放 443 和 8080 端口。443 返回统一登录页面,8080 则直接响应后端服务。两个端口访问同一路径时状态码不同:curl -k -i curl -i # 对比响应头和响应体摘要 curl -ksS -D 443.headers -o 443.body curl -sS -D 8080.headers -o 8080.body sha256sum 443.body 8080.body443 返回 302 并跳转到认证中心,8080 返回 403,说明后端仍有访问控制,但该控制未必与网关认证完全等价。随后检查 403 响应中的错误信息以及前端静态资源,发现后端可能读取 X-Forwarded-For 和 X-Forwarded-User。技术分析问题通常由三层配置共同造成:网络层:后端监听 0.0.0.0:8080,且防火墙允许普通办公网或其他业务网段直接访问。代理层:Nginx 直接透传客户端提交的身份头,没有先清空再重写。应用层:后端把 X-Forwarded-User 当作认证结果,或仅根据 X-Forwarded-For 是否属于内网地址决定管理权限。需要区分“真实客户端地址恢复”和“身份认证”两个概念。X-Forwarded-For 只能作为代理链路中的地址信息,不能单独承担身份认证。X-Forwarded-User 也只有在后端确认请求确实来自受信代理,并且代理会覆盖同名头时才可使用。最小化漏洞验证先选择不会改变系统状态的接口,例如当前用户信息、版本信息或健康检查。不要一开始就测试命令执行、文件上传或配置写入。# 基线:不带任何代理头 curl -i # 检查后端是否错误信任来源地址头 curl -i \ -H 'X-Forwarded-For: 127.0.0.1' # 检查后端是否错误信任网关注入的用户头 # 测试时使用专门创建的低权限测试账号,不冒用真实管理员 curl -i \ -H 'X-Forwarded-For: 127.0.0.1' \ -H 'X-Forwarded-User: pentest-readonly' # 只记录状态码、响应大小和耗时,减少敏感数据落盘 curl -sS -o /dev/null \ -w 'code=%{http_code} size=%{size_download} time=%{time_total}\n' \ \ -H 'X-Forwarded-For: 127.0.0.1' \ -H 'X-Forwarded-User: pentest-readonly'如果基线请求返回 401 或 403,而加入可控请求头后返回 200,且响应内容体现了测试账号身份,就可以确认存在信任边界错误。若只有同时伪造两个头才能成功,应在报告中保留这一条件,不能笼统描述为“未授权访问”。排除误判确认响应来自真实后端,而不是 WAF、自定义错误页或缓存节点。可对比 Server、Date、请求标识和响应体哈希。更换不存在的用户名重复测试。如果任意用户名都返回相同内容,可能只是公开接口或固定模拟数据。检查 Cookie、客户端证书和已有代理环境变量,避免测试终端残留的认证状态影响结果。分别测试直连后端和经过代理的路径,记录每一步的源地址、Host、状态码和请求标识。只证明权限边界被突破即可,不继续访问业务数据或执行管理动作。Nginx 配置排查下面是一类常见的不安全配置。它把客户端提供的 X-Forwarded-User 原样传给后端,并使用可追加的 X-Forwarded-For:location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-User $http_x_forwarded_user; proxy_pass }身份头应由认证网关生成,而不是从客户端请求中读取。后端也不应暴露给非代理网段。可按实际认证模块调整为以下思路:# 仅信任明确的上游负载均衡地址,不要直接信任整个办公网段 set_real_ip_from 10.20.10.11; set_real_ip_from 10.20.10.12; real_ip_header X-Forwarded-For; real_ip_recursive on; location / { # 覆盖客户端同名头 proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Proto $scheme; # $authenticated_user 应来自已经完成校验的认证模块 proxy_set_header X-Forwarded-User $authenticated_user; proxy_pass }如果代理链路包含多级负载均衡,不能简单相信最左侧或最右侧地址。需要明确每一级由谁追加、谁覆盖,并只把固定代理节点加入信任列表。网络与主机侧验证在服务器上确认后端监听地址和防火墙范围。若服务只供本机 Nginx 使用,优先绑定回环地址;若使用容器或独立代理节点,则只允许代理网段访问。# 查看监听进程和地址 ss -lntp | grep ':8080' # 查看主机防火墙规则 nft list ruleset # 示例:后端只监听本机 server.address=127.0.0.1 server.port=8080 # 若必须跨主机访问,可仅允许代理节点连接 nft add rule inet filter input ip saddr { 10.20.10.11, 10.20.10.12 } tcp dport 8080 accept nft add rule inet filter input tcp dport 8080 drop生产环境修改 nftables 前应先导出现有规则,并确认控制台或带外管理可用,防止错误规则导致远程失联。容器环境还要同时检查宿主机端口映射、云安全组和 Kubernetes Service 类型。日志留痕与关联分析只记录 X-Forwarded-For 而不记录 TCP 对端地址,会让伪造请求看起来像来自本机。建议同时记录代理实际看到的连接地址、恢复后的客户端地址、身份头和请求标识:log_format boundary '$time_iso8601 request_id=$request_id ' 'peer=$remote_addr realip=$realip_remote_addr ' 'xff="$http_x_forwarded_for" user="$http_x_forwarded_user" ' 'host="$host" request="$request" status=$status bytes=$body_bytes_sent'; access_log /var/log/nginx/access_boundary.log boundary;排查时可围绕测试时间窗口和请求标识进行关联:grep 'request_id=已记录的请求标识' /var/log/nginx/access_boundary.log journalctl -u backend.service --since '2025-03-08 10:00:00' --until '2025-03-08 10:10:00' # 查找“连接来源不是代理节点,但携带身份头”的可疑请求 awk '$0 !~ /peer=10\.20\.10\.(11|12)/ && $0 ~ /user="[^"]+"/' \ /var/log/nginx/access_boundary.log日志中涉及用户标识、内部地址和接口参数时,应设置访问权限、留存周期和脱敏策略。不要为了排查方便记录完整 Authorization、Cookie 或口令字段。风险判定验证结果风险判断建议优先级后端可直连,但仍执行独立强认证暴露面扩大,暂未形成认证绕过中伪造来源地址头可访问管理接口基于来源的访问控制失效高伪造用户头可获得任意用户身份身份认证边界失效严重绕过后可调用写接口或任务执行接口可能进一步导致数据篡改或主机失陷严重,立即处置修复建议优先从网络层收敛后端端口,只允许反向代理或服务网格节点访问。代理必须删除或覆盖客户端提交的身份相关请求头,不能使用 $http_x_forwarded_user 作为认证结果。应用侧验证请求来源,必要时使用双向 TLS、短时签名令牌或服务身份,而不是只判断 IP。管理接口继续执行应用自身的认证和授权检查,不把“来自内网”视为管理员身份。对 X-Forwarded-For、Forwarded、X-Real-IP 等头建立统一解析规则,限制受信代理数量和地址。同时记录 TCP 对端地址、代理解析后的地址和请求标识,便于发现绕过代理的直连请求。修复后重复执行基线、伪造单个头、组合伪造以及正常代理访问四组回归测试。总结这类问题不是单一 Header 漏洞,而是网络暴露、代理配置和应用鉴权共同形成的权限边界缺口。有效的渗透验证应还原完整链路,明确成功条件,并用只读请求证明影响。修复时也不能只在应用里增加一个来源判断,而应同时收敛后端端口、覆盖客户端身份头、强化服务间认证并补齐日志字段。 服务器、云资产与权限边界梳理示意 网络请求、日志与边界流量分析示意
3小时前3小时 这类绕过的关键通常不是“代理隐藏了来源”,而是后端把客户端可控的转发头当成了可信身份。建议先确认管理接口究竟读取了哪个值:连接源地址、X-Forwarded-For、X-Real-IP,还是框架解析后的 remote_addr。 可以直接做一组差分请求,观察伪造头是否改变鉴权结果: curl -i curl -i \ -H 'X-Forwarded-For: 127.0.0.1' \ -H 'X-Real-IP: 127.0.0.1' \ -H 'Forwarded: for=127.0.0.1' curl -i \ -H 'X-Forwarded-For: 127.0.0.1, 203.0.113.10' 如果第二组或不同顺序的地址链能通过,重点检查代理是否直接透传了用户提交的头。Nginx 边界层应覆盖,而不是追加不受信任的原始值: location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header Forwarded ""; proxy_pass } 如果确实存在多级代理,则只信任明确的代理网段,不要配置成 0.0.0.0/0: set_real_ip_from 10.20.0.0/16; set_real_ip_from 192.0.2.10/32; real_ip_header X-Forwarded-For; real_ip_recursive on; 这里还要注意:real_ip_recursive on 会从地址链右侧逐级剥离可信代理,CIDR 配宽后反而会扩大绕过面。建议临时记录原始连接地址和各转发头,核对每一跳实际写入情况: log_format trust_debug '$remote_addr realip=$realip_remote_addr ' 'xff="$http_x_forwarded_for" xri="$http_x_real_ip" ' 'fwd="$http_forwarded" request="$request" status=$status'; access_log /var/log/nginx/trust_debug.log trust_debug; 修复层面不建议继续用“来源 IP 属于内网”作为管理接口的唯一鉴权。更稳妥的是入口限制叠加独立身份认证,例如 mTLS、短期令牌或统一认证;后端同时限制只能由反向代理所在网段访问,避免攻击者绕过代理直连服务端口。验证修复时还应覆盖 IPv6 回环、IPv4 映射地址以及不同头格式,例如 ::1、::ffff:127.0.0.1、重复的 X-Forwarded-For 头,防止标准化差异造成二次绕过。
创建帐户或登录后发表意见