今天會結束Symmetric CryptoGraphy系列!
解最後一題,內容包括CTR過程
網址 : https://cryptohack.org/courses/symmetric/bean_counter/
根據題目,這題會跟CTR有關
先簡單看甚麼是CTR
我們把最上面(Nonce那邊)稱為keystream
流程大概是
keystream進block cipher encryption
之後跟第一段plaintext xor
得到第一段ciphertext
接下來把keystream + 1
之後就重複流程了
接下來點入題目網址,之後看source code在幹嘛
source code
class StepUpCounter(object):
def __init__(self, step_up=False):
self.value = os.urandom(16).hex()
self.step = 1
self.stup = step_up
def increment(self):
if self.stup:
self.newIV = hex(int(self.value, 16) + self.step)
else:
self.newIV = hex(int(self.value, 16) - self.stup)
self.value = self.newIV[2:len(self.newIV)]
return bytes.fromhex(self.value.zfill(32))
def __repr__(self):
self.increment()
return self.value
先看__init__這邊
他把
self.value 設為隨機一個16bytes字串,之後轉為十六進位的字串
之後把
self.step = 1
然後
self.stup = step_up
因為一開始step_up=False
所以self.stup = 0
發現他會先看self.stup是否為True
因為我們的self.stup為0 -> False
所以進入
else:
self.newIV = hex(int(self.value, 16) - self.stup)
self.value = self.newIV[2:len(self.newIV)]
return bytes.fromhex(self.value.zfill(32))
self.newIV = hex(int(self.value, 16) - self.stup
因為self = 0
所以可以直接看成
self.newIV = self.value
self.value = self.newIV[2:len(self.newIV)]
因為self.newIV為16進制字串,所以開頭會有一個"0x"
而[2:len(self.newIV)]的目的就是為了忽略那個0x
return bytes.fromhex(self.value.zfill(32))
最後回傳bytes.fromhex(self.value),如果self.value長度不為32,就補足
但因為code沒用到,所以就不多解釋
總結
看encrypt
cipher = AES.new(KEY, AES.MODE_ECB)
ctr = StepUpCounter()
out = []
with open("challenge_files/bean_flag.png", 'rb') as f:
block = f.read(16)
while block:
keystream = cipher.encrypt(ctr.increment())
xored = [a^b for a, b in zip(block, keystream)]
out.append(bytes(xored).hex())
block = f.read(16)
先創建一個AES 模式為ECB的cipher
之後會以二進制打開bean_flag.png
block = 前面16個bytes
進入迴圈,直到block沒東西
之後設keystream = 使用cipher進行加密的ctr.increment()回傳的字串
接下來簡單講一下zip的用法
如果block = [1, 2, 3]
keystream = [6, 7, 8]
zip(block, keystream) -> [(1,6), (2,7), (3,8)]
所以xored = a跟對應的b xor
最後把結果轉為十六進制字串添加到out
block = 下一組16bytes
總結
根據上面得知,因為他把step_up=False所以keystream其實是固定的
且加上我們可以知道png的前16bytes,透過這個,可以順利求出keystream!
甚麼叫我們可以知道png的前16bytes呢?
詳細可以看這個解釋 : https://blog.csdn.net/weixin_45942479/article/details/117036148
跟這張圖(來源 : https://blog.csdn.net/qq_61778128/article/details/122577861)
png前面16bytes一定是固定的(第一句)
長這樣
所以可以知道前16bytes = "89504e470d0a1a0a0000000d49484452"
之後我們也把ciphertext取前16
ciphertext為網站上給的{ciphertext : ""}值
最後兩個xor就可以求出keystream!
求出後再跟ciphertext做xor,就可以求得原本bean_flag.png的值
最後我的話,是在網路上隨便載一個png檔案,之後再把剛剛求出來的直接複寫進去,那個png檔案就會變成我們的flag檔案,即可獲得flag!
from pwn import *
#ciphertext為網站上給的{ciphertext : ""}值
ciphertext = "省略"
png_header = bytes.fromhex('89504e470d0a1a0a0000000d49484452')
ciphertext = bytes.fromhex(ciphertext)
key = xor(png_header, ciphertext[:16])
flag = xor(key, ciphertext)
with open('cat.png', 'wb') as fd:
fd.write(bytes(flag))
flag : crypto{hex_bytes_beans}
成功結束對稱密碼啦!!!!!!╰(°▽°)╯╰(°▽°)╯╰(°▽°)╯
之後先來統整我們學過的東東
再看看甚麼時候會進入非對稱密碼 XD