刷题记录:[LCTF]bestphp's revenge

刷题记录:[LCTF]bestphp's revenge

题目复现链接:https://buuoj.cn/challenges
参考链接:https://xz.aliyun.com/t/3341#toc-22
从LCTF WEB签到题看PHP反序列化
LCTF2018-bestphp's revenge 详细题解

这是LCTF的web签到题??打扰了。现在一天一题已经有点跟不上了。。。。

一、知识点

这几个知识点环环相扣形成利用链,所以我一起讲了
session反序列化->soap(ssrf+crlf)->call_user_func激活soap类

1、SoapClient触发反序列化导致ssrf

2、serialize_hander处理session方式不同导致session注入

3、crlf漏洞

二、解题思路

首先,php反序列化没有可利用的类时,可以调用php原生类,参考
反序列化之PHP原生类的利用,
贴上源码和poc讲

//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!

很容易想到f传入extract覆盖b为我们想要的函数,问题是后面session的利用。
先说SoapClient,参考从几道CTF题看SOAP安全问题

SOAP(简单对象访问协议)是连接或Web服务或客户端和Web服务之间的接口。
其采用HTTP作为底层通讯协议,XML作为数据传送的格式
SOAP消息基本上是从发送端到接收端的单向传输,但它们常常结合起来执行类似于请求 / 应答的模式。

那么如果我们能通过反序列化调用SoapClientflag.php发送请求,那么就可以实现ssrf

接下要解决的问题是:

  • 在哪触发反序列化
  • 如何控制反序列化的内容

这里要知道call_user_func()函数如果传入的参数是array类型的话,会将数组的成员当做类名和方法,例如本题中可以先用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()从而发送请求

SoapClient的内容怎么控制呢,贴上大佬的poc

<?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;

这里又涉及到crlf,参考[转载]CRLF Injection漏洞的利用与实例分析,我的理解是因为http请求遇到两个\r\n%0d%0a,会将前半部分当做头部解析,而将剩下的部分当做体,那么如果头部可控,就可以注入crlf实现修改http请求包。如果我的理解有错,请大佬指正。

这个poc就是利用crlf伪造请求去访问flag.php并将结果保存在cookie为PHPSESSID=tcjr6nadpk3md7jbgioa6elfk4的session中。

最后一点,就是如何让php反序列化结果可控。这里涉及到php反序列的机制。

php中的session中的内容并不是放在内存中的,而是以文件的方式来存储的,存储方式就是由配置项session.save_handler来进行确定的,默认是以文件的方式存储。
存储的文件是以sess_sessionid来进行命名的,文件的内容就是session值的序列话之后的内容。
在php.ini中存在三项配置项:

session.save_path=""   --设置session的存储路径
session.save_handler="" --设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式)
session.serialize_handler   string --定义用来序列化/反序列化的处理器名字。默认是php(5.5.4后改为php_serialize)

PHP内置了多种处理器用于存储$_SESSION数据时会对数据进行序列化和反序列化,常用的有以下三种,对应三种不同的处理格式:

处理器 对应的存储格式
php 键名 + 竖线 + 经过serialize()函数反序列化处理的值
php_binary 键名的长度对应的ASCII字符 + 键名 + 经过serialize()函数反序列化处理的值
php_serialize(php>=5.5.4) 经过serialize()函数反序列处理的数组

配置选项 session.serialize_handler,通过该选项可以设置序列化及反序列化时使用的处理器。

如果PHP在反序列化存储的$_SEESION数据时的使用的处理器和序列化时使用的处理器不同,会导致数据无法正确反序列化,通过特殊的伪造,甚至可以伪造任意数据。

当存储是php_serialize处理,然后调用时php去处理,如果这时注入的数据时a=|O:4:"test":0:{},那么session中的内容是a:1:{s:1:"a";s:16:"|O:4:"test":0:{}";},那么a:1:{s:1:"a";s:16:"会被php解析成键名,后面就是一个test对象的注入。

正好我们一开始的call_user_func还没用,可以构造session_start(['serialize_handler'=>'php_serialize'])达到注入的效果。

三、解题步骤

先注入poc得到的session

触发反序列化使SoapClient发送请求

携带poc中的cookie访问即可得到flag

posted @ 2019-09-12 22:57  MustaphaMond  阅读(3136)  评论(9编辑  收藏  举报