前陣子看到靠北工程師版在討論某個資外洩的服務,提到了大家經常搞錯的三件事情:encode、encrypt、hash,今天就簡單聊聊這三個的差別,BTW,這也是我之前在面試來投 security 時經常問的問題,如果答不出來的面試者... 基本上就抱歉了 XD"
中文翻譯為 編碼,其實跟 security 沒什麼關係,主要是因為需要傳輸資料而衍伸的一種技術,像是將文字編碼 (big5, utf-8)、音訊編碼 (mp3)、影像編碼 (mp4, 3gp)
如果我們把 encode 以數學函數的方式表達 raw = f(data)
,一定存在另一個反函數 data = f'(raw)
可以將資料 decode 回原本的內容
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
所組成的字串,這樣我們就可以用 printable 的資料流來處理 non-printable 的資料
0x20~0x7f
之間,這範圍之外的稱為 non-printable中文翻譯為 加密,是真正因為需要保護資料不讓別人知道而發展的一種技術,如果有修過密碼學的課程,一般都會從 凱薩密碼 開始講 到 二次世界大戰 XD
以數學函式表達加密會是 c = f(p, k)
,同樣也會存在反函數 p = f'(c, k)
,知道 key 和哪一種加密函式,就能把 cipher 轉回原本的 plaintext,也就是俗稱的 解密 (decrypt)
中文翻譯為 雜湊,跟前面兩者最大的不同是,hash 沒有辦法還原回原本的 data,因此不存在反函數可以直接將 hash 算回原本的 data
希望以上的說明可以幫大家快速了解三者的區別,如果之後聽到有人說 base64 加密
、md5 加密
這種說法,請不要客氣的糾正過來 XD
最後提一下 rot13
,有些人會說他是加密的一種,不能說完全錯誤,rot13
從原理上來說是凱薩加密,但 key 固定取 13 得到的結果,但由於 key 固定就表示不是可變動的參數,因此從數學的角度來看, rot13
就很明顯屬於編碼,而不是加密了
先提一下 PPC 這個題目分類,全名是 Professionally Program Coder,主要會考大家 coding 的能力,題目會類似 ACM 的形式,在早些年的 CTF 中經常出現這個分類,但近年來已經很少看到,可喜可賀 XD 話說回來這題其實也沒有太多 PPC 的成分就是了
這題是用 python 寫的 server,連上 server 後會先得到一個 answer_hash,然後問兩個問題,如果兩題都答對 server 就會印出 flag
第一個問題要回答 server 是透過哪些 function 將 answer 計算成 answer_hash,需要輸入四個數字,server 會根據四個數字取四個 function 去計算 answer_hash,部分 source 如下
f['fun1']=reverse
f['fun2']=base64.b64decode
f['fun3']=zlib.decompress
f['fun4']=dec2hex
f['fun5']=binascii.unhexlify
f['fun6']=gb2312
f['fun7']=bin2dec
f['fun8']=hex2bin
f['fun9']=hex2dec
answer = 78864179732635837913920409948348078659913609452869425042153399132863903834522365250250429645163517228356622776978637910679538418927909881502654275707069810737850807610916192563069593664094605159740448670132065615956224727012954218390602806577537456281222826375
answer_hash = f['fun6'](f['fun2'](f[f1](f[f2](f[f3](f[f4](answer))))))
dec2hex
,剩下的組合全部試一遍看哪個正確,可以得到正確答案是 3, 5, 1, 4answer_hash = base64(gb2312(zlib.decompress(binascii.unhexlify(reverse(dec2hex(answer))))))
第二個問題是要輸入一組 passcode,passcode 不能與 answer 相同,但 passcode 經過與問題一相同的 function 處理後要得到和 answer_hash 一樣的結果
base64(gb2312(zlib.decompress(binascii.unhexlify(reverse(dec2hex(passcode)))))) == answer_hash && passcode != answer
由於所使用到的的 function 都是 encode,input 和 output 是 1-to-1 對應,沒有製造 collision 的可能性,只有 zlib.decompress 是 壓縮 所以有機會做手腳
zlib.decompress 的演算法有容錯空間,會自動捨去無法被解壓縮的資料,所以我們可以隨便在 zlib.decompress 要處理的資料後面隨便塞個資料,就可以得到與 answer 不同的 passcode
tmp = binascii.hexlify(((ff2))) + '01'
assert f['fun6'](f'fun2' == answer_hash, 'failed'
passcode = hex2dec(reverse(tmp))
最後得到結果:
Your passcode: 2046914671302815174999479572879926709311623516344310480161044657024943192185778242098416328741805906729519967582134066703522565680955045139113850683617281109011304982006585757796402695817434740126949224951202731646556997125542758488503105749434074546856689060231
Welcome back! The door always open for you, your majesty!BCTF{py7h0n-l1b-func7i0ns-re4lly-str4nge}