Typecho SSRF漏洞分析与利用

之前有个朋友问了我一句玩儿过Typecho SSRF没有,肯定没玩儿过呀!第二天就在网上找到一个相关的文章,然后呢,就有先记录下来,大家边看边学习一下吧!转至互联网,因为不知道原创作者是谁了,我是从一个大的平台看到的,但是别人也有人在博客里早些时候就发表过。

原文:

Google了下Typecho SSRF关键字,发现和WordPress一样,XMLRPC也存在同样的SSRF问题。自己博客也是使用Typecho,所以就分析一下,顺便自己再打个PATCH。

Author: JoyChou@Meili-inc Mail: viarus#qq.com  Date: 20171107

1. 前言

Google了下Typecho SSRF关键字,发现和WordPress一样,XMLRPC也存在同样的SSRF问题。自己博客也是使用Typecho,所以就分析一下,顺便自己再打个PATCH。

本文所有测试均在以下测试环境:

  • Typecho 1.0 (14.10.10)
  • CentOS 7
  • libcurl/7.29.0
  • Redis server v=3.2.10
  • PHP 5.4.16 (fpm-fcgi)

2. 漏洞原理

XMLRPC这个接口在Typecho 1.0版本中,默认有该功能,并无设置选项。后面的1.1版本有设置该选项的功能。

XMLRPC里的Pingback协议,很多人可能不知道 Pingback 协议是干嘛的。我在这里简单解释下,这个协议诞生在Web 2.0概念诞生之初,由于在互联网世界各个博客站点之间是独立的存在,而它们之间又经常存在互相引用的情况。作为一个原创博主,我是无法知道我这篇文章被哪些站点引用过的,因此Pingback协议就是为了解决这个问题存在的。

当你在写的文章发表后,如果文中引用了某个链接,系统会自动向那个链接发一个PING,告诉对方我引用了这篇文章,地址是: xxx。对方收到这个PING以后会根据你给的原文地址回去检验一下是否存在这个引用,这就是一次BACK。检验完以后,会把这次引用记录下来,大家经常在Typecho或者WordPress之类博客评论列表里看到的引用记录,就是这么来的。

而造成这个漏洞的关键就在于这个BACK过程,该过程会用cURL或者socket请求原文地址,由于未做任何限制,导致SSRF。

2.1 代码分析

漏洞URL:http://localhost/action/xmlrpc。POST提交以下Payload:

收到源地址服务器错误这样的错误返回。

代码里搜索源地址服务器错误,发现只有var/Widget/XmlRpc.php文件里有,这就能确定案发现场了。只需要看懂public function pingbackPing($source, $target)函数即可,该函数的$source参数为http://127.0.0.1:2222$target为joychou

先调用Typecho_Http_Client类的get方法,返回 发起HTTP请求的类。如果失败,直接返回错误,整个调用结束。

XmlRpc.php

get方法代码如下,功能为,从Client/Adapter/目录中,添加两个发起HTTP请求的类,一个是Curl,另一个是Socket。如果Curl可用,就用Curl,否则用fsockopen。

var/Typecho/Http/Client.php

回到XmlRpc.php,$http->setTimeout(5)->send($source);该行代码用上面返回的HTTP类调用send方法发起HTTP请求。具体发起请求的代码var/Typecho/Http/Client/Adapter/Curl.php

由于是cURL造成的SSRF,利用姿势就比较多了。还有Socket.php也会造成SSRF。

2.2 代码整体逻辑

  1. 程序写了两种发起HTTP请求的方式,Curl和fsockopen,Curl如果可用,优先选择使用
  2. 如果cURL返回失败或者返回成功后但状态码不是200,返回源地址服务器错误
  3. 如果cURL返回成功,并且状态码为200,如果没有x-pingback头,返回源地址不支持PingBack,如果有x-pingback头,就继续往下判断。

3. 漏洞利用

3.1 端口探测

所以,可以根据返回码,我们可以来探测端口。

  • 返回源地址服务器错误,端口不开启。
  • 返回源地址不支持PingBack或者其他错误,端口开启。

3.1.1 探测Redis端口

返回:

所以,这就很尴尬,php curl对http://127.0.0.1:6379发起请求,返回true,但是状态码返回不是200。导致输出的也是源地址服务器错误。所以应该就只能探测WEB端口了。类似Redis、FastCGI、Struts2就盲打吧…

而且用时间差测试,端口是否有无,时间差几乎一样。

3.1.2 探测Web服务

python开一个2222的Web服务python -m SimpleHTTPServer 2222

payload:

返回源地址不支持PingBack,说明端口开启。

3.2 攻击Redis

EXP中由于带有&字符,需要使用CDATA。

3.3 攻击FastCGI

3.3.1 利用条件

  • libcurl版本>=7.45.0
  • PHP-FPM监听端口
  • PHP-FPM版本 >= 5.3.3
  • 知道服务器上任意一个php文件的绝对路径

由于EXP里有%00,CURL版本小于7.45.0的版本,gopher的%00会被截断。官方版本升级日志中有提到如下内容:

Fixed in 7.45.0 - October 7 2015

gopher: don't send NUL byte

3.3.2 转换为Gopher的EXP

监听一个端口的流量 nc -lvv 2333 > 1.txt,执行EXP,流量打到2333端口

urlencode

得到gopher的EXP

执行EXP

4. 修复

4.1 热修复

  • 如果不使用XMLRPC的pingback接口,可将/action/xmlrpc接口用Nginx处理下。if ($uri ~ ^/action/xmlrpc$) {return 403;}
  • WAF拦截

4.2 代码修复

最后官方很快给出了修复代码,修复的方式是限制IP为内网IP,并且协议限制为HTTPS/HTTP。

aimorc

我还没有学会写个人说明!

Leave a Reply

Your email address will not be published. Required fields are marked *

微信扫一扫,分享到朋友圈

Typecho SSRF漏洞分析与利用
返回顶部

显示

忘记密码?

显示

显示

获取验证码

Close