一道简单的web题,由蓝鲸安全平台提供

题目:为什么后台无法登录了?好像增加了cookie也不能登录,答题地址:http://ctf.whaledu.com:10802/23h72gdsi8/

目录

  1. 背景知识
    1. 什么是PHP序列化(serialize)与反序列化(unserialize)
    2. PHP反序列化漏洞
      1. Magic Function
  2. 题目解析
  3. 参考连接
  4. 总结

背景知识

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,传入字符为s1表示对象的名称有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>
<!--hint:paramenter:hint-->
</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题中经常见到,这道题考查相对单一,大家可以找找其他相关的题目,继续加深下印象。