反序列化字符逃逸

@TOC

反序列化特点

1、php在反序列化时,底层代码是以;作为字段的分隔,以}作为结尾,并且是根据长度判断内容的,同时反序列化的过程中必须严格按照序列化规则才能成功实现反序列化规则才能成功。例如:

1
2
3
4
5
6
7
8
9
10
class A{
public $name='shy';
public $pass='123456';
}

$lemon = new A();
echo serialize($lemon);
#反序列化后的结果为:
O:1:"A":2:{s:4:"name";s:3:"shy";s:4:"pass";s:6:"123456";}

2、在反序列化过程中长度不对应会报错
在反序列化的时候php会根据s所指定的字符长度去读取后边的字符。如果指定的长度错误则反序列化就会失败。

1
2
3
<?php
$str='O:1:"A":2:{s:4:"name";s:3:"shy";s:4:"passs";s:6:"123456";}';
var_dump(unserialize($str));

结果是bool(false)报错
3、可以反序列化类中不存在的元素

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

最后结果会出现gender,值为boy。

反序列化字符逃逸

一、本质:闭合
二、分为两种情况:1、字符变多;2、字符变少。
三、字符变多举例

1
2
3
4
5
6
7
8
9
10
11
12
<?php
function filter ($string) {
$filter =.'/p/i ' ;
return preg replace ($filter, 'WW',$str)
}
$username · =. 'purplet'; #pppppppppppppppp";
$age· =. "10";
$user· =·array ($username,$age) ;
var dump (serialize ($user));
echo "<pre>";
$r = filter (serialize($user));var_dump ($r);
Lvar dump(unserialize ($r));

这里是一个p换成两个w,结果会报错
在这里插入图片描述
这时候我要改变age的值,就需要利用反序列化字符逃逸,用16个p填充user,然后在后面加上”;i:1;s:2:”20”;}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
//过滤函数
function filter($string)
{
$filter = '/p/i';
//正则匹配,表示变量string中的p(由filter决定)会被替换成WW
return preg_replace($filter,'WW',$string);
}
$username = 'purplet';
$age = "20";
$user = array($username,$age);//数组
//序列化后的数值输出
var_dump(serialize($user));
echo "<pre>";//分隔符
//序列化过滤后的数值赋值给r并输出
$r = filter(serialize($user));
var_dump($r);
//对 序列化过滤后的数值 反序列化
var_dump(unserialize($r));
?>

2、字符减少
与增多不同的是,不仅要构造username,还要构造age。
第一步:改变age的值,序列化后为;i:1;s:2:"20"},符,而此时uesrname的值无法确定,可随意构造
第二步:age处传递一个任意数值和双引号进行闭合,即再次传入age = A”;i:1;s:2:”20”;},查看结果
第三步:计算选中部分(长度为13)根据过滤后字符缩减情况构造,Demo中每两个p变为1个W,相当于逃逸1位(选中部分即为逃逸字符)所以输入13*2=26个p进行逃逸,即最终传递usernmae=pppppppppppppppppppppppppp,age=A”;i:1;s:2:”20”;}

例题

题目代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
error reporting(O);class a
{
public $uname = '123";
public $password = 'yu22x';
function filter($string){
return str replace('Firebasky','Firebaskyed'}
}
$a = new a();
var dump(serialize($a));
echo "<pre>";
$r = filter(serialize($a));vardump($r);
var dump(unserialize($r));
?>

此题要求以get的方式上传password为yussy,但是password无法修改

在这里插入图片描述

在这里插入图片描述