為什么我抓不到Baidu的數(shù)據(jù)包,你會嗎?
最近,有位讀者問起一個奇怪的事情,他說他想抓一個baidu.com的數(shù)據(jù)包,體驗下看包的樂趣。
但卻發(fā)現(xiàn)“抓不到”,這就有些奇怪了。
我來還原下他的操作步驟。
首先,通過ping命令,獲得訪問百度時會請求哪個IP。
從上面的結(jié)果可以知道請求baidu.com時會去訪問39.156.66.10。
于是用下面的tcpdump命令進行抓包,大概的意思是抓eth0網(wǎng)卡且ip為39.156.66.10的網(wǎng)絡(luò)包,保存到baidu.pcap文件中。
此時在瀏覽器中打開baidu.com網(wǎng)頁?;蛘咴诹硗庖粋€命令行窗口,直接用curl命令來模擬下。
按理說,訪問baidu.com的數(shù)據(jù)包肯定已經(jīng)抓下來了。
然后停止抓包。
再用wireshark打開baidu.pcap文件,在過濾那一欄里輸入http.host == "baidu.com"。
此時發(fā)現(xiàn),一無所獲。
在wireshark中搜索baidu的包,發(fā)現(xiàn)一無所獲
這是為啥?
到這里,有經(jīng)驗的小伙伴,其實已經(jīng)知道問題出在哪里了。
為什么沒能抓到包
這其實是因為他訪問的是HTTPS協(xié)議的baidu.com。HTTP協(xié)議里的Host和實際發(fā)送的request body都會被加密。
正因為被加密了,所以沒辦法通過http.host進行過濾。
但是。
雖然加密了,如果想篩選還是可以篩的。
HTTPS握手中的Client Hello階段,里面有個擴展server_name,會記錄你想訪問的是哪個網(wǎng)站,通過下面的篩選條件可以將它過濾出來。
通過tls的擴展server_name可以搜索到baidu的包
此時選中其中一個包,點擊右鍵,選中Follow-TCP Stream。
右鍵找到tcp 流
這個TCP連接的其他相關(guān)報文全都能被展示出來。
HTTPS抓包
從截圖可以看出,這里面完整經(jīng)歷了TCP握手和TLS加密握手流程,之后就是兩段加密信息和TCP揮手流程。
可以看出18號和20號包,一個是從端口56028發(fā)到443,一個是443到56028的回包。
一般來說,像56028這種比較大且沒啥規(guī)律的數(shù)字,都是客戶端隨機生成的端口號。
而443,則是HTTPS的服務(wù)器端口號。
HTTP用的是80端口,如果此時對著80端口抓包,也會抓不到數(shù)據(jù)。
粗略判斷,18號和20號包分別是客戶端請求baidu.com的請求包和響應(yīng)包。
點進去看會發(fā)現(xiàn)URL和body都被加密了,一無所獲。
那么問題就來了。有沒有辦法解密里面的數(shù)據(jù)呢?
有辦法。我們來看下怎么做。
解密數(shù)據(jù)包
還是先執(zhí)行tcpdump抓包。
然后在另外一個命令行窗口下執(zhí)行下面的命令,目的是將加密的key導(dǎo)出,并給出對應(yīng)的導(dǎo)出地址是??/Users/xiaobaidebug/ssl.key?
?。
然后在同一個命令行窗口下,繼續(xù)執(zhí)行curl命令或用命令行打開chrome瀏覽器。目的是為了讓curl或chrome繼承這個環(huán)境變量。
此時會看到在/Users/xiaobaidebug/下會多了一個ssl.key文件。
這時候跟著下面的操作修改wireshark的配置項。
打開wireshark的配置項
找到Protocols之后,使勁往下翻,找到TLS那一項。
在配置項中找到Protocols
將導(dǎo)出的ssl.key文件路徑輸入到這里頭。
在Protocols中找到TLS那一欄
點擊確定后,就能看到18號和20號數(shù)據(jù)包已經(jīng)被解密。
解密后的數(shù)據(jù)包內(nèi)容
此時再用http.host == "baidu.com",就能過濾出數(shù)據(jù)了。
解密后的數(shù)據(jù)包中可以過濾出baidu的數(shù)據(jù)包
到這里,其實看不了數(shù)據(jù)包的問題就解決了。
但是,新的問題又來了。
ssl.key文件是個啥?
這就要從HTTPS的加密原理說起了。
HTTPS握手過程
HTTPS的握手過程比較繁瑣,我們來回顧下。
先是建立TCP連接,畢竟HTTP是基于TCP的應(yīng)用層協(xié)議。
在TCP成功建立完協(xié)議后,就可以開始進入HTTPS階段。
HTTPS可以用TLS或者SSL啥的進行加密,下面我們以??TLS1.2?
?為例。
總的來說。整個加密流程其實分為兩階段。
第一階段是TLS四次握手,這一階段主要是利用非對稱加密的特性各種交換信息,最后得到一個"會話秘鑰"。
第二階段是則是在第一階段的"會話秘鑰"基礎(chǔ)上,進行對稱加密通信。
TLS四次握手
我們先來看下第一階段的TLS四次握手是怎么樣的。
第一次握手:
- Client Hello:是客戶端告訴服務(wù)端,它支持什么樣的加密協(xié)議版本,比如 TLS1.2,使用什么樣的加密套件,比如最常見的RSA,同時還給出一個客戶端隨機數(shù)。
第二次握手:
- Server Hello:服務(wù)端告訴客戶端,服務(wù)器隨機數(shù) + 服務(wù)器證書 + 確定的加密協(xié)議版本(比如就是TLS1.2)。
第三次握手:
- Client Key Exchange: 此時客戶端再生成一個隨機數(shù),叫 pre_master_key 。從第二次握手的服務(wù)器證書里取出服務(wù)器公鑰,用公鑰加密 pre_master_key,發(fā)給服務(wù)器。
- Change Cipher Spec: 客戶端這邊已經(jīng)擁有三個隨機數(shù):客戶端隨機數(shù),服務(wù)器隨機數(shù)和pre_master_key,用這三個隨機數(shù)進行計算得到一個"會話秘鑰"。此時客戶端通知服務(wù)端,后面會用這個會話秘鑰進行對稱機密通信。
- Encrypted Handshake Message:客戶端會把迄今為止的通信數(shù)據(jù)內(nèi)容生成一個摘要,用"會話秘鑰"加密一下,發(fā)給服務(wù)器做校驗,此時客戶端這邊的握手流程就結(jié)束了,因此也叫Finished報文。
第四次握手:
- Change Cipher Spec:服務(wù)端此時拿到客戶端傳來的 pre_master_key(雖然被服務(wù)器公鑰加密過,但服務(wù)器有私鑰,能解密獲得原文),集齊三個隨機數(shù),跟客戶端一樣,用這三個隨機數(shù)通過同樣的算法獲得一個"會話秘鑰"。此時服務(wù)器告訴客戶端,后面會用這個"會話秘鑰"進行加密通信。
- Encrypted Handshake Message:跟客戶端的操作一樣,將迄今為止的通信數(shù)據(jù)內(nèi)容生成一個摘要,用"會話秘鑰"加密一下,發(fā)給客戶端做校驗,到這里,服務(wù)端的握手流程也結(jié)束了,因此這也叫Finished報文。
四次握手中,客戶端和服務(wù)端最后都擁有三個隨機數(shù),他們很關(guān)鍵,我特地加粗了表示。
第一次握手,產(chǎn)生的客戶端隨機數(shù),叫client random。
第二次握手時,服務(wù)器也會產(chǎn)生一個服務(wù)器隨機數(shù),叫server random。
第三次握手時,客戶端還會產(chǎn)生一個隨機數(shù),叫pre_master_key。
這三個隨機數(shù)共同構(gòu)成最終的對稱加密秘鑰,也就是上面提到的"會話秘鑰"。
三個隨機數(shù)生成對稱秘鑰
你可以簡單的認為,只要知道這三個隨機數(shù),你就能破解HTTPS通信。
而這三個隨機數(shù)中,client random? 和 server random? 都是明文的,誰都能知道。而?pre_master_key卻不行,它被服務(wù)器的公鑰加密過,只有客戶端自己,和擁有對應(yīng)服務(wù)器私鑰的人能知道。
所以問題就變成了,怎么才能得到這個?pre_master_key??
怎么得到pre_master_key
服務(wù)器私鑰不是誰都能拿到的,所以問題就變成了,有沒有辦法從客戶端那拿到這個pre_master_key。
有的。
客戶端在使用HTTPS與服務(wù)端進行數(shù)據(jù)傳輸時,是需要先基于TCP建立HTTP連接,然后再調(diào)用客戶端側(cè)的TLS庫(OpenSSL、NSS)。觸發(fā)TLS四次握手。
這時候如果加入環(huán)境變量SSLKEYLOGFILE就可以干預(yù)TLS庫的行為,讓它輸出一份含有pre_master_key?的文件。這個文件就是我們上面提到的/Users/xiaobaidebug/ssl.key。
將環(huán)境變量注入到curl和chrome中
但是,雖然TLS庫支持導(dǎo)出key文件。但前提也是,上層的應(yīng)用程序在調(diào)用TLS庫的時候,支持通過SSLKEYLOGFILE環(huán)境觸發(fā)TLS庫導(dǎo)出文件。實際上,也并不是所有應(yīng)用程序都支持將SSLKEYLOGFILE。只是目前常見的curl和chrome瀏覽器都是支持的。
SSLKEYLOGFILE文件內(nèi)容
再回過頭來看ssl.key文件里的內(nèi)容。
這里有三列。
第一列是CLIENT_RANDOM,意思是接下來的第二列就是客戶端隨機數(shù),再接下來的第三列則是pre_master_key。
但是問題又來了。
這么多行,wireshark怎么知道用哪行的pre_master_key呢?
wireshark?是可以獲得數(shù)據(jù)報文上的client random的。
比如下圖這樣。
Client Hello 里的客戶端隨機數(shù)
注意上面的客戶端隨機數(shù)是以 "bff63bbe5"結(jié)尾的。
同樣,還能在數(shù)據(jù)報文里拿到server random。
找到server random
此時將client random放到ssl.key的第二列里挨個去做匹配。
就能找到對應(yīng)的那一行記錄。
ssl.key里的數(shù)據(jù)
注意第二列的那串字符串,也是以 "bff63bbe5"?結(jié)尾的,它其實就是前面提到的client random。
再取出這一行的第三列數(shù)據(jù),就是我們想要的pre_master_key。
那么這時候wireshark就集齊了三個隨機數(shù),此時就可以計算得到會話秘鑰,通過它對數(shù)據(jù)進行解密了。
反過來,正因為需要客戶端隨機數(shù),才能定位到ssl.key?文件里對應(yīng)的pre_master_key?是哪一個。而只有TLS第一次握手(client hello)的時候才會有這個隨機數(shù),所以如果你想用解密HTTPS包,就必須將TLS四次握手能抓齊,才能進行解密。如果連接早已經(jīng)建立了,數(shù)據(jù)都來回傳好半天了,這時候你再去抓包,是沒辦法解密的。
總結(jié)
- 文章開頭通過抓包baidu的數(shù)據(jù)包,展示了用wireshark抓包的簡單操作流程。
- HTTPS會對HTTP的URL和Request Body都進行加密,因此直接在
filter欄
進行過濾http.host == "baidu.com"
會一無所獲。 - HTTPS握手的過程中會先通過非對稱機密去交換各種信息,其中就包括3個隨機數(shù),再通過這三個隨機數(shù)去生成對稱機密的會話秘鑰,后續(xù)使用這個會話秘鑰去進行對稱加密通信。如果能獲得這三個隨機數(shù)就能解密HTTPS的加密數(shù)據(jù)包。
- 三個隨機數(shù),分別是客戶端隨機數(shù)(client random),服務(wù)端隨機數(shù)(server random)以及pre_master_key。前兩個,是明文,第三個是被服務(wù)器公鑰加密過的,在客戶端側(cè)需要通過SSLKEYLOGFILE去導(dǎo)出。
- 通過設(shè)置SSLKEYLOGFILE環(huán)境變量,再讓curl或chrome會請求HTTPS域名,會讓它們在調(diào)用TLS庫的同時導(dǎo)出對應(yīng)的sslkey文件。這個文件里包含了三列,其中最重要的是第二列的client random信息以及第三列的pre_master_key。第二列client random用于定位,第三列pre_master_key用于解密。