CVE-2016-8339_Redis缓冲区溢出漏洞

Catalogue
  1. 1. 漏洞信息
    1. 1.1. 漏洞简介
    2. 1.2. 组件概述
    3. 1.3. 漏洞概述
    4. 1.4. 漏洞利用条件
    5. 1.5. 漏洞影响
    6. 1.6. 漏洞修复
  2. 2. 漏洞复现
    1. 2.1. 应用协议
    2. 2.2. 环境安装/搭建
    3. 2.3. 漏洞复现
  3. 3. 漏洞分析
    1. 3.1. 技术背景
    2. 3.2. 详细分析
      1. 3.2.1. 漏洞利用过程
      2. 3.2.2. 代码分析
      3. 3.2.3. 漏洞触发过程
      4. 3.2.4. 补丁分析
    3. 3.3. 流量分析
  4. 4. 参考资料

漏洞信息

漏洞简介

  • 漏洞名称:Redis缓冲区溢出漏洞
  • 漏洞编号:CVE-2016-8339
  • 漏洞类型:缓冲区溢出
  • CVSS评分:【CVSS v2.0:】【CVSS v3.0:9.8】
  • 漏洞危害等级:高危

组件概述

​ Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

​ 它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。

漏洞概述

​ 当发送精心制作的命令时,Redis 3.2.x中3.2.4之前的缓冲区溢出会导致任意代码执行。在Redis数据结构存储的CONFIG SET命令期间,对client-output-buffer-limit选项的处理中存在一个越界写入漏洞。精心设计的CONFIG SET命令导致越界写入,可能导致代码执行。

漏洞利用条件

1、攻击者可以访问到redis-server,redis-server配置外网可访问

2、redis-server配置弱密码或空密码

3、redis-server取消保护模式

漏洞影响

Redis:3.2.0

Redis:3.2.1

Redis:3.2.2

Redis:3.2.3

漏洞修复

https://github.com/antirez/redis/commit/6d9f8e2462fc2c426d48c941edeb78e5df7d2977

漏洞复现

应用协议

6379/RESP

环境安装/搭建

​ 在环境共享服务器中获取到环境源码\安装包,地址为:\\10.251.0.11\R-Redis\redis-3.2.3.tar.zip文件,解压编译即可。

​ 启动前先更改配置文件redis.conf,配置外网可访问,取消保护模式。

漏洞复现

​ 攻击机发送config set命令:

1
redis-cli -h 10.251.0.36 CONFIG SET client-output-buffer-limit "master 3735928559 3405691582 373529054"

​ class字段设置为master。

​ redis-server返回ok,虽然redis-server并没有崩溃,但是越界写入已经成功。

漏洞分析

技术背景

​ Redis是轻量级的,非易失性键值数据存储。 它通过Redis序列化协议(RESP)提供对简单易变数据结构的访问,该协议是基于TCP的协议。 与大多数其他数据库一样,Redis遵循客户端—服务器模型。 客户端能够通过Redis命令在Redis服务器上创建,修改和检索记录。

​ 例如,以下命令创建“ TEST”字符串记录并将其分配给“ 1234”键值,将此记录修改为“ TEST2”并分别检索记录:

1
2
3
SET 1234 TEST
GETSET 1234 TEST2
GET 1234

​ 有关Redis命令的完整列表,请参考 http://redis.io/commands

​ Redis客户端通过端口6379通过TCP使用Redis序列化协议(RESP)与服务器进行通信。可通过 http://redis.io/topics/protocol获得该协议详细说明。 RESP使用五种数据类型,这些数据类型由相应数据的第一个字节标识:

  • 简单字符串以“ +”字符开头

  • 错误以“-”字符开头

  • 整数以“:”字符开头

  • 批量字符串以“ $”字符开头

  • 数组以“ *”字符开头

    ​ 批量字符串以“ $”字符开头,后跟相应字符串的长度。 以下重点介绍如何将“ Sangfor”表示为大容量字符串:

1
2
$7 CRLF
TELUS

​ 其中CRLF表示新的行序列回车(CR),后跟换行(LF)。

​ RESP数组以“ *”字符开头,后跟数组中的元素数。 下面说明了一个由2个元素组成的大容量字符串数组:

1
2
3
4
5
*2 CRLF
$7 CRLF
Sangfor CRLF
$4 CRLF
TEST CRLF

​ 所有Redis命令都通过RESP字符串数组发送到服务器。 例如,上述SET命令将以下形式发送:

1
2
3
4
5
6
7
*3 CRLF
$3 CRLF
SET CRLF
$4 CRLF
1234 CRLF
$4 CRLF
TEST CRLF

​ Lua是Redis 支持的轻量级脚本语言。 Redis内置了Lua解释器。Lua在Redis中的使用方法,可参考https://www.redisgreen.net/blog/intro-to-lua-for-redis-programmers/

CONFIG SET client-output-buffer-limit命令

client-output-buffer-limit使用CONFIG SET命令修改选项。设置client-output-buffer-limit选项所需的语法如下所示。

1
CONFIG SET client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>

​ 例如:

1
CONFIG SET client-output-buffer-limit "slave 0 0 0"

详细分析

漏洞利用过程

client-output-buffer-limit使用CONFIG SET命令修改选项期间,存在写越界漏洞。设置client-output-buffer-limit选项所需的语法如下所示。

1
CONFIG SET client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>

​ 当class值为master时,返回3:

1
CONFIG SET client-output-buffer-limit "slave 0 0 0"

​ 在解析中,对client-output-buffer-limit的调用getClientTypeByName用于检索相应类的类型。在这种情况下,getClientTypeByName返回[-1,3]集中的值。查看client_obuf_limits数组的声明,我们看到数组的大小为3

​ 此漏洞就是未考虑CONFIG SET client-output-buffer-limit 时,class为master的情况。client-output-buffer-limit只是预期设置normal,slave和pubsub三类客户端,但是master也是一个有效的客户端。通过提供的客户端类型master,client_obuf_limit数组会溢出,随后的结构变量将被覆盖。

代码分析

​ 在/src/config.c中的loadServerConfigFromString函数,未考虑class等于3的情况,导致class值等于3时,写入server.client_obuf_limits[class]值,导致索引越界写入。

​ 首先,查看server结构体的结构,server结构体在/src/server.h中声明。

​ 其redisServer结构如下:

​ 结构包含很多属性,其中预定义了clinet_obuf_limits数组的大小。

​ 在/src/server.h中宏定义了CLIENT_TYPE_OBUF_COUNT为3,同时发现了CLIENT_TYPE_MASTER值为3。

​ 目前确定了server.client_obuf_limits数组大小为3,也就是索引取值只能为0,1,2,如果索引值为3,则会发生越界读写。分析getClientTypeByName函数,看看class值如何为3。

​ getClientTypeByName函数通过输入的name值,返回相应的类型名,当name为master时,返回CLIENT_TYPE_MASTER, 在/src/server.h中宏定义CLIENT_TYPE_MASTER即为3。

​ 此时,class被赋值CLIENT_TYPE_MASTER,即为3,直接对server.client_obuf_limits[3]赋值,索引越界,导致越界写入。

​ 同样的问题也存在在/src/config.c的configSetCommand函数中。

​ 此漏洞就是未考虑CONFIG SET client-output-buffer-limit ,class为master的情况。client-output-buffer-limit只是预期设置normal,slave和pubsub三类客户端,但是master也是一个有效的客户端。通过提供的客户端类型master,client_obuf_limit数组会溢出,随后的结构变量将被覆盖。

1
CONFIG SET client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>

漏洞触发过程

​ 在/src/config.c中的loadServerConfigFromString函数,未考虑class等于3的情况,导致class值等于3时,写入server.client_obuf_limits[class]值,导致索引越界写入。

​ 目前确定了server.client_obuf_limits数组大小为3,也就是索引取值只能为0,1,2,如果索引值为3,则会发生越界读写。

​ getClientTypeByName函数通过输入的name值,返回相应的类型名,当name为master时,返回CLIENT_TYPE_MASTER,在/src/server.h中宏定义CLIENT_TYPE_MASTER即为3。

​ 此时,class被赋值CLIENT_TYPE_MASTER,即为3,直接对server.client_obuf_limits[3]赋值,索引越界,导致越界写入。

​ 同样的问题也存在在/src/config.c的configSetCommand函数中。

​ 此漏洞就是未考虑CONFIG SET client-output-buffer-limit ,class为master的情况。client-output-buffer-limit只是预期设置normal,slave和pubsub三类客户端,但是master也是一个有效的客户端。通过提供的客户端类型master,client_obuf_limit数组会溢出,随后的结构变量将被覆盖。

1
CONFIG SET client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>

补丁分析

​ 补丁增加了对class的值与CLIENT_TYPE_MASTER判断,如果等于CLIENT_TYPE_MASTER(3),则不会进行赋值,跳转到报错函数。

流量分析

​ 向靶机发送config set命令将class字段设为master,则会发生越界写入。

1
CONFIG SET client-output-buffer-limit "master 3735928559 3405691582 373529054"

​ 靶机返回ok,代表越界写入成功。

参考资料