ECB加密
ecb模式使用相同的key分塊對(duì)明文分別進(jìn)行加密,相同的明文獲得相同的密文輸出。如下所示:

ECB模式對(duì)明文進(jìn)行分塊,然后用key進(jìn)行加密,生成密文,然后將所有的密文組合在一起,完成加密。
加密條件
1、文明分塊長(zhǎng)度一般為16,或16的倍數(shù)
2、文明長(zhǎng)度不滿16,或16的倍數(shù),使用填充(具體看算法。默認(rèn)是\x00)
加密過程
Plaintext: 待加密的數(shù)據(jù)。
Key : 被一些如AES的對(duì)稱加密算法使用。
Ciphertext:加密后的數(shù)據(jù)。
首先將明文進(jìn)行分塊成Plaintext = Plaintext0+Plaintext1+...Plaintextn.
Ciphertext0 = Encrypt(Plaintext0)
Ciphertext1 = Encrypt(Plaintext1)
.....
Ciphertextn = Encrypt(Plaintextn)
Ciphertext = Ciphertext0+Ciphertext1+....+Ciphertextn
解密過程
Plaintext0= Decrypt(Ciphertext0)
Plaintext1= Decrypt(Ciphertext1)
.....
Plaintextn= Decrypt(Ciphertextn)
Plaintext = Plaintext0 + Plaintext1 +.... +Plaintextn
ECB攻擊
服務(wù)器代碼
server.py
from twisted.internet import reactor, protocol
from Crypto.Cipher import AES
PORT = 8001
KEY = "@Ecb_aTTack>esew"
SECRET = "flag{ese@qq!hooray}"
def encrypt_block(key, plaintext):
encobj = AES.new(key, AES.MODE_ECB)
return encobj.encrypt(plaintext).encode('hex')
def encrypt(key, ptxt):
if (len(ptxt) % 16 != 0):
ptxt = ptxt + "*" * (16 - (len(ptxt) % 16))
return encrypt_block(key, ptxt)
class Server(protocol.Protocol):
def connectionMade(self):
msg = '''
Welcome to the AES_ECB Cryptography System
To encrypt, enter "encrypt [plaintext]". Ex, encrypt 123
1. cliphertext = plaintext + SECRET
2. this padding is *
\r\n>'''
self.transport.write(msg)
def dataReceived(self, data):
try:
data = data.strip().split(" ")
print (data)
if data[0] == "encrypt":
response = encrypt(KEY,data[1] + SECRET)
else:
raise
self.transport.write(">" + response + "\r\n>")
except:
self.transport.write(">Something is wrong here\r\n>")
def main():
factory = protocol.ServerFactory()
factory.protocol = Server
reactor.listenTCP(PORT, factory)
reactor.run()
if __name__ == '__main__':
main()
運(yùn)行
nohub python server.py &
分析
nc連接到服務(wù)器
nc xxxx 8001
發(fā)現(xiàn)
1、加密模式AES ECB
2、cliphertext = plaintext + SECRET
3、this padding is * (使用填充方式為*)
利用兩點(diǎn)
1、填充加密
2、相同的明文獲得相同的密文輸出
這里輸入32字節(jié)的a[兩個(gè)16字節(jié)]
encrypt aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
結(jié)果如下
4f41ee8a60e859118bd54bcfe6a23fc04f41ee8a60e859118bd54bcfe6a23fc04a4c43aa0d91f3a4906047558d0d559024514659c34bbb29dd3567c801bb63ba
發(fā)現(xiàn)1-32與33-64位完全一樣,如下:
4f41ee8a60e859118bd54bcfe6a23fc0
4f41ee8a60e859118bd54bcfe6a23fc0
相同的文明得到相同的密文。
同時(shí)發(fā)現(xiàn)后面還有64位,說明SECRET的長(zhǎng)度在17-32之間。
枚舉SECRET長(zhǎng)度
這里進(jìn)行枚舉
輸入1個(gè)字符進(jìn)行加密,查看加密后的長(zhǎng)度并記住len
輸入2個(gè)字符進(jìn)行加密,查看加密后的長(zhǎng)度是否大于len,沒有繼續(xù)
輸入3個(gè)字符進(jìn)行加密,查看加密后的長(zhǎng)度是否大于len,沒有繼續(xù)
....
輸入14個(gè)字符進(jìn)行加密,查看加密后的長(zhǎng)度是否大于len,發(fā)現(xiàn)長(zhǎng)度變了。那么padding應(yīng)該填充13個(gè)字符
這里也知道flag長(zhǎng)度為19位。
枚舉SECRET字符
這里有個(gè)公式
當(dāng)填充為13字節(jié)時(shí),加密明文字符串為13+19=32位
當(dāng)填充為14字節(jié)時(shí),加密明文字符串為14+19=32+1位,發(fā)現(xiàn)最后一位是flag的最后一位加密[后面15位是填充]。如果我們輸入的16位中,第一個(gè)是字符,其余15個(gè)填充,當(dāng)這個(gè)字符與flag最后一個(gè)字符相同時(shí),他們的密文也相同,可以爆破出flag中的最后一位[這里我們需要多輸入16個(gè)字節(jié)構(gòu)造爆破]。當(dāng)填充為16+14字節(jié)時(shí),加密明文字符串為16+14+19=16+32+1位,可以爆破出flag中的最后一位。
當(dāng)填充為16+15字節(jié)時(shí),加密明文字符串為16+15+19=16+32+2位,最后兩位是flag的最后兩位加密[后面14位是填充],因?yàn)樯厦嬷雷詈笠晃涣薣這里的"}"],構(gòu)造第一個(gè)是字符+"}",其余14個(gè)填充。可以爆破出倒數(shù)第二位。
這樣可以一直枚舉出所有flag字符
exp
import socket
import re
host = '172.10.22.70'
port = 8001
def Tostr(st):
return st.encode(encoding='UTF8')
def connect():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host,port))
return s
def getCliphertext(data):
p1 = r"(>)(.*?)(\r\n)"
pattern1 = re.compile(p1)
data = pattern1.findall(data)[0][1]
return data
def get_pad_len(s):
s.recv(1024)
for i in range(1,16):
payload1 = "encrypt "+'a'*i
s.send(Tostr(payload1))
data = (s.recv(1024)).decode('utf-8')
data = getCliphertext(data)
if i==1:
slen = len(data)
if len(data)>slen:
break
return i-1
def forcerFlag(s,slen):
padd = 'a'*(slen+1)
plaintext = ""
print("start...")
array = "`1234567890-=+qwertyuiop[]asdfghjkl;'zxcvbnm,./?<>!@#$%^&*()QWERTYUIOP{}ASDFGHJKLZXCVBNM:"
for i in range(19):
for ch in array:
payload2 = "encrypt "+ch+plaintext+'*'*15+padd
s.send(Tostr(payload2))
data = (s.recv(1024)).decode('utf-8')
data = getCliphertext(data)
bp = data[:32]
sec = data[96:128]
if bp==sec:
plaintext = ch +plaintext
print(plaintext)
break
return plaintext
def exp():
s = connect()
slen = get_pad_len(s)
plaintext = forcerFlag(s,slen)
print(plaintext)
if __name__ == '__main__':
exp()