最近遇到 CVE-2019-16869影响的 io.netty:netty组件的风险修复问题,但是 io.netty:netty全版本都有风险,无法通过升级的方式 解决组件的漏洞风险。
目前可以查到的信息也很有限,只有github上相关的补丁信息,
但是自己又对CVE-2019-16869的漏洞原理很感兴趣,网上资料了了,只说 Netty 错误地处理了 HTTP 标头,但是具体怎么错误处理的,以及修复的原理是什么,没有找到完整分析过的文章,
特此完整整理分析如下:
依赖坐标:
io.netty:netty
...
漏洞风险——Netty 存在 HTTP 请求走私漏洞
CVE-2019-16869
漏洞描述
Netty 是一个非阻塞I/O客户端-服务器框架,主要用于开发Java网络应用程序,如协议服务器和客户端。
Netty 的受影响版本容易受到 HTTP 请求走私的攻击,原因是 Netty 错误地处理了 HTTP 标头中冒号前的空格如 Transfer-Encoding : chunked 。攻击者可利用此漏洞进行 HTTP 请求走私攻击,绕过安全控制,未经授权访问敏感数据。
参考信息
https://nvd.nist.gov/vuln/detail/CVE-2019-16869
https://github.com/netty/netty/issues/9571
https://github.com/netty/netty/pull/9585
https://github.com/netty/netty/commit/017a9658c97ff1a1355c31a6a1f8bd1ea6f21c8d
漏洞原理 及 修复
CVE-2019-16869 ,这枚 CVE 是 OPPO 子午互联网安全实验室发掘的,是关于 Netty 中间件存在 HTTP Smuggling 漏洞。
HTTP请求走私漏洞的核心原因是由于前端服务器和后端服务器在处理HTTP请求头时存在差异,导致请求被错误解析。 例如,Content-Length和Transfer-Encoding头的处理不一致,可能导致请求被分割或合并,从而引发安全漏洞。
HTTP请求走私漏洞的类型包括:
- TE:前端服务器使用Content-Length,后端服务器使用Transfer-Encoding。
- CL:前端服务器使用Transfer-Encoding,后端服务器使用Content-Length。
- TE:前后端服务器都支持Transfer-Encoding,但处理方式不一致。
HTTP请求走私漏洞的危害包括:
- 会话劫持:攻击者可以利用该漏洞劫持用户会话,获取敏感信息如登录凭证和会话标识符。
- 身份伪装:攻击者可以伪装成合法用户,绕过安全机制,获得未授权访问权限。
- 敏感信息泄露:攻击者可能泄露用户的个人身份信息、金融数据等。
- 业务破坏:对业务逻辑和系统配置造成破坏。
在 Netty 4.1.42.Final 版本之前对于 Header 头的处理是使用 splitHeader方法,其中关键代码如下:
for (nameEnd = nameStart; nameEnd < length; nameEnd ++) { char ch = sb.charAt(nameEnd); if (ch == ':' || Character.isWhitespace(ch)) { break; }
}
其他的代码我们并不需要过多了解,这里我们可以知道这里将空格与:冒号同样处理了,也就是如果存在空格会把:其之前的 field name 正常处理,并不会抛出错误或者进行其他操作。这样就与 RFC 标准的规范不一致了,于是就会产生解析差异。
@Bi3g0 构建了比较清晰的漏洞原理图:
这里的例子是采用 ELB 作为 Front 服务器,Netty 作为 Backend 服务器进行举例,我们发送如下请求:
POST /getusers HTTP/1.1 Host:www.backend.com Content-Length:64 Transfer-Encoding :chunked
0 GET /hacker HTTP/1.1 Host: www.hacker.com hacker: hacker
elb是如何处理Incorrect TE:
1)攻击请求
POST /getusers HTTP/1.1 主机:www.backend.com 内容长度:64 传输编码:分块 0 GET /hacker HTTP/1.1 主机: www.hacker.com hacker: hacker
2)ELB处理请求的方式:
ELB 会将 Transfer-Encoding 字段忽略,因为它与冒号中间有一个空格,不符合 RFC 标准(参考 https://tools.ietf.org/html/rfc7230#section-3.2.4),
ELB 会使用 Content-Length 作为解析标准,于是会认为以上请求是一个完整的请求,继而扔给 Backend 服务器,也就是 Netty ,Netty 在这里会优先解析 Transfer-Encoding ,即使这个字段不符合 RFC 标准,但是因为它的实现方式不严格, 所以这里因为优先解析 Transfer-Encoding 的原因,它会将这个请求拆分为两个请求:
POST /getusers HTTP/1.1 Host:www.backend.com Content-Length:64 Transfer-Encoding :chunked 0 GET /hacker HTTP/1.1
Host:www.hacker.com
hacker:hacker
这样就造成了 HTTP Smuggling 攻击。
Netty 于 4.1.42 Final 版本修复了这个漏洞:Correctly handle whitespaces in HTTP header names as defined by RFC72…
Netty 可以识别TE[space]: field value为错误的标头或自定义标头。
1)Netty 可以识别TE[space]: field value 自定义 header,Netty 应该像 elb 一样根据 header 来分离请求CL并转发整个 header TE[space]: field value。
2)或者Netty可以识别TE[space]: field value为不正确的header,返回响应码400(Bad Request)。
当我们发送 field name 与 : 之间有空格的 header 请求时, netty 会“正确”地返回 400 ,如下说明:
代码补丁如下:
https://github.com/netty/netty/commit/017a9658c97ff1a1355c31a6a1f8bd1ea6f21c8d
for(nameEnd =nameStart;nameEnd <length;nameEnd ++){ charch =sb.charAt(nameEnd); if(ch ==':' ||(!isDecodingRequest() && Character.isWhitespace(ch))){ break; } }
以上为总结分析,如有其他建议可留言沟通