作者:V 合天智匯
前言
最近在分組密碼學習的過程中,瞭解了AES分組密碼以及分組密碼的五種操作模式。目前已知的對AES的分析攻擊都比蠻力攻擊的複雜度要高。所以這裡我們跳過AES加密本身,看看這五種操作模式的原理以及是否會因為一些操作不當而產生弱點。
操作模式
分組密碼不僅僅是一個加密算法,也是用於實現多種不同密碼學編碼機制的萬能元件。例如,人們可以使用分組密碼構建各種不同種類的基於分組的加密方案,甚至還可以用它來實現序列密碼。其中,不同的加密方式就稱為操作模式。目前包括五種主流的操作模式:
電子密碼本模式(electronic code book mode, ECB) 密碼分組鏈接模式(cipher block chaining mode, CBC) 密碼反饋模式(cipher feedback mode, CFB) 輸出反饋模式(output feedback mode, OFB) 計數器模式(Counter mode, CTR)
電子密碼本模式(EBC)
電子密碼本模式是一種最直接的消息加密方式,
<code>假設e~k~()表示用密鑰k進行的分組加密;e~k~^-1^()對應解密。 x~i~,y~i~表示長度為b的位字符串,(長度不夠的字符串會按照指定的填充規則進行填充) 加密:y~i~=e~k~(x~i~) ,i≥1 (e~k~()表示用密鑰k進行的分組加密) 解密:x~i~=e~k~^-1^(y~i~) ,i≥1 (e~k~^-1^()表示用密鑰k進行的分組加密的解密)/<code>
電子密碼本模式就是最簡單的一一映射加密,這意味著每一組的加密都是獨立的,所以EBC模式加密可以實現並行化,這種方式在高速實現中具有很強的優勢。並且,如果由於傳輸問題導致消息中間有一段數據受影響,也不會影響到後面的消息,這與後面要提到的CBC,CFB等模式是不同的。
缺陷
正因為EBC模式的一一映射加密,每一組加密相互獨立,所以同樣的明文用同樣的密鑰就會加密得到同樣的密文。下面介紹EBC模式下的一種簡單攻擊方式:
代換攻擊
假設某個協議支持銀行間的電匯,電匯包含五個字段,分別有五個分組
首先一個前提是我們能監聽,攔截並更改這個電匯的密文,
然後我們可以先開個賬戶,進行若干次試驗,每一次變更一個參數,比如發送賬號,轉賬金額,一組密碼的位數等,以此來確定每一個分組的意義。
等我們大概知道五個分組的意義後,我們就能進行替換攻擊。比如銀行A的某個賬戶向銀行B的某個賬戶轉賬,我我們將其攔截,把接受賬號密文代換成事先存好的我們賬號的密文,則銀行用key解密後,就會把錢都轉到我們的賬戶下。值得一提的是,整個過程我們並沒有攻擊分組密碼本身,所以儘管加密方案本身再安全也抵禦不了這種攻擊;我們甚至也不知道真正明文的內容,所以實現代換攻擊是破壞了消息的完整性。
例子很特殊,比如五個參數剛好在五個分組等,只是為了方便理解~
防禦
現實中抵禦代換攻擊常用消息驗證碼(Message Authentication Code, MAC),和數字簽名,主要就是為了確保消息的完整性。
CTF中EBC相關的題目-2019OGeek-Babycry
前面提到,分組加密中長度不夠的字符串會按照指定的填充規則進行填充,再加上ECB這樣一一映射的明文-密文對關係,就會產生利用pad來逐位爆破明文的攻擊。
nc 過去得到加密規則
加密規則是des(key,sth..flag{*}padding) (模式可以類比為ECB的一一映射)
加上hint
所以我們控制輸入,可以使得被加密的字符末尾為
<code>}_____
__(7個_
)/<code>
比如key加上flag是50位,並且flag以‘}’ 結尾。他是8位一組,那麼我們就填上7個a,這樣key+sth+flag就有57位,八位一組,前面56位字符分8組,於是最後一組加上填充就是
<code>}_______(7個'_'
)/<code>
然後服務器會返回給我們密文,於是密文的最後一組就代表著
<code>}_______(7個'_'
)/<code>
的密文,
如果我們再多填一個a,那麼返回密文中最後一組就是
<code>?}
______
(6
個'_'
)/<code>
於是我們就開始爆破,我們先測出key的長度先,(看填幾個a會密文會多出一組來就能知道)
然後填a,讓 len(key)+len(a)%8 == 0,接著,我們就發送aaa*}______(6個'_'),(a的個數由key的長度決定,*代表任意字符,我們直接遍歷可見字符表)讓服務返回加密結果,我們找到明文對應的那一組密文,和之前返回的密文①做比較,結果一樣,說明那位字符正確,開始爆破下一位字符。
以此類推可以逐位爆破出所有位字符。(只是爆破超過八位後,用來作為判斷依據的密文①就不能再從密文最後一組找了,得從到數第二組,第三組....找~)
密碼分組鏈接模式(CBC)
在CBC模式中,後一組的密文都受前一組密文影響,第一組密文受初始向量IV影響,從而明文的重複排列不會反應在密文中。
<code>假設e~k~()表示用密鑰k進行的分組加密;e~k~^-1^()對應解密。 x~i~,y~i~表示長度為b的位字符串,IV表示長度為b的初始向量,(長度不夠的字符串會按照指定的填充規則進行填充) 加密:y~1~=e~k~(x~1~ ⊕ IV) y~i~=e~k~(x~i~ ⊕ y~i-1~) ,i≥2 解密:x~1~=e~k~^-1^(y~1~)⊕ IV x~i~=e~k~^-1^(y~i~) ⊕ y~i-1~ ,i≥2 /<code>
如果每次加密是都選擇一個新的IV,則CBC模式就變成了一個概率加密方案,即相同的明文加密會生成不同的密文。
需要說明的是,如果每次選擇IV的數值得當,則代換攻擊會徹底失效,因為監聽者無法識別加密的模式。但是如果若干次加密使用相同的IV,則監聽者還是可以識別加密模式。
雖然(承接ECB的例子)這一次直接將我們的賬戶的密文替換上去已經不能實現向我們賬戶轉賬的效果,因為我們代換,會導致第四、五組消息都被破壞,但是有可能解密後,賬戶和金額仍然具有意義呢?(雖然可能性似乎很低)只是是隨機的,不再可控了而已。但這仍然是銀行所不可接受的。所以消息驗證碼和數字簽名的作用顯得尤為重要。
缺陷
因為某些原因,使得某一組密文中出現了錯誤的bit,將影響這一組(整組)及其之後一組(相應位)分組的解密。由此產生了CBC反轉字節攻擊。另外與下面所講的CFB模式一樣,CBC模式也不能抵禦重放攻擊。
CBC反轉字節攻擊(Modification attack on CBC)
在加密時,前一組明文會影響後面所有分組的密文。
但是在解密時,由公式及原理圖可知,一組密文只會影響這一組的解密結果和下一組的解密結果。
我們看到箭頭2,這一組Ciphertext(密文2)在Decryption(解密)之後會生成Ciphertext_1(我yy的),然後這個Ciphertext_1與箭頭1所指的Ciphertext(密文1)按位異或後最終生成Plaintext(明文2)
所以我們可以修改Ciphertext1,來達到控制Plaintext2的效果,當然如果想要精確控制,則需要知道Plaintext2的具體內容。(在某些特定條件下,Plaintext2的內容可以利用Padding Oracle Attack 來獲取,下文會簡略介紹這種攻擊方式)
並且由於Ciphertext1的修改,導致自己這一組的解密會受影響,所以還需要修改圖中的IV來給Ciphertext1“擦個屁股”。(如果前面不是IV呢?那隻能一路改下去,直到IV為止了。。。)
CTF中CBC反轉字節攻擊 應用場景
CBC反轉字節攻擊可能會用於Web題中的繞過。比如一個登陸,然後在傳輸過程中是將你提交的信息用分組密碼加密,模式是CBC,然後到服務端後再解密。
提交的時候他不讓你填寫admin,但是題目又需要你有admin的身份。這個時候就可以考慮用CBC反轉字節攻擊繞過過濾。
比如:先用bdmin的身份登陸,然後攔截、更改身份對應密文組的前一組的對應位,讓服務器將身份解密成明文後為admin,但是身份前一組的明文應該是毀掉了,那就再改前一組的密文來進行補救,直到改到初始向量IV為止。
Padding Oracle Attack
這裡粗略介紹一下CBC模式下的Padding Oracle Attack
在AES加密的CBC模式中,若加密數據的長度模除分塊長度而有餘,則會在末尾填充一組數據。
<code>即 (pad_length-len(data)%pad_length)*hex
(pad_length-len(data)%pad_length) /<code>
舉個栗子(8字節一組)
<code>數據: aaaaa 填充後的數據: aaaaa\x3\x3\x3 數據: aaaaaaaaaa 填充後的數據: aaaaaaaaaa\x06\x06\x06\x06\x06\x06 數據: aaaaaaaa 填充後的數據: aaaaaaaa\x08\x08\x08\x08\x08\x08\x08\x08/<code>
Padding Oracle Attack 需要兩個前提就是:
1.可以控制密文 或者 IV
2.如果解密後不滿足 padding 服務端會報錯. (只有最後一個塊長度不足時會有填充)
再次舉栗子:
此處選擇了AES-128作為例子,明文長度為17位,故補齊至32位後分2組進行加密。
然後我們偽造一組IV來和第一組密文一起發回服務器,讓其進行判定。
<code>明文:southseastisacat! IV:\x31\x32\x33\x34\x35\x36\x37\x38\x38\x37\x36\x35\x34\x33\x32\x31 密文:\xbf\x61\xb3\x1b\xd3\x6a\x47\x11\xb9\xcc\xd7\x11\x7b\x8a\x42\xd9\x3a\x1e\x37\xe9\xd2\xde\xfa\xca\xa2\x50\xfd\xc0\x6e\xf2\x11\xc1/<code>
對IV的最後一位字符進行爆破,構造0x00到0xff,使得第一組密文的最後一個字節解密的結果同IV異或結果為0x01,導致Padding通過,當IV最後一位字符是0x44時,服務器返回正確結果。
這時IV的最後一個字節和原IV的最後一個字節還有0x01異或,即0x44\^0x31\^0x01,得到結果為t ,是第一組明文的最後一個字符。
然後可以固定IV的最後一個值使得第一組密文的最後一個字節解密的結果同IV異或結果為0x02,此時爆破IV到數第二個字節,當第一組密文的到數第二個字節解密的結果同IV異或結果為0x02時,服務器返回正確結果,此時又能確定第一組明文的倒數第二個字符,以此類推~
Padding Oracle Attack
http://www.hetianlab.com/expc.do?ec=ECID4ba7-767b-40e1-93bd-c763e20a1dba
複製鏈接做實驗
密碼反饋模式(CFB)
與CBC類似,CFB模式每一組的密文也會影響後續所有組的密文,直接看加解密規則吧
<code>假設e~k~()表示用密鑰k進行的分組加密; x~i~,y~i~表示長度為b的位字符串,IV表示長度為b的初始向量(不需要填充) 加密:y~1~=e~k~(IV)⊕x~1~ y~i~=e~k~(y~i-1~)⊕x~i~ ,i≥2 解密:x~1~=e~k~(IV)⊕y~1~ x~i~=e~k~(y~i-1~)⊕y~i~ ,i≥2/<code>
加密上面確實感覺挺像,只是x~i~和⊕在括號裡還是在括號外的區別。但正因為這點小小的區別,導致解密上CFB模式仍然用的是分組加密的加密函數。
類似“CBC翻轉字節攻擊”,在CFB上也可進行類似的控制,只不過這次“擦屁股的人"是這一組密文後面的所有密文組了。
缺陷
與CBC類似,因為某些原因,使得某一組密文中出現了錯誤的bit,將影響這一組(相應位)及其之後一組(整組)分組的解密。CFB模式也不可抵禦重放攻擊。
重放攻擊
舉個栗子:
Alice向Bob發送一條消息,這條消息由4個密文分組組成。主動攻擊者Mallory將該消息中的後3個密文分組保存了下來。第二天,Alice又向Bob發送了內容不同的4個密文分組(假設Alice使用了相同的密鑰)。攻擊者用昨天保存下來的3個密文分別將今天發送的後3個密文分組進行了替換。
於是,Bob解密時,4個分組中只有第1個可以解密成正確的明文分組,第2個會出錯,而第3個和第4個則變成了被攻擊者替換的內容(也就是昨天發送的明文內容)。攻擊者沒有破解密碼,就成功地用以前的電文混入了新電文中。而第2個分組出錯到底是通信錯誤呢,還是被人攻擊所造成的呢?Bob是無法做出判斷的。
CTF中的CFB模式相關的題目-hgame2020-week3-Feedback
<code> import os, random import string, binascii import signal import socketserver from hashlib import sha256 from Crypto.Cipher import AES from secret import MESSAGE assert len(MESSAGE) ==48
class
Task
(socketserver
.BaseRequestHandler
):
def
__init__
(
self
, *args, **kargs):self
.KEY = b""
self
.IV = b""
super
().__init__
(*args, **kargs)def
_recvall
(
self
): BUFF_SIZE =2048
data = b''
while
True:
part =self
.request.recv(BUFF_SIZE) data += partif
len(part)BUFF_SIZE:
break
return
data.strip()
def
send
(
self
, msg, newline=True):try:
if
newline:
msg += b'\n'
self
.request.sendall(msg)except:
passdef
recv
(
self
, prompt=b'> '
):self
.send(prompt, newline=False)return
self
._recvall()def
encrypt
(
self
, data): assert len(data) %16
==0
aes = AES.new(self
.KEY, AES.MODE_CFB,self
.IV, segment_size=128
)return
aes.encrypt(data)def
decrypt
(
self
, data): assert len(data) %16
==0
aes = AES.new(self
.KEY, AES.MODE_CFB,self
.IV, segment_size=128
)return
aes.decrypt(data)def
handle
(
self
): signal.alarm(60
)self
.KEY = os.urandom(32
)self
.IV = os.urandom(16
)self
.send(b"You have only 3 times to decrypt sth, then I'll give u the FLAG."
)try:
for
_
in
range(3
):self
.send(b"Give me sth(hex) to decrypt"
) hex_input =self
.recv()if
not
hex_input:
break
ciphertext = binascii.unhexlify(hex_input) plaintext =self
.decrypt(ciphertext)self
.send( binascii.hexlify(plaintext) )except:
self
.send(b"Rua!!!"
)self
.request.close() enc_msg =self
.encrypt(MESSAGE)self
.send(b"Here is your encrypted FLAG(hex): "
, newline=False)self
.send( binascii.hexlify(enc_msg) )self
.request.close()class
ForkedServer
(socketserver
.ForkingMixIn
,socketserver
.TCPServer
): passif
__name__
=="__main__"
: HOST, PORT ='0.0.0.0'
,1234
server = ForkedServer((HOST, PORT), Task) server.allow_reuse_address = True server.serve_forever()/<code>
可蒐集到的信息:
MESSAGE (flag) 的長度為48位,三組,每組16位。
程序的功能:AES加密,CFB模式。每次連接會隨機生成一個IV和key,然後最多可以對我的輸入進行三次解密,最後會把flag用同樣的IV和key加密,然後發送給我們。
解題思路:
首先列出加密解密的流程,由於只有三組,都寫出來會比較好看些。
需要搞清楚的是,在三次解密階段,我們的輸入是圖中左側的y1,y2,y3;在flag加密階段,flag是圖中右側的f1,f2,f3
步驟1:拿第一段flag(稱為flag1):
既然程序會對我們的輸入進行解密,我們發送一串與flag密文等長的密文過去,程序解密後返回一段明文。
看到解密第一條,我們只需要將明文和密文的前16字節逐位異或就能得到e~k~(IV)的值。
而三次解密之後程序返回的flag的密文的第一組就是flag1與e~k~(IV)異或的結果。所以我們拿著e~k~(IV)同flag的密文的第一組異或就能得到flag1。
步驟2:拿第二段flag(稱為flag2):
看到加密那一側,flag2是flag的明文同e~k~(y~1~)異或的結果,那麼我們的目的就是拿到e~k~(y~1~)
我們現在是手持flag1的人,我們現把將要發送的48字節劃分為三段,flag1:pad1:pad2
在經過第一次解密後,我們的得到的結果的組成是:
flag1與e~k~(IV)異或的結果f1
pad1與e~k~(flag1)異或的結果f2
pad2與e~k~(pad1)異或的結果f3
這裡我們需要轉變思想的是,在加密階段,flag1與e~k~(IV)異或的結果就是y1,所以我們現在已經拿到y1了
然後我們只需要將我們要發送的48字節劃分為三段:y1:pad1:pad2
我們得到的結果的組成就是:
y1與e~k~(IV)異或的結果f1
pad1與e~k~(y1)異或的結果f2
pad2與e~k~(pad1)異或的結果f3
現在,我們只需要將f2和pad1異或就能提取出e~k~(y1)了。然後就可以拿去解密flag2了。
步驟3:拿第三段flag(稱為flag3)
現在我們是手持flag1和flag2的人了。為了拿到flag3,我們需要拿到上圖右側的e~k~(y2)
而上圖右側y2是flag2和e~k~(y1)異或的結果,flag2我們有了,e~k~(y1)我們也能拿到,那就沒啥困難了。
首先重複第二步驟的操作拿到e~k~(y1),然後我們將e~k~(y1)和flag2異或得到y2
然後我們發送的48字節的組成為:pad1:y2:pad2
這樣我們得到的結果的組成就是:
pad1與e~k~(IV)異或的結果f1
y2與e~k~(pad1)異或的結果f2
pad2與e~k~(y2)異或的結果f3
現在我們只需要將f3和pad2異或就能提取出e~k~(y2)了。然後就可以拿去解密flag3了。
輸出反饋模式(OFB)
OFB模式形成了一個同步序列密碼,因為此密鑰序列既不依賴明文,也不依賴密文。同CFB模式相同是,接收者不需要使用分組密碼的解密函數來解密密文。
加解密規則
<code>假設e~k~()表示用密鑰k進行的分組加密; x~i~,y~i~,s~i~表示長度為b的位字符串,IV表示長度為b的初始向量(不需要填充) 加密:s~1~=e~k~(IV) 且y~1~=s~1~⊕x~1~ s~i~=e~k~(s~i-1~) 且y~i~=s~i~⊕x~i~ ,i≥2 解密:s~1~=e~k~(IV) 且x~1~=s~1~⊕y~1~ s~i~=e~k~(s~i-1~) 且x~i~=s~i~⊕y~i~ ,i≥2/<code>
OFB模式並不是通過密碼算法對明文間接進行加密的,而是通過將“明文分組”和“密碼算法的輸出”進行XOR來產生“密文分組”的,在這一點上OFB模式和CFB模式十分類似。
而且OFB 可事前進行加密、解密的預備。與ECB不同的是,雖然每一組加密也相互獨立,但明文的重複排列不會反應在密文中。因為一般情況下每次加密都會使用新的IV,所以使得OFB能夠抵禦重放攻擊
缺陷
由於每一組的加解密獨立,這降低了反轉字節這樣的主動攻擊的成本。(不需要找”擦屁股的人“了)
計數器模式(CTR)
感覺上是OFB模式的升級版
加解密規則:
<code>假設e~k~()表示用密鑰k進行的分組加密; x~i~,y~i~表示長度為b的位字符串,初始值IV和計數器CTR
~i~的連接表示位表示為(IV ||CTR
~I~),也是一個長度為b位的位字符串(不需要填充) 加密: y~1
~=e~k~(IV ||CTR
~i~) ⊕x~1
~ ,i≥1
解密: x~1
~=e~k~(IV ||CTR
~i~) ⊕y~1
~ ,i≥1
/<code>
解釋一下這個密鑰序列是如何生成:
比如128位的AES。首先選擇一個IV,長度小於分組長度(比如96位)。而剩下的32位則由計數器使用,並且該計數器的CTR值初始化為0。在會話期間加密的每個分組,計數器都會遞增而IV則保持不變。在本例中,在不更換IV的情況下可加密的最大分組個數為2^32^。由於每個分組長度都是8個字節,所以在生成一個新的IV前,可以加密的最大數據大小為8x2^32^=2^35^字節,大概為32G字節。
與OFB模式和CFB模式一樣,CTR模式的密鑰序列也是分組計算,但最吸引人的一個特點就是可以並行化,因為計數器模式不需要任何反饋,這與OFB或CFB模式完全不同。所以,我們可以讓兩個分組密碼引擎同時並行工作,即讓兩個引擎同時使用第一個分組密碼加密計數器值CTR和CTR2。 等這兩個分組密碼引擎完成後,第一個引擎將繼續加密值CTR3,而另一個引擎則繼續加密CTR,如此循環。
這種方案的加密速率是單個實現方式的兩倍。當然,也可以同時運行多個分組密碼引擎,這也會使加密速率按比例增加。對吞吐率要求嚴格的應用而言,比如在要求幾Gb/s數據率的網絡中,並行化的加密模式非常合適。
缺陷
與OFB模式相同,降低了反轉字節這樣的主動攻擊的成本。
總結
總的來說,感覺現在用的比較多的還是CBC加密模式,CTF中OFB加密模式和CTR加密模式的賽題也不常見。
但對分組加密的五種主流加密模式及其一些攻擊手法的學習,可以讓我們瞭解這五種模式的各自的優缺點,這樣,就算在以後的CTF的賽題中遇到,我們也好有的放矢。
參考
《深入淺出密碼學——常用加密技術原理與應用》(清華大學出版社)
https://blog.csdn.net/csu_vc/article/details/79619309
https://blog.csdn.net/chengqiuming/article/details/82355772
https://www.dazhuanlan.com/2019/09/02/5d6c9d6f823a7/
CBC字節翻轉攻擊
http://hetianlab.com/expc.do?ce=718fded3-e2d8-4ad0-91f0-e1f0d44082ea
(瞭解CBC模式實現流程、異或運算的高級應用、python中crypto庫的使用以及cbc字節翻轉攻擊的原理與代碼實現)
聲明:筆者初衷用於分享與普及網絡知識,若讀者因此作出任何危害網絡安全行為後果自負,與合天智匯及原作者無關!