php反序列化魔术方法
__construct(),类的构造函数 new的时候触发
__destruct(),类的析构函数 销毁对象的或者serialize时候触发
__call(),在对象中调用一个不可访问方法时调用
__callStatic(),用静态方式中调用一个不可访问方法时调用
__get(),获得一个类的成员变量时调用 当访问一个对象不存在的变量时就会被触发
__set(),设置一个类的成员变量时调用 当给一个对象不存在的变量赋值时就会被触发
__isset(),当对不可访问属性调用isset()或empty()时调用
__unset(),当对不可访问属性调用unset()时被调用 当尝试使用unset() 销毁函数去销毁一个不可访问的成员属性时会触发,不可访问(包括私有成员属性,不存在的成员属性)
__sleep(),执行serialize()时,先会调用这个函数 当对象被serialize 序列化时触发调用
__wakeup(),执行unserialize()时,先会调用这个函数 当进行unserialize 反序列化对象时
__toString(),类被当成字符串时的回应方法
__invoke(),调用函数的方式调用一个对象时的回应方法
__set_state(),调用var_export()导出类时,此静态方法会被调用
__clone(),当对象复制完成时调用
__autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息
字符串逃逸
序列化后的字符串在进行反序列化操作时,会以{}两个花括号进行分界线,花括号以外的内容不会被反序列化。
字符逃逸的主要原理就是闭合,和sql注入类似,只不过它判断的是字符串的长度。输入恰好的字符串长度,让无用的部分字符逃逸或吞掉,从而达到我们想要的目的。
<?php
class people{
public $name = 'aaa';
public $sex = 'boy';
}
$a = new people();
print_r(serialize($a));
$str='O:6:"people":2:{s:4:"name";s:3:"aaa";s:3:"sex";s:3:"boy";}123';
var_dump(unserialize($str));
?>
PHP不会报错,并且也不会输出123.说明{}是字符串反序列化时的分界符。当然,在进行反序列化时,是从左到右读取。读取多少取决于s后面的字符长度。
字符增多逃逸
<?php
class A{
public $name = 'aaaaaaaaaaaaaaaaaaaaaaaaaa";s:6:"passwd";s:3:"123";}';
public $passwd = '1234';
}
$ss = new A();
$str = serialize($ss);
//echo $str;
function filter($str){
return str_replace('aa','bbbb',$str);
}
$tt = filter($str);
echo $tt;
$qq = unserialize($tt);
var_dump($qq);
?>
这段代码主要目的就是间接修改序列化对象里的passwd的值。
“;s:6:“passwd”;s:3:“123”;} 长度26
这里的”;是用来闭合前面的.将该字符串添加到name
这个字符串一共有26字符。我们想要让这段字符串进行反序列化,而;}正好将前面闭合,从而将字符串";s:6:“passwd”;s:3:“123”;}逃逸出去。
然后进行构造:我们输入13个aa就会转换为13个bbbb,多出26个字符,成功将";s:1:“b”;s:3:“104”;}逃逸。
字符减少逃逸
<?php
class A{
public $name = 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb';
public $passwd = '1234";s:6:"passwd";s:3:"123';
}
$ss = new A();
$str = serialize($ss);
//echo $str;
function filter($str){
return str_replace('bb','a',$str);
}
$tt = filter($str);
echo $tt;
$qq = unserialize($tt);
var_dump($qq);
?>
同样道理,我们要将s:6:“passwd”;s:3:“123成功反序列化,那么就要把”;s:6:“passwd”;s:27:"1234 这25个字符吃掉
在name输入25个bb就可以达到效果
原序列化值:
O:1:"A":2:{s:4:"name";s:50:"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:6:"passwd";s:27:"1234";s:6:"passwd";s:3:"123";}
字符串逃逸后
O:1:"A":2:{s:4:"name";s:50:"aaaaaaaaaaaaaaaaaaaaaaaaa";s:6:"passwd";s:27:"1234";s:6:"passwd";s:3:"123";}
这里相当于变量name的值变为aaaaaaaaaaaaaaaaaaaaaaaaa";s:6:"passwd";s:27:"1234 其长度刚好为50后续;s:6:"passwd";s:3:"123刚好和最后形成闭合覆盖了passwd的值成功逃逸