0x00: 前言
Base64編碼的作用:
- 將一些特殊的字元轉換成常見的字元。特殊的字元可能是不可見字元或者是大於ascii碼127的,將其變成常見的字元(在base64中為a~z A~Z 0~9 + /)。
- Base64特別適合在某些網路協議下快速傳輸。
在學習Base64隱寫之前,得先熟悉Base64編碼與解碼的過程。
0x01: Base64的編碼過程
Base64編碼後的字元為”a~z A~Z 0~9 + /“共計64個,每個需要6個位元位進行儲存。原本,ASCII編碼字元每個字元佔8個位元位。Base64編碼則是把原來每單位8個位元位的字元序列劃分成每單位6個位元位,然後按單位轉換成上述中的64個字元。
Base64編碼表
舉個例子~:將字串"tolele"進行Base64編碼。
- 根據ASCII編碼進行轉換:tolele <==> 01110100 01101111 01101100 01100101 01101100 01100101
- 重新按6bit進行劃分:011101 000110 111101 101100 011001 010110 110001 100101
- 根據Base64編碼表進行轉碼:dG9sZWxl
檢驗一下是沒問題的:
通過這種方式編碼,當字元數為3的倍數時才會剛好可以轉換成若干個Base64編碼字元。那當字元數不為3的倍數時,該怎麼辦呢?解決方法就是往後面以8bit為單位填充0。
這時,有兩種情況:
- 字元數為3n+1:此情況最後會多出2個位元位,我們可以填充2個單位的0(即16個位元位的0),這時會有多餘的18個位元位。前6個位元位按表格進行轉碼,其餘的每6個bit位轉換成'='。
- 字元數為3n+2:此情況會多出4個位元位,填充1個單位的0,這樣就多餘12個位元位,為6的整數倍。後續和1中類似。
影像總是比話語更能說明內容:
檢驗一下:
0x02: Base64的解碼過程:
很顯然,解碼過程就是編碼的逆過程。
拿上面"tole"的Base64編碼"dG9sZQ=="進行舉例:
- 先把填充的'='去掉:dG9sZQ
- 根據Base64編碼表進行轉碼:dG9sZQ <==> 011101 000110 111101 101100 011001 010000
- 從前往後,以每8個位元位為單位進行ASCII轉換成字元。最後面會有4個'0'多餘,直接去掉就行。
0x03: Base64隱寫原理:
可以留意一下解碼過程中的第三步,會將多餘的位元位去掉(因為湊不到8位)。那麼,這說明了:這多餘的位元位即使我們隨意的改變值也不會影響解碼後的結果,因為它會被丟棄掉。
測試一下:還是上面的例子,最後是Q,為010000。後面的4個0在解碼時會被丟棄掉的,那我們使其變成010101,變成了V。解碼後的結果會改變嗎?
可見,這個改變並不會對解碼結果造成影響。
這樣,為了隱寫某些資料,我們就可以將資料寫入這裡。但每個Base64編碼最多多餘4個位元位,為了隱藏較大的資料,我們常常需要多個位元位。提取時,我們可以將每個多餘的位元位擷取出來,按一定的順序組合,從而得到我們的隱藏資料。
0x04: 例題實踐
Buuctf的base64隱寫:
https://buuoj.cn/challenges#[ACTF%E6%96%B0%E7%94%9F%E8%B5%9B2020]base64%E9%9A%90%E5%86%99
開啟關鍵的txt檔案一看,大量的base64編碼,base64隱寫跑不了了:
這裡直接用大佬的指令碼了,python2執行是沒問題的,至於python3的話……
# -*- coding: cp936 -*-
b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
with open('1.txt', 'rb') as f:
bin_str = ''
for line in f.readlines():
stegb64 = ''.join(line.split())
rowb64 = ''.join(stegb64.decode('base64').encode('base64').split())
offset = abs(b64chars.index(stegb64.replace('=','')[-1])-b64chars.index(rowb64.replace('=','')[-1]))
equalnum = stegb64.count('=') #no equalnum no offset
if equalnum:
bin_str += bin(offset)[2:].zfill(equalnum * 2)
print ''.join([chr(int(bin_str[i:i + 8], 2)) for i in xrange(0, len(bin_str), 8)]) #8 位一組
0x05: 感慨
“您這flag挺能藏的呀~”
tolele
2022-05-14