iT邦幫忙

2023 iThome 鐵人賽

DAY 27
0
Security

資安小白的密碼學從0到1-CryptoHack平台解題紀錄系列 第 28

【Day 27】Symmetric CryptoGraphy09 - Bean Counter

  • 分享至 

  • xImage
  •  

前言

今天會結束Symmetric CryptoGraphy系列!
解最後一題,內容包括CTR過程
https://ithelp.ithome.com.tw/upload/images/20231008/20162613eBF57jY1pa.png

Writeup

題目

網址 : https://cryptohack.org/courses/symmetric/bean_counter/
https://ithelp.ithome.com.tw/upload/images/20231007/20162613JuzPIH89ej.png

思路

根據題目,這題會跟CTR有關
先簡單看甚麼是CTR

  • CTR encrypt
    https://ithelp.ithome.com.tw/upload/images/20231008/20162613Ht4PrrKOfb.png

我們把最上面(Nonce那邊)稱為keystream

流程大概是

keystream進block cipher encryption
之後跟第一段plaintext xor
得到第一段ciphertext
接下來把keystream + 1

之後就重複流程了

接下來點入題目網址,之後看source code在幹嘛

  • source code

    • 先看StepUpCounter
    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

    • 接下來看increment(self)

    發現他會先看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,就補足

    • 最後看__repr__

    但因為code沒用到,所以就不多解釋

  • 總結

    • 呼叫StepUpCounter(),會先設置初始值(__init__裡面內的內容),且產生一個隨機一個長度為32的十六進位的字串
    • 呼叫increment(),回傳剛剛隨機產生的十六進位的字串(不管呼叫幾次永遠不變,因為self.stup必為Flase(0))
  • 看encrypt

    • code
    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

  • 總結

    • keystream經過ECB後跟bean_flag.png裡面的值做xor後輸出
      補充一下ECB圖
      https://ithelp.ithome.com.tw/upload/images/20231008/20162613tFKx8bXIWW.png
    • 那跟CTR有甚麼關係呢?
      https://ithelp.ithome.com.tw/upload/images/20231008/20162613vTBBmncgay.png
      圈起來的部分就是可以當作ECB

解法

根據上面得知,因為他把step_up=False所以keystream其實是固定的
且加上我們可以知道png的前16bytes,透過這個,可以順利求出keystream!

  • 目標 : 求出keystream

甚麼叫我們可以知道png的前16bytes呢?

詳細可以看這個解釋 : https://blog.csdn.net/weixin_45942479/article/details/117036148
跟這張圖(來源 : https://blog.csdn.net/qq_61778128/article/details/122577861)
https://ithelp.ithome.com.tw/upload/images/20231008/20162613uScAXY8iMe.png

png前面16bytes一定是固定的(第一句)
長這樣
https://ithelp.ithome.com.tw/upload/images/20231008/20162613qHKTfUJQ7Q.png
所以可以知道前16bytes = "89504e470d0a1a0a0000000d49484452"
之後我們也把ciphertext取前16

ciphertext為網站上給的{ciphertext : ""}值

最後兩個xor就可以求出keystream!

求出後再跟ciphertext做xor,就可以求得原本bean_flag.png的值

最後我的話,是在網路上隨便載一個png檔案,之後再把剛剛求出來的直接複寫進去,那個png檔案就會變成我們的flag檔案,即可獲得flag!

  • code
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))
  • 結果
    • 原本
      https://ithelp.ithome.com.tw/upload/images/20231008/20162613HCdyI1QeaF.png
    • 執行後
      https://ithelp.ithome.com.tw/upload/images/20231008/20162613XLghuTQZkK.png
    • 打開後得flag
      https://ithelp.ithome.com.tw/upload/images/20231008/20162613eTZtnyZnjC.png

    flag : crypto{hex_bytes_beans}

小結

成功結束對稱密碼啦!!!!!!╰(°▽°)╯╰(°▽°)╯╰(°▽°)╯
https://ithelp.ithome.com.tw/upload/images/20231008/201626137lQ85eYIqK.png
之後先來統整我們學過的東東
再看看甚麼時候會進入非對稱密碼 XD


上一篇
【Day 26】Symmetric CryptoGraphy08 -Symmetry
下一篇
【Day 28】對稱式密碼 - AES + 5大Mode 筆記
系列文
資安小白的密碼學從0到1-CryptoHack平台解題紀錄31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言