IPBUF安全漏洞报告
English
CVE-2026-41688 CVSS 7.7 高危

CVE-2026-41688 Wallos SSRF漏洞(DNS重绑定)

披露日期: 2026-05-07

漏洞信息

漏洞编号
CVE-2026-41688
漏洞类型
SSRF (服务端请求伪造)
CVSS评分
7.7 高危
攻击向量
网络 (AV:N)
认证要求
低权限 (PR:L)
用户交互
无需交互 (UI:N)
影响产品
Wallos

相关标签

SSRFDNS RebindingWallosTOCTOUSubscription Tracker

漏洞概述

Wallos个人订阅追踪器在4.8.4及之前版本中存在SSRF漏洞。尽管开发者尝试通过gethostbyname()验证webhook URL,但在11个出站HTTP端点中有10个未使用CURLOPT_RESOLVE进行IP固定。这种不完整的修复导致了DNS重绑定TOCTOU竞态窗口,攻击者可利用该漏洞绕过安全验证,致使服务器向内部网络发起恶意请求,目前尚无公开补丁。

技术细节

该漏洞源于SSRF修复不完整导致的TOCTOU(Time-of-Check to Time-of-Use)竞态条件。Wallos在验证阶段使用gethostbyname()解析目标域名并检查IP地址合法性,若非内网地址则放行。然而,在后续的请求阶段,代码直接将原始主机名传递给cURL库,且未利用CURLOPT_RESOLVE选项将IP固定。关键问题在于,攻击者若控制恶意DNS服务器,可在验证通过后、cURL实际发起请求前的短暂时间窗口内,迅速更改DNS解析记录,将域名指向内网敏感地址(如127.0.0.1或云元数据服务IP)。由于cURL会重新解析或接收新的DNS响应,服务器最终向内网发起了未授权的请求,从而绕过了旨在阻断SSRF攻击的IP校验逻辑。

攻击链分析

STEP 1
步骤1
攻击者准备恶意域名,并控制其DNS服务器。
STEP 2
步骤2
攻击者向Wallos提交Webhook URL,指向该恶意域名。
STEP 3
步骤3
Wallos验证阶段,DNS解析为合法公网IP,通过校验。
STEP 4
步骤4
攻击者迅速修改DNS记录,将域名解析指向内网IP(如127.0.0.1)。
STEP 5
步骤5
Wallos发起请求阶段,cURL重新解析(或使用新记录)连接内网地址。
STEP 6
步骤6
服务端向内网发起请求,导致SSRF攻击成功。

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
# Conceptual PoC demonstrating the DNS Rebinding attack vector # Attacker needs to control the DNS server for 'evil.com' import socket import time # Simulation of the vulnerable backend logic def vulnerable_backend_logic(target_domain): print(f"[Backend] Processing webhook for: {target_domain}") # Step 1: Time-of-Check (TOC) # Using gethostbyname() to validate the IP try: initial_ip = socket.gethostbyname(target_domain) print(f"[Backend] Validation check: {target_domain} resolves to {initial_ip}") # Check if IP is private (simplified check) if initial_ip.startswith("127.") or initial_ip.startswith("192.168."): print("[Backend] Validation Failed: Private IP detected.") return print("[Backend] Validation Passed: Public IP allowed.") except socket.gaierror: print("[Backend] DNS resolution failed.") return # TIMING WINDOW: Attacker switches DNS record here (e.g., to 127.0.0.1) # In a real attack, this is where the race condition happens. print("[Simulated Delay] ... Attacker switches DNS record ...") time.sleep(1) # Forcing a delay to simulate the gap # Step 2: Time-of-Use (TOU) # Vulnerable code passes 'target_domain' to cURL without CURLOPT_RESOLVE # Here we simulate the cURL request by resolving again try: # In the real vulnerability, cURL might resolve again or use a different cache final_ip = socket.gethostbyname(target_domain) print(f"[Backend] cURL Request sent to: {final_ip}") if final_ip != initial_ip: print(f"[EXPLOIT SUCCESS] DNS Rebinding occurred! Request hit {final_ip} instead of {initial_ip}") else: print("[Safe] IP remained consistent.") except socket.gaierror: print("[Backend] DNS resolution failed during request.") # To test this, one would need to manipulate the local hosts file or run a custom DNS server # that changes the response between the two gethostbyname calls. if __name__ == "__main__": # This is a demonstration of the logic gap, not a functional exploit against a live target # without a controlled DNS environment. pass

影响范围

Wallos <= 4.8.4

防御指南

临时缓解措施
目前无补丁,建议暂时禁用Webhook功能或严格限制服务器出站网络连接,隔离应用服务器与敏感内网资源。

参考链接