Redis CVE-2016-8339 分析
前言:
影响版本: Redis 3.2.x-3.2.4.
分析版本: Redis 3.2.0
环境:ubuntu 17.10
git clone https://github.com/antirez/redis.git
切换到历史版本
git reset --hard 670586715a19e7aff
0x01 漏洞类型:数组越界
数组下标越界导致溢出(Redis是使用标准C语言开发的)。
0x02 漏洞原理:
在 Redis 中,CONFIG SET parameter value
命令可以动态的修改服务器配置,而无需重启。其中,有一条命令CONFIG SET client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
可以给当前未连接到服务端的某一类客户端设置”客户端输出缓冲区限制”。
1 | /* Finally set the new config */ |
通过 getClientTypeByName
函数可以获取类型,我们转到这个函数的定义可以发现
1 | int getClientTypeByName(char *name) { |
通过简单字符串类型比较,然后返回不同的值。返回值的定义宏如下:
1 |
然后将返回的值放在 class
字符串中,然后紧接着作为数组下标进行处理。
1 | server.client_obuf_limits[class].hard_limit_bytes = hard; |
我们查看 server.client_obuf_limits[class]
的解析:
1 | clientBufferLimitsConfig client_obuf_limits[CLIENT_TYPE_OBUF_COUNT]; //数组大小为3 |
1 |
可以看到,getClientTypeByName
函数解析客户端类型并返回一个值存储在class变量中,它的取值范围是[-1, 3],接下来client_obuf_limits
使用class变量作为下标去访问结构体数组并赋值。然而从client_obuf_limits
的定义处可以发现,它的长度是3。
这就意味着,这存在着数组下标越界的可能性,由于后续操作是写,所以存在越界写。我们进一步查看关于clientBufferLimitsConfig
结构体的定义
1 | typedef struct clientBufferLimitsConfig { |
结构体大小为 24 字节,这说明攻击者最多向client_obuf_limits数组后写入24字节的数据。
1 | clientBufferLimitsConfig client_obuf_limits[CLIENT_TYPE_OBUF_COUNT]; //数组大小为3 |
由上部分内容,我们大概可以知道client_obuf_limits数组是redisServer结构体的一个成员,它的后面紧跟着AOF状态域(Redis 将所有对数据库进行过写入的命令记录到 AOF 文件, 以此达到记录数据库状态的目的)。攻击者是可以覆盖到这些域并写入数据的。
0x03 利用
由于只能写入24 字节的内容,我们仅仅只能覆盖到后面的,在这些域中暂时只看到aof_filename
这个指针存在利用点。
what is AOF?
Redis 分别提供了 RDB 和 AOF 两种持久化机制:
- RDB 将数据库的快照(snapshot)以二进制的方式保存到磁盘中。
- AOF 则以协议文本的方式,将所有对数据库进行过写入的命令(及其参数)记录到 AOF 文件,以此达到记录数据库状态的目的。
AOF doing ?
Redis 将所有对数据库进行过写入的命令(及其参数)记录到 AOF 文件, 以此达到记录数据库状态的目的, 为了方便起见, 我们称呼这种记录过程为同步。
举个例子, 如果执行以下命令:
1 | redis> RPUSH list 1 2 3 4 |
那么其中四条对数据库有修改的写入命令就会被同步到 AOF 文件中:
1 | RPUSH list 1 2 3 4 |
为了处理的方便, AOF 文件使用网络通讯协议的格式来保存这些命令。
比如说, 上面列举的四个命令在 AOF 文件中就实际保存如下:
1 | *2 |
除了 SELECT 命令是 AOF 程序自己加上去的之外, 其他命令都是之前我们在终端里执行的命令。
同步命令到 AOF 文件的整个过程可以分为三个阶段:
- 命令传播:Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF 程序中。
- 缓存追加:AOF 程序根据接收到的命令数据,将命令转换为网络通讯协议的格式,然后将协议内容追加到服务器的 AOF 缓存中。
- 文件写入和保存:AOF 缓存中的内容被写入到 AOF 文件末尾,如果设定的 AOF 保存条件被满足的话,
fsync
函数或者fdatasync
函数会被调用,将写入的内容真正地保存到磁盘中。
利用思路
aof_filename
这个字符指针值得注意,通过修改这个指针,在具体的环境下
(1)通过修改这个指针,在具体的环境下,攻击者可以达到利用AOF数据覆写任意文件的目的;
(2)加载通过其他途径构造的恶意AOF文件,来进行进一步的攻击。
POC
config set client-output-buffer-limit "master 1094795585 1094795585 1094795585"
效果如下:
可以发现 aof_state 、aof_filename、以及**aof_no_fsync_on_rewrite **被覆写了。
0x04 漏洞修复
redis 的修复记录:
https://github.com/antirez/redis/commit/6d9f8e2462fc2c426d48c941edeb78e5df7d2977
class 的值了做了进一步判断。