您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > php

细说php反序列化字符逃逸

时间:2020-11-24 13:23:47  来源:  作者:

本文涉及的实操——实验:php反序列化漏洞实验(合天网安实验室)https://www.hetianlab.com/expc.do?ec=ECID172.19.104.182016010714511600001&pk_campaign=toutiao-wemedia

通过本次实验,大家将会明白什么是反序列化漏洞,反序列化漏洞的成因以及如何挖掘和预防此类漏洞。

前言

php反序列化的字符逃逸算是比较难理解的一个知识点,在最近的好几场比赛中都出现了相关的题,于是下定决心彻底理解透彻这个知识点,于是便有了这篇文章。

基础知识理解

字符逃逸在理解之后就能够明白,这是一种闭合的思想,它类似SQL中的万能密码,理解这种原理之后会变得特别容易。

在SQL注入中,我们常用'、"来对注入点进行一些闭合或者一些试探,从而进行各种姿势的注入,反序列化时,序列化的值是以;作为字段的分隔,在结尾是以}结束,我们稍微了解一下,

<?php
 class people{
    public $name = 'Tom';
    public $sex = 'boy';
    public $age = '12';
 }
 $a = new people();
 print_r(serialize($a));O:6:"people":3:{s:4:"name";s:3:"Tom";s:3:"sex";s:3:"boy";s:3:"age";s:2:"12";}

反序列化的过程就是碰到;}与最前面的{配对后,便停止反序列化。我们可以将上面的序列化的值稍作改变:

O:6:"people":3:{s:4:"name";s:3:"Tom";s:3:"sex";s:3:"boy";s:3:"age";s:2:"12";}123123
细说php反序列化字符逃逸

 

可以看到,并没有报错,而且也顺利将这个对象反序列化出来了,恰好说明了我们以上所说的闭合的问题,与此同时,修改一些序列化出来的值可以反序列化出我们所知道的对象中里没有的值,在学习绕过__wakeup就能过知道了,这里可以自己去做一些尝试,去理解。

接下来就是要说到报错的时候了,当你的字符串长度与所描述的长度不一样时,便会报错,比如上图中s:3:"Tom"变成s:4:"Tom"或s:2:"Tom"便会报错,为的就是解决这种报错,所以在字符逃逸中分为两类:

  1. 字符变多
  2. 字符减少

关键字符增多

在题目中,往往对一些关键字会进行一些过滤,使用的手段通常替换关键字,使得一些关键字增多,简单认识一下,正常序列化查看结果

细说php反序列化字符逃逸

 

这里,我们对序列化后的字符串进行了替换,使得后来的反序列化报错,那我们就需要在Tom这里面的字符串做手脚,在username之后只有一个age,所以在双引号里面可以构造我需要的username之后参数的值,这里修改age的值,我们这里将Tom替换为Tom";s:3:"age";s:2:"35";}然后进行反序列化,这里指的是在对username传参的时候进行修改,也就是我们写链子的时候进行的操作

细说php反序列化字符逃逸

 

可以看到构造出来的序列化字符串长度为25,而在上面的反序列化过程中,他会将一个o变成两个,oo,那么得到的应该就是s:25:"Toom"我们要做的就是让这个双引号里面的字符串在过滤替换之后真的有描述的这么长,让他不要报错,再配合反序列化的特点,(反序列化的过程就是碰到;}与最前面的{配对后,便停止反序列化)闭合后忽略后面的age:13的字符串成功使得age被修改为35。

age的修改需要前面的字符串username的值长度与描述的一样,这需要我们精确的计算,这里是将一个o变成两个,以下就只写o不写Tom,效果一致,我们需要知道我们除了双引号以内的,所构造的字符串长度为多少,即";s:3:"age";s:2:"35";}的长度22,那就需要22个o,

总的来说就是22个o加上后面的字符串长度22,总长度就为44,在被过滤替换后,光o就有44个,符合描述的字符串长度。下面就说明(为什么叫做逃逸

细说php反序列化字符逃逸

 

这里特意写了"将一大串o进行与前面的"闭合了,如果直接反序列化,在序列化出来的值中就包含了";s:3:"age";s:2:"35";}。

反序列的过程中,所描述的字符串长度(这里为44),而后面双引号包裹的字符串长度(这里为22)不够所描述的长度,那么他将会向后吞噬,他会将后双引号吞噬,直至足够所描述的长度,在一切吞噬结束之后,序列化出来的字符串如果不满足反序列化的字符串的格式,就会报错。我们这里是他吞噬结束后,还满足这个格式,所以不报错。

在这个例子中,我们利用他对序列化后的值,进行增加字符串长度的过滤,让他填充双引号内的字符串达到所描述的44这么长,使得后面的s:3:"age";s:2:"35";不被吞噬,让这部分代码逃逸出吞噬,又让他提前遇到}忽略后面的一些不需要的字符串,结束反序列化。

细说php反序列化字符逃逸

 

可以看到,我们构造的payload是成功修改了age,这里是数组,在对对象操作时也是一样的。

刚刚说到吞噬,在增加字符串的题目中,我们是利用题中的增加操作,阻止他进行向后吞噬我们构造的代码,而在字符减少的过程中,我们也是利用这个操作。

关键字符减少

有了前面”吞噬“的一种解释,那么字符串减少就很好说了 ,同样的也是因为替换的问题,使得参数可以让我们构造payload

细说php反序列化字符逃逸

 

这里的错误是因为s:5:"zddo"长度不够,他向后吞噬了一个双引号,导致反序列化格式错误,从而报错,我们要做的就是让他往后去吞噬一些我们构造的一些代码。以下将具体实施。

同样的,我们这里以修改age为例,不同的是与增加字符串传值的地方有些许不同,我们构造的值是有一部分让他吞噬的

先正常传递值序列化出我们需要修改的值,我们需要的是将age:13改为35

细说php反序列化字符逃逸

 

取出";s:3:"age";s:2:"35";}这就是我们需要构造的,接着继续将这部分内容重新传播,序列化出来,得到下面的结果

细说php反序列化字符逃逸

 

选中部分就是我们构造出来,他需要吞噬的代码,s:22:""这个双引号里面我们还有操作的空间,用来补齐字符串长度,接着就是计算我们自己所需要吃掉的字符串长度为18,根据过滤,他是将两个o变成一个,也就是每吃掉一个字符,就需要有一个oo,那我们需要吃掉的是18个长度,那么我们就需要18个oo,在吞噬结束之后我们的格式又恢复正确,使得真正的字符s:3:"age";s:2:"35";逃逸出来,成功加入反序列化

细说php反序列化字符逃逸

 

这就是我们最终的payload,可以看到下图成功修改了

细说php反序列化字符逃逸

 

例题

有了以上基础,就可以做题了,简单的开始入手

安恒四月(字符减少)

<?php
show_source("index.php");
function write($data) {
    return str_replace(chr(0) . '*' . chr(0), '', $data);
}

function read($data) {
    return str_replace('', chr(0) . '*' . chr(0), $data);
}

class A{
    public $username;
    public $password;
    function __construct($a, $b){
        $this->username = $a;
        $this->password = $b;
    }
}

class B{
    public $b = 'gqy';
    function __destruct(){
        $c = 'a'.$this->b;
        echo $c;
    }
}

class C{
    public $c;
    function __toString(){
        echo file_get_contents($this->c);
        return 'nice';
    }
}

$a = new A($_GET['a'],$_GET['b']);
//省略了存储序列化数据的过程,下面是取出来并反序列化的操作
$b = unserialize(read(write(serialize($a))));

看到上面的代码,很明显,我们需要利用file_get_contents();读取文件,将flag读取出来,但是他是个__toString()方法,我们就要让他触发这个方法,当反序列化出对象后,被当作字符串使用时,就可以触发,那我们就需要写一个链,与此同时我们也要知道字符串被删减了几个字符

function write($data) {
    return str_replace(chr(0) . '*' . chr(0), '', $data);
}

function read($data) {
    return str_replace('', chr(0) . '*' . chr(0), $data);
}

看这一部分即可了解到,如果发现不可见字符*不可见字符,字符串就会增多,接着又将的6个字符变成3个字符不可见字符*不可见字符,我们自己是不会去写入不可见字符的在这道题中,相反可以故意写入使得字符串减少,通过计算逃逸字符,读取flag文件

题目中序列化的是对象$a,里面有两个参数,username和password,我们要传入的也是这两个参数的值,所以我们构造的payload,应该是往password传我们构造好的字符,而在username传入的为计算好的个数,

按照流程来,先写一个链子,某些参数先随便写

细说php反序列化字符逃逸

 

";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:5:"/flag";}}}

选中部分就是我们需要重新传参的地方,我们传进password,再次序列化看看效果

细说php反序列化字符逃逸

 

我所选择的地方就是需要被吞噬的部分,然后才能使得后边的代码全部逃逸,长度为23,计算后发现需要8个,但8个这样的符号会吞噬24个字符,因此我们可以在s:70:""双引号里面可以随意补一个字符让它吞噬。

因此我们最终在password里面传参的应该是:

i";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:5:"/flag";}}}

而在username中传的就是8个:

最终payload:

?a=&b=i";s:8:"password";O:1:"B":1:{s:1:"b";O:1:"C":1:{s:1:"c";s:5:"/flag";}}}
细说php反序列化字符逃逸

 

0CTF piapiapia(字符增加)

扫描目录发现www.zip,下载后开始,审计源码

我们根据他的网页一步步看源码,首先他要我们登陆,我们先注册一个账号进行登陆

细说php反序列化字符逃逸

 

看到上图的界面,这时我们看到update.php,都是一些对参数的白名单按要求写即可,图片也随便传一个符合大小的即可,但是注意nickname是我们要操作的地方,稍后讲解,然后有个序列化数组的过程

$user->update_profile($username, serialize($profile));

在上传成功后,他会到profile.php,我们看到profile.php,有这么一段东西

$profile = unserialize($profile);
$phone = $profile['phone'];
$email = $profile['email'];
$nickname = $profile['nickname'];
$photo = base64_encode(file_get_contents($profile['photo']));

这里告诉我们,他反序列化的是profile这个数组序列化后的值,读取的是键名为photo里面的文件名,而flag在config.php也就是说我们需要构造的就是数组中$profile['photo']='config.php'

那么怎样才能让他读这个config.php呢

我们看到class.php,看到父类MySQL中:

public 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);
    }

发现序列化的值他会传递给user中的方法update_profile,接着update_profile又将这个值传递给了父类方法filter,显而易见,就是一种过滤,防止sql注入,但是可以发现,他是以替换的方式给返回值,在过滤的字符串中,只有where变成hacker,由5个字符变成6个,所以是字符增加的逃逸方式,接着开始构造

细说php反序列化字符逃逸

 

选择部分是需要传入的,在phone和email都是白名单,传入的格式受限制,只有nickname是黑名单,所以我们要绕过这个黑名单,bp抓包

细说php反序列化字符逃逸

 

他使用的是strlen()所以这里有个方式,这个函数如果参数是字符串,那么数出来的就是字符串个数,如果是数组,那么数出来的就是数组元素的个数

将nickname改成nickname[]然后传参

传的参数我们需要构造,需要计算,上面分析了我们需要构造的字符串";}s:5:"photo";s:10:"config.php";},但是他是字符增多,我们就需要知道需要逃逸的字符有多少个,计算了一下为34个,(这里因为改为了数组,而数组的序列化格式结束后是一个大括号,所以我们在一开始的”;后面增加了一个花括号)所以需要34个where帮助我们逃逸这部分代码,上传成功,根据这个修改放包即可

细说php反序列化字符逃逸

 

返回网页点击

细说php反序列化字符逃逸

 

之后可以看到一张显示不出来的图片,因为使用base64编码了,右击查看图片信息

细说php反序列化字符逃逸

 

解码即可看到flag

细说php反序列化字符逃逸

 



Tags:php   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
网络地址来源:获取公网IP地址 https://ipip.yy.com/get_ip_info.phphttp://pv.sohu.com/cityjson?ie=utf-8http://www.ip168.com/json.do?view=myipaddress...【详细内容】
2021-12-15  Tags: php  点击:(14)  评论:(0)  加入收藏
一、编程语言1.根据熟悉的语言,谈谈两种语言的区别?主要浅谈下C/C++和PHP语言的区别:1)PHP弱类型语言,一种脚本语言,对数据的类型不要求过多,较多的应用于Web应用开发,现在好多互...【详细内容】
2021-12-15  Tags: php  点击:(17)  评论:(0)  加入收藏
序言:前段时间织梦因为版权的问题在网上闹得沸沸扬扬,也提醒了众多开发者选择cms上应该谨慎使用,今天给大家展示一款自己搭建的内容管理系统,不用担心版权的问题,而且非常容易维...【详细内容】
2021-11-30  Tags: php  点击:(31)  评论:(0)  加入收藏
序言:习惯了用集成软件来安装php的运行环境,单独配置php、apache成为了部分程序员不愿意面对的问题,下面和我一块来复习一下,赶紧点赞收藏吧。 php官方下载地址https://windows....【详细内容】
2021-11-23  Tags: php  点击:(24)  评论:(0)  加入收藏
准备安装包(PHP: Hypertext Preprocessor)下载安装包以及组件wget https://www.php.net/distributions/php-8.0.0.tar.bz2wget https://github.com/phpredis/phpredis/archive...【详细内容】
2021-11-09  Tags: php  点击:(40)  评论:(0)  加入收藏
golang context 很好用,就使用php实现了github地址 : https://github.com/qq1060656096/php-go-context context使用闭坑指南1. 将一个Context参数作为第一个参数传递给传入和...【详细内容】
2021-11-05  Tags: php  点击:(41)  评论:(0)  加入收藏
TechEmpower BenchmarkTechEmpower 的测试比较科学,包含 SQL 查询、JSON 序列化等各种测试,同时标注了哪些是使用了 ORM,哪些是 RAW 查询。 TechEmpower Benchmark Web Framew...【详细内容】
2021-10-12  Tags: php  点击:(70)  评论:(0)  加入收藏
一、背景介绍Pikachu是一个带有各种漏洞的Web应用系统,这个系统是使用PHP搭建的,需要PHP环境和Mysql数据库支持。我们可以利用phpstudy在本地搭建这个渗透测试平台,如果你是一...【详细内容】
2021-10-09  Tags: php  点击:(52)  评论:(0)  加入收藏
一段数组为例:$list = array:4 [ 0 => array:7 [ "id" => 56 "mer_id" => 7 "order_id" => "wx163265961408769974" "is_postage" => 0 "store_name" => "奇...【详细内容】
2021-09-29  Tags: php  点击:(64)  评论:(0)  加入收藏
利用JS的CryptoJS 3.x和PHP的openssl_encrypt,openssl_decrypt实现AES对称加密解密,由于需要两种语言对同一字符串的操作,而CryptoJS 的默认加密方式为“aes-256-cbc”,PHP端也...【详细内容】
2021-09-16  Tags: php  点击:(79)  评论:(0)  加入收藏
▌简易百科推荐
序言:前段时间织梦因为版权的问题在网上闹得沸沸扬扬,也提醒了众多开发者选择cms上应该谨慎使用,今天给大家展示一款自己搭建的内容管理系统,不用担心版权的问题,而且非常容易维...【详细内容】
2021-11-30  小程序软件开发    Tags:管理系统   点击:(31)  评论:(0)  加入收藏
准备安装包(PHP: Hypertext Preprocessor)下载安装包以及组件wget https://www.php.net/distributions/php-8.0.0.tar.bz2wget https://github.com/phpredis/phpredis/archive...【详细内容】
2021-11-09  mimic96    Tags:PHP   点击:(40)  评论:(0)  加入收藏
golang context 很好用,就使用php实现了github地址 : https://github.com/qq1060656096/php-go-context context使用闭坑指南1. 将一个Context参数作为第一个参数传递给传入和...【详细内容】
2021-11-05  1060656096    Tags:PHP   点击:(41)  评论:(0)  加入收藏
一段数组为例:$list = array:4 [ 0 => array:7 [ "id" => 56 "mer_id" => 7 "order_id" => "wx163265961408769974" "is_postage" => 0 "store_name" => "奇...【详细内容】
2021-09-29  七七小影视    Tags:PHP   点击:(64)  评论:(0)  加入收藏
利用JS的CryptoJS 3.x和PHP的openssl_encrypt,openssl_decrypt实现AES对称加密解密,由于需要两种语言对同一字符串的操作,而CryptoJS 的默认加密方式为“aes-256-cbc”,PHP端也...【详细内容】
2021-09-16  李老师tome    Tags:对称加密   点击:(79)  评论:(0)  加入收藏
1、checkdate()验证格利高里日期即:日期是否存在。checkdate(month,day,year);month必需。一个从 1 到 12 的数字,规定月。day必需。一个从 1 到 31 的数字,规定日。year必需。...【详细内容】
2021-08-31  七七小影视    Tags:时间函数   点击:(80)  评论:(0)  加入收藏
对于各类开发语言来说,整数都有一个最大的位数,如果超过位数就无法显示或者操作了。其实,这也是一种精度越界之后产生的精度丢失问题。在我们的 PHP 代码中,最大的整数非常大,我...【详细内容】
2021-08-26  硬核项目经理    Tags:PHP   点击:(83)  评论:(0)  加入收藏
遵从所有教材以及各类数据结构相关的书书籍,我们先从线性表开始入门。今天这篇文章更偏概念,是关于有线性表的一个知识点的汇总。上文说过,物理结构是用于确定数据以何种方式存...【详细内容】
2021-07-19  硬核项目经理    Tags:线性表   点击:(94)  评论:(0)  加入收藏
一、开启IIS全部功能。二、部署PHP1.官网下载并解压PHP: https://windows.php.net/downloads/releases/2.将php.ini-development文件改为php.ini3.修改php.ini(1)去掉注释,并修...【详细内容】
2021-07-15  炘蓝火诗  今日头条  Tags:PHP环境   点击:(128)  评论:(0)  加入收藏
一、环境说明本文中使用本地VM虚机部署测试。OS:CentOS Linux release 7.8.2003 (Core)虚机配置:2核CPU、4G内存①系统为CentOS 7.8 x64最小化安装,部署前已完成系统初始化、...【详细内容】
2021-06-25  IT运维笔记  今日头条  Tags:PHP8.0.7   点击:(141)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条