HTTP请求走私漏洞原理
[TOC]
相关知识
在了解 HTTP 请求走私之前,需要知道 HTTP 协议中的 Content-Length 和 Transfer-Encoding 这两个字段的作用。
Content-Length 用于明确指示 HTTP 消息体的字节长度(以字节为单位)。
Transfer-Encoding用于定义消息体在传输过程中的编码方式,最常见的是 chunked(分块传输),其核心作用是:允许在不知道总长度的情况下传输数据。
Connection: Keep-alive,其作用是告诉服务器,处理完这个 HTTP 请求后不要关闭 TCP连接,对后面访问这台服务器的 HTTP 请求重用这个 TCP 连接,这样只需要进行一次 TCP握手的过程,可以减少服务器的开销,节约资源,还能加快访问速度。
HTTP请求走私原理
HTTP 请求走私漏洞是利用不同网络组件(浏览器、反向代理、负载均衡器、后端服务器)对同一报文边界或头部(尤其 Content-Length 与Transfer-Encoding )的解释不一致来把 隐藏/额外 的请求塞入合法请求流,从而让攻击者在别人的会话中注入请求、窃取/篡改响应、绕过访问控制或使服务器异常。此类攻击基于协议解析差异,而不是应用逻辑漏洞。
RFC 2616 中关于 HTTP 请求走私的内容描述
HTTP 报文由请求行、头部字段、空行和消息体组成。问题就出在“消息体(message-body)”的长度确定。
RFC 2616 明确了优先级:Transfer-Encoding > Content-Length >连接关闭。但并未强制要求服务器在同时出现两者时必须拒绝请求。它只说 “If Transfer-Encoding is present… then the transfer-length is defined by that header”。因此,不同实现(代理、Web 服务器)对这种情况的处理不一致:有的按 Transfer-Encoding,有的按 Content-Length。这种模糊性导致解析差异,是 HTTP 请求走私的根本原因。
当使用 Transfer-Encoding: chunked 时,消息体由若干“块(chunk)”组成。只有遇到“大小为 0 的块”才表示结束。
当服务器依据 Transfer-Encoding: chunked 对 HTTP数据包进行处理时,在消息体中遇到下面一串字符,服务器会认为消息体到这里就结束了!
1 | 0\r\n |
当使用 Content-Length 时,消息体的有效长度由 Content-Length 的值控制,Content-Length 的值只有在大于等于 0 时才是有效的,多余的消息体会被放到 HTTP 缓冲区中。
如果服务器 A 认为这是 chunked,而服务器 B 认为这是固定长度,则二者对“结束位置”的理解不同。
HTTP1.1 中有一个 Connection: Keep-alive 的特征,其作用是告诉服务器,处理完这个 HTTP 请求后不要关闭 TCP连接,对后面访问这台服务器的 HTTP 请求重用这个 TCP 连接,这样只需要进行一次 TCP握手的过程,可以减少服务器的开销,节约资源,还能加快访问速度。
HTTP Pipeline 是 HTTP/1.1 的一个特性,允许在同一个 TCP 连接上,不必等待前一个请求的响应,就可以连续发送多个请求,如
1 | 客户端 → TCP连接 → 服务器客户端发送:GET /page1 HTTP/1.1Host: example.com |
上面三个请求连续发送,不必等待第一个响应;
服务器在处理时必须按顺序处理并返回响应:
1 | TTP/1.1 200 OK...响应1... |
pipeline 中多个请求共享同一连接,如果前端与后端对报文边界解析不一致(分别按照 Content-length 和 Transfer-Encoding: chunked 进行解析),容易导致 HTTP 请求走私!
利用场景
场景:前端服务器(例如代理服务器)负责安全控制,只有被允许的请求才能转发给后端服务器,而后端服务器无条件的相信前端服务器转发过来的全部请求,并对每一个请求都进行响应。
影响范围
Nginx<1.17.7
漏洞利用
可以利用 HTTP 请求走私漏洞绕过一些 WAF!
CL.TE Bypass
CL.TE 代表当 HTTP 请求包中同时存在 Content-length 与 Transfer-Encoding 字段时,前端服务器按照 Content-Length 字段处理 HTTP 请求包,后端服务器按照 Transfer-Encoding 字段处理 HTTP 请求包!
构造payload
1 | POST / HTTP/1.1 |
这里前端服务器按照 Content-Length 获取有效消息体,这里将 Content-length 设置为 6
0\r\n
\r\n
G
注:这里需要关闭 Update Content-Length ,以防止自动更新 Content-Length 字段
当后端服务器收到该请求后,按照 Transfer-Encoding 获取消息体的有效长度,当读到
1 | 0\r\n |
时认为消息体已经结束了,字符 G 被放到了 pipeline 中,当处理下一个请求时会将字符 G 及下一个 HTTP 请求包从pipeline 中取出并作为下一个请求,当字符 G 与下一个 POST 请求包拼接时,HTTP 请求方法就变成了 GPOST !
TE.CL Bypass
TE.CL 代表当 HTTP 请求包中同时存在 Content-length 与 Transfer-Encoding 字段时,前端服务器按照 Transfer-Encoding 字段处理 HTTP 请求包,后端服务器按照 Content-Length 字段处理 HTTP 请求包!
1 | POST / HTTP/1.1 |
Obfuscating TE
前端和后端服务器都支持 Transfer-Encoding ,通过混淆能让它们在处理时产生分歧!
1 | POST / HTTP/1.1 |
其他可用于 TE 混淆的 payload
1 | Transfer-Encoding: xchunked |
HTTP请求走私绕WAF原理
这里首先要明白一个点:pipeline 是在后端服务器中的!
前端服务器(用于安全检查的服务器)按照 Content-Length 或 Transfer-Encoding 指定的长度或特定结束标识符检查对应长度的内容,若内容没有问题,会将整个 HTTP 请求包发送到后端服务器,这样就能利用 Content-Length 指定的长度或 Transfer-Encoding 特定结束标识符绕过 WAF 检测,多余的内容存放在后端服务器的 pipeline 中!





