PHP反序列化字符逃逸


前言

一、PHP序列化(serialize)

序列化就是将变量或者对象转换为字符串的过程

#序列化结果:
o:1:"A":2:{s:4:"name";s:4:"aaaa";s:4:"pass";s:6:"123456";}
//o		即object简写的对象
//1		对象的字符数(这里对象是后面“A”,只有一个字符)
//A		对象名
//2		花括号中的对象数(花括号中总是两个为一对,封号前后两个为一组前为变量名,后为变量值)
//s		表示字符串(string)
//4		变量名长度(这里是后面的name)
//aaaa	对应的变量名,前面name对应值	
//后面pass,123456同理

二、PHP反序列化(unserialize)

反序列化是将字符串转换成变量或者对象的过程

三、魔幻函数

_construct()		创建对象时初始化
_destruction()		结束时销毁对象
_tostring()			对象被当做字符串时使用
_sleep()			序列化对象前使用
_wakeup()			反序列对象之前调用
_call()				调用对象不存在时使用
_get()				调用私有属性时使用		

四、PHP反序列化字符逃逸

反序列化的字符串都是以“;}结束,如果把”;}带入需要反序列化的字符串中(除结尾处),就能让反序列化提前闭合结束,导致后面的原有内容丢失

1.替换修改后导致序列化字符串变长

<?php
function filter($str){
    return str_replace('bb', 'ccc', $str);
}
class A{
    public $name='aaaa';
    public $pass='123456';
}
$AA=new A();
echo serialize($AA)."\n";
$res=filter(serialize($AA));

$c=unserialize($res);
echo $c->pass;
?>

以上代码的运行过程是:
先序列化AA,然后进行替换将不希望出现的词‘bb‘转换为替换词‘ccc’,然后再进行反序列化,最后输出pass

#这里序列化后的代码:
o:1:"A":2:{s:4:"name";s:4:"aaaa";s:4:"pass";s:6:"123456";}

在反序列化的时候php会根据s所指定的字符长度去读取后面的字符,但读取过程很霸道,如果后面的字符数不够,就会继续向后读取,不论后面是什么符号

o:1:"A":2:{s:4:"name";s:5:"aaaa";s:4:"pass";s:6:"123456";}
//这里如果s指定的字符长度为5,aaaa只有四位就会继续读取将“也读取,而正常是需要”;来闭合变量的,这里只剩下了;就会使闭合出现错误

在一开始的代码中会将‘bb’替换为‘ccc’就会使字符量从2变成3

<?php
function filter($str){
    return str_replace('bb', 'ccc', $str);
}
class A{
    public $name='aaaabb';
    public $pass='123456';
}
$AA=new A();
echo serialize($AA)."\n";
$res=filter(serialize($AA));

$c=unserialize($res);
echo $c->pass;
?>

//这样序列化出来的代码应该为
o:1:"A":2:{s:4:"name";s:6:"aaaabb";s:4:"pass";s:6:"123456";}
**然后经过filter过滤后“aaabb”就会变为“aaaccc”而指定的长度为6,就只能读取到‘aaacc’,最后一个‘c’就读取不到,而这个c就是所谓的字符串逃逸**

那么如果我们将name的变量值改为“;s:4:“pass”;s:4:“hack”;}

<?php
function filter($str){
    return str_replace('bb', 'ccc', $str);
}
class A{
    public $name='“;s:4:"pass";s:4:"hack";}';
    public $pass='123456';
}
$AA=new A();
echo serialize($AA)."\n";
$res=filter(serialize($AA));

$c=unserialize($res);
echo $c->pass;
?>


此时序列化后是o:1:"A":2:{s:4:"name";s:25:"“;s:4:"pass";s:4:"hack";}";s:4:"pass";s:6:"123456";}
c的变量值输出为
name=;s:4:"pass";s:4:"hack";}
pass=123456

这里我们想要做到的是要逃逸“;s:4:"pass";s:4:"hack";}将pass改为hack,
所以我们只要在name之中再加25个‘bb’这样经过过滤就会变成50个c如下

<?php
function filter($str){
    return str_replace('bb', 'ccc', $str);
}
class A{
    public $name='bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb   “;s:4:"pass";s:4:"hack";}';
    public $pass='123456';
}
$AA=new A();
echo serialize($AA)."\n";
$res=filter(serialize($AA));

$c=unserialize($res);
echo $c->pass;
?>


此时序列化后是o:1:"A":2:{s:4:"name";s:75:"ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc“;s:4:"pass";s:4:"hack";}";s:4:"pass";s:6:"123456";}
c的变量值输出为
name=cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc(75)
pass=hack

想要逃逸的字符有25个所以我们要让name变量值读取在读取到这25个字符前就闭合,前面就加25个‘bb’,变量名读取的长度就为(25*2+25)75个字符,这样过滤后就变成75个c,刚好读够变量长度,然后逃逸的字符中下一个是;”是变量名的读取闭合,后面逃逸的s:4:"pass";s:4:"hack";}"就作为了对pass变量的赋值

2.替换之后导致序列化字符变短

类推替换之后导致序列化字符变长即可推出

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值