漏洞信息
漏洞简介
- 漏洞名称:Redis跨站脚本漏洞
- 漏洞编号:CVE-2016-10517
- 漏洞类型:跨协议脚本
- CVSS评分:【CVSS v2.0:】【CVSS v3.0:7.4】
- 漏洞危害等级:中危
组件概述
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
漏洞概述
Redis 3.2.7之前的版本中的networking.c文件存在跨站脚本漏洞。远程攻击者可利用该漏洞在浏览器中执行任意的脚本代码。
漏洞利用条件
攻击者可以远程访问redis-server
1.可以从浏览器发起特定的 HTTP 请求
2.可以从开发者的机器访问到 Redis 服务器
漏洞影响。
Redis up to 3.2.7
漏洞修复
https://github.com/redis/redis/commit/874804da0c014a7d704b3d285aa500098a931f50
漏洞复现
应用协议
6379/RESP/HTTP
环境安装/搭建
在环境共享服务器中获取到环境源码\安装包,地址为:\\10.251.0.11\R-Redis\redis-3.2.7-unpacth.tar.zip文件,解压编译即可。
因为Redis对全版本进行了修复,若直接用redis-3.2.7源码编译,发送poc后,redis-server会提示该漏洞利用的提示:
并且发送的TCP连接会直接中断,如果存在漏洞redis-server会回复err不会直接断连接。
所以,先对redis-3.2.7源码进行去补丁操作。
补丁增加了一个判断函数 securityWarningCommand,将此函数的声明和调用在src/server.h、src/networking.c和src/server.c中删除。
漏洞复现
利用brup直接向靶机发送poc
1 | post / HTTP/1.1 |
未打补丁的redis-server则会响应,并保持连接。
漏洞分析
技术背景
Lua是Redis 支持的轻量级脚本语言。 Redis内置了Lua解释器。Lua在Redis中的使用方法,可参考https://www.redisgreen.net/blog/intro-to-lua-for-redis-programmers/
详细分析
漏洞利用过程
Redis 在解析命令的时候,会把每行文本当做输入。如果输入不能匹配上特定的命令,则丢弃输入。这意味着,如果我们用 HTTP 协议请求 Redis,那么 Redis 会跳过不认识的各种报头,执行请求体中的命令。举个例子,下面的 HTTP 请求中,只有最后的 EVAL 会被解析成合法的命令并执行。
1 | post / HTTP/1.1 |
初看好像不算什么问题,毕竟攻击者都能直接请求 Redis 了,没必要用 HTTP 协议掩饰攻击行为。不过漏洞的发掘者想得更加深入。在他的 POC 中,构造了一个 HTML 页面,利用 AJAX 去请求 6379 端口。
一般来说,Redis 是不会暴露在公网里的,但是有可能跟开发者的机器在同一个内网中。假设开发者的浏览器发起了 AJAX 请求,便能绕过外网的限制,直接访问内网的 Redis。即使 Redis 不在 localhost 上,通过扫描(或者社工出具体的服务器地址)也有可能嗅探到内网中的 Redis 实例。
代码分析
在src/server.c中,声明全局数组redisCommandTable,存放redis中所有命令名称,由于不包括POST和host等一些HTTP定义的头部,导致无法判断出HTTP协议,攻击者将真实的redis命令写入HTTP协议的body中,redis没有直接拒绝连接而是继续搜索到body部分,成功执行命令。
每个列表由以下字段组成:
name:表示命令名称的字符串。
function:指向实现命令的 C 函数的指针。
arity: 参数数,可以使用 -N 表示>= N
sflags:命令标志为字符串。有关标志表,请参阅下文。
flags: 标志作为字掩码。由 Redis 使用”sflags”字段计算。
get_keys_proc:从命令获取键参数的可选函数。
这仅在以下三个字段不足以指定哪些参数是键。
first_key_index: 第一个参数是关键
last_key_index: 最后一个论点是关键
key_step:步骤获取从第一个到最后一个参数的所有键。
microseconds:此命令的总执行时间微秒。
calls:此命令的调用总数。
flags、 microseconds、calls字段由 Redis 计算,应始终设置为零。
命令标志使用字符串表示,其中每个字符都表示一个标志。稍后,populateCommandTable 函数将处理使用此字符填充真正的”flags”字段。
这是标志的含义:
- w: 写入命令 (可以修改键空间)。
- r: 读取命令(永远不会修改密钥空间)。
- m: 一旦调用, 可能会增加内存使用量。如果内存不足,请不允许。
- a: 管理命令,如保存或关闭。
- p: Pub/子相关命令。
- f: 强制复制此命令, 无论服务器。
- s: 脚本中不允许使用命令。
- R:随机命令。命令不是确定性的,也就是说,同一个命令具有相同的参数,具有相同的键空间,可能有不同的结果。例如,SPOP 和 RANDOMKEY 是两个随机命令。
- S: 排序命令输出数组,如果从脚本调用,使输出是确定性的。
- l:在加载数据库时允许命令。
- t: 当从属有陈旧数据但不允许使用时,允许命令服务器此数据。在这种情况下,通常不接受任何命令但只是几个。
- M: 不要在监视器上自动传播命令。
- k: 对此命令执行隐式请求,因此该命令将如果插槽标记为”导入”,则接受群集模式。
- F: 快速命令: O(1) 或 O(log(N)) 命令,不应延迟只要内核调度程序给我们时间,它的执行就行。注意,可能触发 DEL 作为副作用的命令(如 SET)不是快速命令。
populateCommandTable函数对提交的命令进行flags的设置:
processCommand函数对提交的命令的flags进行判断是否为redisCommandTable中合法的命令,若不是则输出unknown command。
漏洞触发过程
在src/server.c中,声明全局数组redisCommandTable,存放redis中所有命令名称,由于不包括POST和host等一些HTTP定义的头部,导致无法判断出HTTP协议,攻击者将真实的redis命令写入HTTP协议的body中,redis没有直接拒绝连接,而是在processCommcand函数中一直搜索,遇到不在redisCommandTable中合法的命令,则输出unknown command。否则继续搜索到body部分,最后成功执行命令。
补丁分析
先分析 Redis 该补丁的工作原理:
作为 HTTP/1.1 规范,请求报头中应该有 Host 报头。如果没有,服务器会返回 400 错误码。这意味着,浏览器自己发送的 AJAX 请求中必然会带上 Host 报头。将Host和POST放进redisCommandTable数组中,当以后遇到这两个关键词,直接报错
1 | # Possible SECURITY ATTACK detected. It looks like somebody is sending POST or Host: commands to Redis. This is likely due to an attacker attempting to use Cross Protocol Scripting to compromise your Redis instance. Connection aborted. |
可以尝试用 setRequestHeader 去自定义请求报头。
浏览器也知道这一点。如果用了 setRequestHeader,浏览器会先发送一个 OPTIONS 请求,附上 Access-Control-Request-Headers 报头,待目标服务器明断。只有服务器许可之后,才会进一步发送定制的 AJAX 请求。在这个场景里, Redis 自然什么都不会回复。
总而言之,没有什么办法,可以发送不带 Host 报头的 AJAX 请求。意味着只要 Host 被拉黑,所有的 AJAX 请求也被拉黑了。
对于没打该补丁的版本,该漏洞会有多大的影响?
漏洞依赖的第二点(可以从开发者的机器访问到 Redis 服务器),一般情况下难以满足。毕竟大多数时候,办公区网络和机房网络不在同一个网段里,而且还是互相隔离的。如果不是定点攻击,很难击中目标服务器。
漏洞依赖的第一点(可以从浏览器发起特定的 HTTP 请求),在很大程度上受浏览器的限制。对于那些想利用 AJAX 的攻击者,浏览器可是见得多了。不要忘记同源策略。即使黑客可以去请求特定的 Redis 服务器,受同源策略的影响,浏览器也不会返回响应的结果。意味着,攻击者可以向 Redis 发送命令,但是他们无法知道攻击是否成功。无法评估攻击的效果,通常会令漏洞的价值大打折扣。
当然如果能结合同源策略绕过,确实可以拿到具体的响应。但是这么一来利用空间就更窄了。
流量分析
用HTTP协议伪装redis命令,在POST方法中的body部分添加redis命令。
参考资料
- https://github.com/redis/redis/commit/874804da0c014a7d704b3d285aa500098a931f50
- https://gist.githubusercontent.com/lamby/01ef8eaa7066282d0a5611a500ad4203/raw
- https://segmentfault.com/a/1190000008237858
- https://github.com/dxa4481/whatsinmyredis
- https://www.agarri.fr/blog/archives/2014/09/11/trying_to_hack_redis_via_http_requests/index.html