请求走私笔记(HTTP/2降级,绕过clte,请求隧道,响应投毒,缓存投毒,缓存欺骗)
笔记:
必要条件
1. 存在请求转发链
- 典型场景:
客户端 → 前端服务器(反向代理/负载均衡)→ 后端服务器
- 差异性:前端与后端服务器对 HTTP 请求的解析逻辑不一致。
2. 协议解析差异
- 前端(如反向代理)与后端服务器对
Content-Length
(CL)或Transfer-Encoding
(TE)标头的处理方式不同。- CL.TE 走私:前端用
Content-Length
解析,后端用Transfer-Encoding
解析。 - TE.CL 走私:前端用
Transfer-Encoding
解析,后端用Content-Length
解析。 - TE.TE 走私:前后端均支持
Transfer-Encoding
,但对标头值的处理不一致(如大小写、非法字符)。
- CL.TE 走私:前端用
3. 连接复用(Keep-Alive)
“连接复用”特指前端服务器和后端服务器之间的TCP连接复用,而不是用户和前端服务器或者攻击者和前端服务器的复用连接
后端服务器必须复用 TCP 连接处理多个请求,否则走私的请求片段无法被后续请求捕获。
4. 非法请求构造可行性
- 攻击者能构造包含歧义边界的请求:
- 同时包含
Content-Length
和Transfer-Encoding
标头。 - 使用非规范化的标头值(如畸形的块长度、多个
Content-Length
头)。
- 同时包含
- 前端服务器未对非法请求进行规范化或拦截。
存在请求走私的环境要求:HTTP/1.1
如果只有HTTP/2是不行的
但是如果前端服务器会把HTTP/2进行降级那么也可能存在请求走私
注意事项:
注意cl是否需要被自动更新
换行是\r\n在包里占两个字节也会计算在cl里
第一个请求走私的污染包
路径最好不要只写/根路径
带点文件的路径拿过来更好打请求走私
不然有的时候会打不上去
比如这样
1 | POST /resources/images/blog.svg HTTP/1.1(注意如果这里的/resources/images/blog.svg变成/可能会失效) |
在有混淆的请求解析差异的基础上可以总结出这样的请求走私打法规则:
这个适用于适用于clte tecl clcl tete有协议解析差异的情况下
cl限制往小了写是让下面的多出来的字符做第二个请求。往大了写能拿来打延时判断存在
te下面0后面接字符是作为第二个请求。但是没写结束符0则是可以拿来打延时
上面这条规则适用于存在混淆边界(也就是存在协议解析差异)的情况下
困惑:
在不存在协议解析的差异的情况下为什么上面这条规则行不通了?不理解…..有大佬看到的话希望能解答解答教教我
qq:1512624649
这条规则是从cl和te的解析逻辑上推出来的.可是为什么没有协议解析差异就行不通了???
是因为RFC吗?但是tete靶场里明明没有严格遵循RFC好像也是不行
不理解┭┮﹏┭┮
ai说这样可以形成一种只对自己的下一次请求的污染?对别人不行.但我一直用tete靶场打的时候当我不对请求解析混淆的时候一直测不出来对自己的第二个请求的污染.这个观点存疑
其他使用技巧注意事项:
要注意HTTP/1.1如果默认支持HTTP/2的话默认会优先http/2
响应如果说请求头重复
一般是Host头重复了和正常的请求
正常的请求头是无法改变的.我们只好把第二个我们尝试污染的请求头里Host删除掉就可以了
到底什么是请求解析差异?
各种类型的混淆请求解析差异类型:
clte tecl的混淆方式
这里做个简单的举例
比如clte tecl这两种它本身存在两种请求解析
一种是Content-Length
,一种是Transfer-Encoding
当两者共同存在的时候他们的请求解析就是混淆的
所以支持,然后根据前后端对这个不同的处理方式形成请求走私
这是portswigger的靶场cl.te包:
第二个Content-Length: 2的作用是为了让第二个变成完整的请求
这样的话头就不会重复不然会跟原先的Host头重复报错
1 | POST / HTTP/1.1 |
te.cl:
1 | POST / HTTP/1.1 |
tete的混淆方式
虽然前后端解析都是te.但是请求头里仍然要存在cl字段并且正常使用就行.也就是让bp自动update他的字符个数即可
混淆标头的方法是参考参数污染的方式构造多个Transfer-Encoding
请求头,例如:
1 | Transfer-Encoding: xchunked |
混淆的标头要放在正常的te请求头前面才可以
这样可以导致错误的标头被前后端形成不同的处理
放在后面就会导致混淆失败
比如这样
1 | POST / HTTP/1.1 |
clcl的混淆方式
标头同时存在两个cl
前端服务器和后端服务器都使用Content-Length
请求头,但是前后端服务器对多个Content-Length
请求头的处理存在差异时,存在此漏洞。如用户发送以下请求:
1 | POST / HTTP/1.1\r\n |
前端服务器根据Content-Length: 30
解析请求主体获取完整请求,但是后端服务器根据Content-Length: 5
解析请求主体,将a=1\r\n
后的部分当作下一个请求的开头,此时如果前端服务器继续向后端服务器转发请求,后端服务器收到的第二个请求为:
1 | GET /admin HTTP/1.1\r\n |
成功执行了注入的HTTP请求,导致请求走私。
cl.0类型并不需要混淆,本身就是一种被混淆过的
cl.0直接用默认的就可以了
他是前端存在cl.但是后端的cl默认为0
又或者说后端cl被忽略了
所以他本身就是个存在前后端请求解析差异的漏洞类型
前端的cl正常让他update字节就行
然后写入想要污染的请求就行了
比如这样
1 | POST /resources/images/blog.svg HTTP/1.1 |
H2.CL
根据文章里的讲解
原因是
前端会自动把HTTP2进行自动降级发送给后端
返回包里的HTTP/2 200 OK是前端的返回内容
如果发包请求改成HTTP/1.1
这里就是HTTP/1.1 200 OK
所以由此判断这里是前端响应的返回包
因为前端和后端之间是用HTTP/1.1把HTTP/2自动降级的
自动降级的时候如果没有检查请求包里的CL请求头或者验证删除
这个时候就会造成HTTP/1.1的请求走私
原理跟其他的类型其实都有点像
重点:为什么这里在HTTP/2可以的情况下改成HTTP/1.1的时候就不行了
原因在于HTTP/1.1的时候前端检测到的时候会对te和cl头进行检测和验证删除错误的
就不会请求走私
但是HTTP/2本来是不支持te和cl的
但是前端服务器会把它进行降级发送给后端
然后前端服务器对HTTP/2又没有检测验证te和cl头的话那么就可以请求走私
前端服务器在进行降级时会添加HTTP/1请求头,如果降级之前没有验证或删除Content-Length
,那我们就可以注入Content-Length
头,造成请求走私。
注意:YAKIT不太行这里要用bp右边改成http2
而不是直接改发包的字符不然不可以
1 | POST / HTTP/2 |
H2.TE:
原理跟上一个差不多
前端服务器遇到HTPP/2的时候进行降级时会添加HTTP/1请求头,如果降级之前没有验证或删除Transfer-Encoding
,那我们就可以注入Transfer-Encoding
头,造成请求走私。
重点:当我们在成功之后对HTTP/2改成HTTP/1.1发现不能请求走私的原因:
原因在于前端服务器对HTTP/1.1的请求会进行te和cl的检测和验证
遇到HTTP/2的时候不会并且对他们进行降级
所以才存在这个类型的漏洞
1 | POST / HTTP/2 |
H2.0 漏洞:
有点像CL.0
后端默认为0并且忽略了cl
所以前端如果接受到cl
那么这个时候就会出现请求走私
收到的所有cl都会成为下一个数据
如果后端服务器忽略已降级请求的Content-Length
请求头,则容易受到等效的“H2.0”漏洞的攻击。
使用场景利用点:
想要把受害者正常的请求回显出来要去找服务器端点有没有什么类似于公共留言版的地方才可以
- 存在回显或间接回显的端点
- 目标服务器需存在能返回请求内容的功能(如留言板、搜索框、错误页面、日志接口等)。
- 例如:
/search?query=test
返回包含test
的页面。
- 恶意请求能被后端处理并存储/返回
- 走私的第二个请求需触发服务端记录或显示数据(如写入数据库、生成错误日志等)。
或者有没有什么可以外带的地方
如果要外带那么Host请求头可能会出现两个就会重复
这个时候就要用cl请求头去提前截止在第二个Host请求之前
然后构造的外带请求要自己写
注意:如果仅仅只是改host头服务器是不会把请求转发到外部的服务器的
除非服务器的有这样类似的逻辑才可以
1 | url = f"http://{request.headers['Host']}/internal" |
还可以通过我们自己构造的第二个请求里的cl长度去对一些不想要的内容或者协议头重复的地方前进行截止
这样也可以变成一个完整的请求
注意:如果是POST请求则一定需要第二个构造的请求里存在CL
不然会不能被当作一个正常的请求
除了上面使用到的对一个请求的提前截止的一些方法我们还可以在最后加两个\r\n让他变成一个完整的请求
使用场景:响应投毒
概念:缓存投毒和缓存欺骗:
前端服务器会缓存后端服务器资源可能会导致缓存欺骗和缓存投毒漏洞的存在
缓存投毒的原理是:让一个正常的静态文件变成一个恶意的js或者其他形式的代码或者文件
攻击者让用户和前端服务器都认为访问的是正常目录/home但是由于后端的请求走私实际返回的内容是一个恶意的/js/hacker.js的内容.当他们两个之间多次被人访问的时候存在缓存.也就导致缓存投毒.
缓存欺骗的原理是:通过把目标用户存储关键信息api等等路径如my-account的响应内容缓存到一个静态路径是/js/test.js的请求路径
这个时候攻击者就可以访问获取/js/test.js获取api等等
用户虽然访问了/js/test.js前端服务器也认为是/js/test.js但是由于请求走私的构造实际返回的内容是/my-account这个时候也就形成了缓存.别人也就可以获取
投毒和欺骗的区别在于:
投毒是让用户加载恶意的代码然后攻击者这里被动的拿到关键信息什么的通过加载恶意的js.适用于获取cookie什么的.但是api不存储在浏览器里获取不到
如果需要获取某个路径下的数据则需要使用欺骗
欺骗是让用户的关键信息路径的内容和一个静态的公共文件路径形成缓存.这个时候攻击者再去获取静态文件拿到关键信息内容比如api等等
靶场案例:
在最后加两个\r\n让他变成完整的请求之后
那么目标用户这个时候访问请求得到的响应就是第二个请求的响应.
那么我们该如何拿到用户的响应呢?
这个时候就用到了爆破.不断地爆破拿到用户的请求响应
1 | POST / HTTP/2 |
上面最后空两行是因为有两个换行符\r\n
不断的爆破
我们就有机会拿到目标用户的响应
1 | HTTP/2 302 Found |
原理图:
HTTP2下关于绕过CL和te的检测手法:
HTTP/2 request smuggling via CRLF injection
适用场景:HTTP/2的降级情况下.才可以
通过右边的修改添加\r\n进行添加一个换行符绕过
这跟普通的换行符绕过是不同的
先看普通的换行符绕过包未绕过成功的情况下是这样的
1 | POST / HTTP/2 |
这样的包是不会出现请求走私
我们需要做的是
在右边HTTP/2的伪头部里进行换行符的写入
仔细观察HTTP/1和HTTP/2的情况下右边的header是不一样的写法.
HTTP/2的是伪头部
示例: 一个 HTTP/2 请求可能包含如下伪头部:
1 | :method: GET |
把Transfer-Encoding: chunked添加到右边的value里并且换行
这个时候再去点击一次apply changes
然后就会变成这样
这个时候绕过也就成功了
我们尝试找个地方能让他回显出下一次请求的内容
有评论区或者搜索这个靶场里都可以做到
我这里用的是评论区
把这个session拿到然后修改浏览器的session就可以了
http2里的请求隧道攻击–请求走私的变种:
如果前端和后端对我们这个请求不会搭建隧道也就是不复用tcp连接
我们可以把两个请求赛到一个请求里?这样也就可以看成是一个隧道?
只需要在HTTP2伪标头里CRLF换行注入就可以实现请求隧道攻击
- 即使前端和后端不复用连接(即不共享TCP连接处理多用户请求),攻击者仍可通过构造单连接内的歧义请求,让后端解析出多个请求(走私)。
- 这本质上是请求走私的一种实现,而非严格意义的“隧道复用”,但效果类似(绕过前端检查,向后端注入请求)。
请求隧道的crlf注入漏洞的原理图:
根据图片我们可以知道第二个请求走私的响应内容客户端是收不到的?
但是我们可以通过改成HEAD头去看反应
出现两个HTTP/1.1或者类似的两个Server Error都是代表着请求走私成功的意思
1 | HTTP/2 500 Internal Server Error |
补充:这里的环境是前端拿到的第一个请求包的显示的长度
然后返回的内容是第二个请求包的内容
所以当长度和内容不匹配的时候会出现
Server Error: Received only 516 of expected 3247 bytes of data
请求隧道走私缓存投毒笔记和当前比较有阶段性的总结:
请求隧道走私
就是通过伪标头crlf换行注入
实现绕过的前端
前端在降级HTTP/2请求的时候会重写
这个时候识别到里面的CRLF然后就变成正常的换行请求进行写入
这个时候也就绕过了前端的检验
重写为http/1.1的请求之后
一般一个就会变成两个请求
如果第一个请求是get那么返回的内容也是get.里面夹杂的第二个请求就算返回到前端.也最终不会显示到用户浏览器这里
原因是用户只发送了一个请求.前端服务器收到了两个来自后端服务器的响应.那么最终也只能返回一个响应包给用户
并且由于响应包里Content-Length的存在.导致第二个响应不能和第一个相应整合在一起
如何去实现让第二个响应的内容回显到用户这里或者说拼接到第一个响应的内容里呢
这个时候就用到了HEAD请求
他会返回响应头的内容
但是不会返回响应的具体数据.但是又由于响应头里存在Content-Length: 8401这个头
那么这个时候我们就可以把第二个响应包的数据放入形成一个新的响应
这个时候我们把第一个请求包的头改成HEAD
第二个请求是get情况下
我们最终得到的内容是
第一个响应的响应头和第二个响应的数据包内容
我们现在知道了可以把响应包内容控制到第一个响应包里面
那么如何让响应包的内容变成我们想要的呢
只需要找到一个目录接口
如图
当我们尝试给这个目录的接口写入一点东西的时候由于是一个文件夹的目录.他会帮我们把这个路径302跳转到文件夹的子路径里然后加上后面的值
那么这个时候test部分就是我们可以自由控制和编写
写点html标签和script就可以实现xss攻击
再回到我们的请求走私包的构造
当我们尝试去构造一个xss的payload之后我们可以发现
响应的时候会出现一个等待响应还是响应等待时间过长的报错回显(没有图忘记截图和懒得重启靶场了)
原因是当我们的响应头是Content-Length: 8401
那么意味着响应的内容应该是8401个字节的值
如果只写了个xss.那么不满足响应头这个数值的值
所以会让前端服务器以为还有后续的内容没有发送过来
然后一直等待响应
然后响应超时
这个时候我们需要做的是写入大量的字符让我们这个响应包的内容满足长度.甚至超出长度也没关系那样前端服务器会自动截取
但不要超过太多超过太多可能就到达前端服务器的处理上限也就报错了
这就是我们第一张图出现很多test的原因
最后的最后就是这个靶场里的缓存漏洞实现原理很简单
我们频繁的在bp里发送这个恶意的包
然后就会形成缓存.别人访问/路径的时候也就会出现我们写好的xss
然后造成危害
这个缓存是比较短暂的
可能十几秒或者一分钟缓存就会消失
这个时候你浏览器访问什么就是什么原来的内容
所以是缓存
所以需要利用的话要频繁发包
客户端不同步
客户端不同步同步 (Client-Side Desync) 是一种特定的 HTTP 请求走私攻击技术,可以使受害者的 Web 浏览器与易受攻击的网站之间的连接失去同步。它主要利用的是客户端和服务器之间在处理 HTTP 请求时的不同步现象,而不一定需要多个服务器之间的解析差异。因此,即使在单服务器环境中,也可能会受到这种攻击的影响。
当受害者访问包含恶意 JavaScript 的任意域(图中evil.com
)上的网页时,恶意代码执行会导致受害者的浏览器向存在漏洞的网站(图中example.com
)发出包含HTTP请求走私数据的请求,这一过程和CSRF有点相似。在服务器响应第一个请求后,走私请求会留在服务器上,从而导致与浏览器的连接不同步。后续请求会追加都走私请求后边,从而执行走私的请求,返回走私请求的响应。
这里贴一部分,感觉跟csrf有点关系利用环境有点严格,感兴趣的师傅可以看看这个文章里的
HTTP 请求走私漏洞详解 - Hack All Sec的博客
服务器端基于暂停的不同步
当后端服务器在超时后不会关闭连接时,使用服务器端基于暂停的不同步可以引发类似CL.0
的效果。如果我们将以下请求发送到后端服务器时:
1 | POST /example HTTP/1.1 |
只把请求头部分发送到后端服务器,然后停止发送请求主体部分的内容。后端服务器根据Content-Length: 34
等待获取请求主体,但是一直没等到,直到超时并发送响应。然后发送请求主体。前端服务器将其视为初始请求的主体并通过同一连接将其转发到后端服务器。但是后端服务器已经响应了初始请求,因此把这些字节当作另一个请求的开始。当下一个请求被转发过来时,后端服务器收到的请求为:
1 | GET /hopefully404 HTTP/1.1 |
此时我们实现 CL.0
的效果。Apache 2.4.52
就容易受此影响。检测和利用此漏洞需要使用Burp的Turbo Intruder
插件,直接在BApp store安装即可。
感兴趣的师傅可以看看这个文章里的.
HTTP 请求走私漏洞详解 - Hack All Sec的博客
AI—-基础知识的讲解举例
关于ai对小总结的补充和分析
1 | --- |
- 服务器行为差异:前端可能取第一个值(
xchunked
),后端取最后一个值(chunked
)。
(2) 分块格式混淆
- 分块扩展:
0;x=1\r\n\r\n
,诱导服务器误判分块终止位置。 - 非法换行符:用
\n
代替\r\n
,破坏分块解析逻辑。 - 分块大小混淆:如十六进制大小写(
0X0
代替0
)。
(3) 请求体混淆
- 注入伪头:在请求体中插入类似 HTTP 头的结构(如
X: Y\r\n
),诱导后端将其解析为新请求头。
3. 对 TE.TE 场景的补充
严格模式下的走私可能:
即使前后端均使用Transfer-Encoding
,若后端对分块扩展(;
)或注释解析存在漏洞,仍可走私。例如:1
2
3
4
5POST /
Transfer-Encoding: chunked
0;x="\r\n" # 分块扩展参数
GET /admin HTTP/1.1\r\n...- 后端可能忽略分块扩展,误判终止位置,将
GET
视为新请求。
- 后端可能忽略分块扩展,误判终止位置,将
4. CL.CL 场景的修正
多 Content-Length 头的处理:
你的示例中构造两个Content-Length
头(30 和 5),实际场景中:RFC 规范:多个
Content-Length
头必须值相同,否则视为非法请求(返回400 Bad Request
)。实战利用条件:需服务器未严格校验多值一致性。例如:
1
2Content-Length: 5
Content-Length: 5 # 合法(重复相同值)或通过空格/TAB 绕过:
1
2Content-Length: 5
Content-Length : 5 # 服务器可能解析为同一头
5. CL.0 场景的澄清
本质是 CL 与默认行为的冲突:
前端使用Content-Length
,后端忽略该头并默认请求体长度为 0(或从其他上下文推断长度)。无需混淆,直接构造:1
2
3
4
5POST /
Content-Length: 30 # 前端读取30字节
Host: example.com
GET /admin HTTP/1.1\r\n...- 后端可能仅处理前0字节,后续
GET
被保留为下一个请求。
- 后端可能仅处理前0字节,后续
总结:混淆的核心目标与实战策略
漏洞类型 | 是否需要混淆 | 关键混淆手段 | 示例场景 |
---|---|---|---|
CL.TE | 可选(依赖解析差异) | 无需混淆,直接构造CL与TE冲突 | 前端CL截断,后端TE解析后续数据 |
TE.TE | 必需(绕过严格校验) | 分块扩展、非法换行符、多TE头污染 | 0;x=1\r\n + 注入请求 |
CL.CL | 必需(绕过多值校验) | 多CL头构造(空格/TAB混淆) | Content-Length : 5 + 非法重复 |
CL.0 | 无需(利用默认行为差异) | 直接构造CL覆盖实际数据长度 | 前端CL大值,后端默认长度0 |
你的测试结果解释
在 TE.TE 靶场中无法自我污染的原因是:
- 靶场严格遵循 RFC:后端在解析
0\r\n\r\n
后立即终止请求,后续字符被丢弃。 - 缺乏混淆手段:未使用分块扩展或非法格式,无法绕过终止符校验。
修正后的实战建议
- 明确漏洞类型:先确定目标是 CL.TE、TE.CL 还是 TE.TE。
- 按需选择混淆:
- CL.TE:无需混淆,直接构造 CL 与 TE 冲突。
- TE.TE:必须混淆(分块扩展、非法头)。
- 工具辅助:使用 Burp Scanner 或 Turbo Intruder 自动探测解析差异点。
1 | ### 关于RFC的讲解: |
RFC(互联网技术标准)与HTTP请求解析的关系,可以比喻为**“交通规则与车辆行驶”**的关系。RFC定义了互联网通信的“规则”,而请求解析则是服务器按照这些规则“解读”客户端发送的数据。以下是通俗易懂的解释:
1. RFC是HTTP请求的“语法规则书”
就像写作文需要遵循语法,HTTP请求必须符合RFC定义的格式,服务器才能正确理解。例如:
- RFC 9110(HTTP/1.1):规定了HTTP消息的格式,如:
- 请求行必须为
方法 URL 协议版本
(如GET /index.html HTTP/1.1
)。 - 头部字段需用
:
分隔键值(如Host: example.com
)。 - 正文需通过
Content-Length
或Transfer-Encoding
指定长度或分块。
- 请求行必须为
如果请求不按RFC写,服务器可能“读不懂”,就像写错语法的句子让人困惑。
2. 请求解析:服务器如何“拆解”请求
当服务器收到HTTP请求时,会按照RFC规则逐步解析:
- 解析请求行:确认方法、URL和协议版本。
- 解析头部:逐个读取头部字段,直到遇到空行(
\r\n\r\n
)。 - 解析正文:
- 如果有
Content-Length
,按指定长度读取正文。 - 如果有
Transfer-Encoding: chunked
,按分块格式读取(如5\r\nhello\r\n0\r\n\r\n
)。
- 如果有
关键点:RFC规定了解析的每一步逻辑,服务器必须严格遵循,否则可能被攻击者利用(如请求走私)。
3. RFC与请求走私漏洞的关系
请求走私的本质是**“前后端解析不一致”**,而这种不一致往往是因为违反了RFC规则。例如:
场景1:CL.TE攻击(前端用Content-Length,后端用Transfer-Encoding)
攻击请求:
1
2
3
4
5
6POST /
Content-Length: 6 # 前端按此长度截断
Transfer-Encoding: chunked
0\r\n\r\n # 后端按分块解析到此处结束
GET /admin HTTP/1.1 # 前端忽略,后端视为新请求RFC规则:
RFC规定Transfer-Encoding
优先级高于Content-Length
。如果后端严格遵循RFC,应忽略Content-Length
,但某些服务器未遵守此规则,导致解析差异。
场景2:TE.TE攻击(前后端均用Transfer-Encoding,但解析不一致)
攻击请求:
1
2
3
4
5
6POST /
Transfer-Encoding: chunked
Transfer-Encoding: xchunked # 混淆头
0\r\n\r\n
GET /admin HTTP/1.1RFC规则:
RFC规定多个Transfer-Encoding
头时,需按顺序处理(如chunked
必须最后)。若后端错误解析,可能将GET
视为新请求。
4. 为什么混淆请求能绕过防御?
RFC虽然严格,但部分服务器实现存在“容错性”,导致攻击者可构造看似非法但实际被部分服务器接受的请求。例如:
- 分块扩展:
0;x=1\r\n\r\n
(RFC允许分块后加注释,但某些服务器可能忽略注释)。 - 非法换行符:用
\n
代替\r\n
(RFC要求\r\n
,但某些服务器接受\n
)。
这些“模糊点”让攻击者能构造混淆请求,诱导服务器错误解析。
总结:RFC与请求解析的关系
角色 | 作用 | 实际影响 |
---|---|---|
RFC | 定义HTTP请求的合法格式和解析逻辑。 | 确保全球服务器和客户端能互相理解。 |
服务器解析 | 按RFC规则拆解请求内容,提取方法、URL、头部、正文等。 | 若实现与RFC不一致,可能导致漏洞(如走私)。 |
攻击者 | 构造混淆请求,利用服务器解析漏洞,突破RFC的预期逻辑。 | 通过前后端解析差异实现请求走私。 |
类比理解
- RFC:像乐高说明书,规定每块积木如何拼接。
- 请求解析:像你按说明书拼乐高,如果某一步拼错(不符合RFC),最终模型可能不稳固(漏洞)。
- 攻击者:像故意拼错积木的人,利用某些连接点的松动(服务器解析漏洞),让模型变成危险的陷阱。
理解RFC规则,就能明白为什么某些请求能走私成功,以及如何防御(强制所有服务器严格遵循RFC)。
请求隧道攻击的概念讲解:
概念讲解:
通俗易懂版:HTTP请求隧道攻击详解
1. 什么是HTTP请求隧道?
想象你寄快递时,快递员(前端服务器)负责检查包裹内容,但实际送货的是另一位快递员(后端服务器)。请求隧道就是攻击者把“违禁品”(恶意请求)藏在正常包裹里,让快递员A(前端)不检查,直接转交给快递员B(后端),从而绕过安检。
2. 隧道 vs 传统走私的区别
- 传统请求走私:污染快递分拣队列,让别人的包裹错发到你的地址。
- 请求隧道:直接在包裹里藏一个“小包裹”(隧道请求),让后端处理隐藏的请求,而前端浑然不知。
3. 为什么能绕过前端安全措施?
- 前端只做表面检查:比如检查快递单号(URL路径),但不拆开包裹看内容(请求体)。
- 后端老实处理所有内容:只要包裹送到,后端会拆开所有隐藏的小包裹(隧道请求)。
🌰 生活化案例
攻击场景:某网站禁止普通用户访问
/admin
,但后端实际存在该接口。攻击步骤
:
- 你发送一个“正常包裹”(合法请求),里面藏了一个“小包裹”(
GET /admin
)。 - 前端检查快递单号(URL)没问题,转交给后端。
- 后端拆开包裹,处理两个请求:正常请求 + 隐藏的
/admin
请求。 - 后端把两个响应打包返回,但前端只看到正常响应,隐藏的
/admin
数据被你窃取。
- 你发送一个“正常包裹”(合法请求),里面藏了一个“小包裹”(
🔧 技术细节拆解
HTTP/1 与 HTTP/2 的隧道差异
对比项 HTTP/1 环境 HTTP/2 环境 检测难度 高(响应混杂,难区分) 低(每个请求/响应有独立“流”) 利用特征 响应可能包含多个混杂内容 响应中可明显看到HTTP/1格式的“走私响应” 示例 前端返回的数据里夹带私货(需手动解析) 直接看到类似 HTTP/1.1 200 OK
的完整响应头
盲隧道 vs 非盲隧道
盲隧道(你蒙着眼攻击)
特征
:
- 前端只返回主请求的响应,隐藏请求的响应被忽略。
- 攻击者无法直接看到隐藏请求的结果。
利用场景
:
- 触发后台操作(如删除数据、发送邮件)。
- 需要间接验证(如观察系统状态变化)。
非盲隧道(睁着眼攻击)
特征
:
- 隐藏请求的响应会“泄露”到主请求的响应中。
- 攻击者可直接窃取数据(如Cookie、敏感页面)。
示例攻击
:
- 构造一个
HEAD
请求包裹,隐藏GET /admin
请求。 - 前端认为
HEAD
请求的响应长度应为131字节。 - 后端返回
HEAD
响应 +/admin
的完整响应(总长度超过131)。 - 前端只读取前131字节,但攻击者能从后续字节中提取
/admin
数据。
- 构造一个
⚡ 攻击步骤(以非盲隧道为例)
1. 构造隧道请求
1
2
3
4
5
6
7http复制HEAD /comment HTTP/2
Host: vulnerable-website.com
A: 1
GET /tunnelled HTTP/1.1
Host: vulnerable-website.com
X: x关键点
:
- 主请求是
HEAD /comment
(前端认为无响应体)。 - 隐藏请求是
GET /tunnelled
(后端会处理并返回数据)。
- 主请求是
2. 分析响应
正常响应
(无隧道):
1
2
3http复制HTTP/2 200 OK
Content-Length: 131
(空响应体,因为HEAD请求)隧道成功响应
:
1
2
3
4
5
6
7
8
9
10http复制HTTP/2 200 OK
Content-Length: 131 # 前端只读到这里
HTTP/1.1 200 OK # 隐藏响应的内容从这里开始泄露
Content-Type: text/html
Content-Length: 4286
<!DOCTYPE html>
<h1>Tunnelled</h1>
<p>This is a tunnelled respo...攻击者收获:窃取了
/tunnelled
页面的前131字节内容。
🛡️ 防御措施
严格校验请求边界
:
- 禁止同时包含
Content-Length
和Transfer-Encoding
头。
- 禁止同时包含
禁用连接复用
:
- 前后端之间强制使用
Connection: close
。
- 前后端之间强制使用
隔离用户请求
:
- 确保同一连接仅处理同一用户的请求(通过IP或Session绑定)。
监控异常响应
:
- 检测响应中是否包含其他请求的数据(如HTTP/1响应混入HTTP/2流)。
📌 总结
- 请求隧道是高级走私技术:专攻前端“偷懒检查”的弱点,让后端处理隐藏请求。
- HTTP/2更易检测:因响应格式清晰,而HTTP/1需手动“挖宝”。
- 防御核心:前后端统一解析规则 + 严格隔离用户请求。
1. 什么是“隧道”(Tunneling)?
在HTTP请求走私中,“隧道”指将恶意请求隐藏在合法请求中,利用前后端服务器对协议解析的差异,绕过前端安全检查,使后端处理隐藏的请求。核心特征:
- 一个请求通道传输多个请求:前端认为只发送了一个请求,后端却处理了多个请求。
- 隐藏请求的执行结果:恶意请求的响应可能被前端忽略或部分截取,但攻击效果已达成。
📌 攻击步骤中的隧道体现
阶段1:CRLF注入(确认漏洞)
操作:在Header名中插入
\r\n
,覆盖Host
头。隧道体现
:
- 前端视角:只看到一个Header(
foo: bar\r\nHost: abc
)。 - 后端视角:解析为两个Header(
foo: bar
和Host: abc
),说明通过一个请求传递了篡改后的请求信息。 - 本质:在单个请求中“夹带”额外指令,验证了隧道可行性。
- 前端视角:只看到一个Header(
阶段2:构造POST请求走私认证头
操作:注入
Content-Length:500
头,填充超长请求体。隧道体现
:
前端视角:认为这是一个完整的POST请求(长度500字节)。
后端视角
:
- 解析到
Content-Length:500
后,期望接收500字节的请求体。 - 实际请求体长度不足500字节,后端会等待后续数据,导致后续请求被拼接。
- 解析到
结果
:
- 后端将下一个请求(如用户的其他请求)拼接到当前请求体中。
- 攻击者通过观察响应,发现前端添加的认证头(如
X-FRONTEND-KEY
),证明后端处理了多个请求。
- 隧道作用:通过单次请求污染后端请求队列,窃取敏感信息。
阶段3:构造HEAD请求隧道访问/admin
操作:在Header名中注入完整的
GET /admin
请求。隧道体现
:
前端视角
:
- 认为这是一个合法的HEAD请求(无请求体)。
- 头部包含
foo: bar\r\n...GET /admin...
,但前端不解析Header名中的换行符。
后端视角
:
解析Header名时发现
1
\r\n
,将其视为
两个独立请求
:
1
2http复制HEAD / HTTP/1.1 # 主请求
GET /admin HTTP/1.1 # 隐藏请求(隧道)
结果
:
- 后端处理
GET /admin
请求,返回管理页面内容。 - 前端只读取HEAD请求的响应(固定长度),但攻击者从响应截断处获取部分敏感数据。
- 后端处理
- 隧道作用:通过一个请求触发后端执行隐藏的高权限操作。
阶段4:删除用户(最终攻击)
操作:隧道请求路径改为
/admin/delete?username=carlos
。隧道体现
:
前端视角:仍认为是一个HEAD请求,无副作用。
后端视角
:处理了两个请求:
1
2http复制HEAD /login HTTP/1.1 # 主请求(伪装)
GET /admin/delete?... HTTP/1.1 # 隐藏请求(实际攻击)结果:用户
carlos
被删除,实验完成。隧道作用:完全隐藏攻击行为,前端无法感知。
🌰 生活化类比
- 快递员(前端)检查包裹:只检查快递单(请求头),不拆开包裹(请求体)。
- 攻击者(你):在快递单上写“内附贺卡”,实际在包裹里藏了“销毁货物指令”(隐藏请求)。
- 仓库(后端):拆开包裹后,执行“销毁指令”,但快递员只看到“贺卡已送达”。
- 结果:货物被销毁,但快递系统(前端)无记录。
🔍 总结:隧道的关键特征
- 请求嵌套:一个外层请求包裹内层恶意请求。
- 解析差异:前端和后端对请求边界的理解不同。
- 隐蔽执行:恶意请求的响应被前端忽略或部分截断,但攻击效果生效。
⚡ 快速自查:哪里用到了隧道?
- 注入额外请求头(如
GET /admin
) → 隧道传递恶意指令。 - 修改协议或方法(如HEAD改为POST) → 掩盖隧道请求。
- 利用响应截断 → 隐藏隧道请求的完整响应。
只有同时满足以上三点,才是完整的隧道攻击!