一道简单的web题,由蓝鲸安全平台提供
题目:为什么后台无法登录了?好像增加了cookie也不能登录,答题地址:http://ctf.whaledu.com:10802/23h72gdsi8/
目录
- 背景知识
- 什么是PHP序列化(serialize)与反序列化(unserialize)
- PHP反序列化漏洞
- Magic Function
- 题目解析
- 参考连接
- 总结
背景知识
PHP反序列化漏洞虽然利用的条件比较苛刻,但是如果可以利用一般都会产生很严重的后果,而且该知识点在CTF考题中屡考不鲜,它相对weblogic等java项目的反序列化漏洞利用起来相对简单,更加灵活。通常来说,PHP反序列化就是一种针对php对象的注入漏洞。
什么是PHP序列化(serialize)与反序列化(unserialize)
一个对象在某个php中可以调用,但是它不能穿越“时空”的限制跑到另一个网站或者页面中去使用。为了解决这个问题,于是就为php对象构建了一种“时光机”,也就是序列化:serialize,它可以将任意对象转变为一个静态的字符串,因此,对象就可以作为一个简单的变量出入任何需要它的php中了。
反序列化就是一个php序列化时光机的接收器,通过它可以将静态序列还原为原本对象的样子。
举个栗子:我们用一下代码来序列化几个对象(字符串,数字,一个只有变量的类和一个有函数的类)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?php $str = "hello,world!"; $arr = array("name"=>"hello", "id"=>1); class A{ public $a = 123; } class B{ public $b = 123; public function world(){ $b = 111; } } $a = new A(); $b = new B(); echo 'str- '.serialize($str); echo "</br>"; echo 'arr- '.serialize($arr); echo "</br>"; echo 'classA- '.serialize($a); echo "</br>"; echo 'classB- '.serialize($b); ?>
|
运行结果:序列化后的格式如下
1 2 3 4
| str- s:12:"hello,world!"; arr- a:2:{s:4:"name";s:5:"hello";s:2:"id";i:1;} classA- O:1:"A":1:{s:1:"a";i:123;} classB- O:1:"B":1:{s:1:"b";i:123;}
|
1、首先按照序列对象的包含结构层层递进(树状结构),如果是一个数组,先列出数组和其中元素,并对其中的元素一一枚举。
2、每个子对象包含三个特征:类型(s:string, a:array, O:class(object)….)、长度、值。
3、类的序列化只会保存所有变量,但是不会保存对象的方法(函数),所有可以看到A和B的序列化没有区别。
这里以第三行classA为例进行讲解:O代表存储的是对象(object),如果给serialize()传入的是一个数组为a,传入字符为s;1表示对象的名称有1个字符。“A”表示对象的名称。1表示有一个值。{s:1:"a";i:123;}中,s表示字符串,1表示该字符串的长度,“a”为字符串的名称,之后的类似。
PHP反序列化漏洞
反序列化的根源在于unserialize()函数的参数是可控的,我们可以通过传入一个精心构造的序列化字符串,从而控制对象内部的变量甚至是函数。如果
Magic Function
php中有一类特殊的方法叫Magic function, 这里我们着重关注一下几个:
- 构造函数__construct():当对象创建(new)时会自动调用。但在unserialize()时是不会自动调用的。
- 析构函数__destruct():当对象被销毁时会自动调用。
- __wakeup() :如前所提,unserialize()时会自动调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?php class chybeta{ var $test = '123'; function __wakeup(){ echo "__wakeup"; echo "</br>"; } function __construct(){ echo "__construct"; echo "</br>"; } function __destruct(){ echo "__destruct"; echo "</br>"; } } $class2 = 'O:7:"chybeta":1:{s:4:"test";s:3:"123";}'; print_r($class2); echo "</br>"; $class2_unser = unserialize($class2); print_r($class2_unser); echo "</br>"; ?>
|
运行结果:
1 2 3 4
| O:7:"chybeta":1:{s:4:"test";s:3:"123";} __wakeup chybeta Object ( [test] => 123 ) __destruct
|
题目解析
访问解题网站,发现是个登录窗口,可是在输入信息之后点击login没有任何反应。查看源码,发现了是个假的表单,然后有一个提示参数hint。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Login</title> <link rel="stylesheet" href="admin.css" type="text/css"> </head> <body> <br> <div class="container" align="center"> <form method="POST" action="#"> <p><input name="user" type="text" placeholder="Username"></p> <p><input name="password" type="password" placeholder="Password"></p> <p><input value="Login" type="button"/></p> </form>
</div> </body> </html>
|
尝试get、post、cookie参数,在get参数中提交hint=1得到源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php error_reporting(0);
include_once("flag.php");
$cookie = $_COOKIE['Whale'];
if(isset($_GET['hint'])){ show_source(__FILE__); } elseif (unserialize($cookie) === "$KEY") { echo "$flag"; } else { ?> <?php } $KEY='Whale:www.whalectf.com'; ?>
|
找到重要代码段,有unserialize函数,考察反序列化。需要提交一个cookie反序列化后和“$KEY”相等。
我们还看到最下方的$KEY变量定义,只需要复制所有源码,然后利用serialize序列化一下就好。先看一下直接对KEY的序列化

但是递交并不正确,所有仔细看一下比较的地方和变量,是对“$KEY”进行比较的,我们重新序列化。发现其实是个空字符。

所有如果我们在cookie中添加一条这个记录就可以显示flag,使用Firefox浏览器,安装cookie manager这个插件,该插件能够帮助我们手动添加cookie,添加后如下图。

然后再次打开连接就会看到flag

参考连接
https://chybeta.github.io/2017/06/17/%E6%B5%85%E8%B0%88php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/
https://www.freebuf.com/articles/web/167721.html
https://www.cnblogs.com/lishiyun19/p/4470086.html
总结
php反序列化漏洞在CTF题中经常见到,这道题考查相对单一,大家可以找找其他相关的题目,继续加深下印象。