base64隱寫


由NJCTF2017一道misc題引出的問題


先來看一下base64的概念

Base64要求把每三個8Bit的字節轉換為四個6Bit的字節(38 = 46 = 24),然后把6Bit再添兩位高位0,組成四個8Bit的字節,也就是說,轉換后的字符串理論上將要比原來的長1/3。
規則
關於這個編碼的規則:
1.把3個字符變成4個字符
2.每76個字符加一個換行符
3.最后的結束符也要處理
4.10個數字,26個大寫字母,26個小寫字母,1個+,一個/剛好64個字符


例子
為方便理解,舉下例子
轉換前 11111011, 11101111, 10111110 (二進制)
首先按每6位分開 111110, 111110, 111110, 111110
再到最高位添加兩個0
轉換后 00111110, 00111110, 00111110, 00111110 (二進制)
上面的三個字節是原文,下面的四個字節是轉換后的Base64編碼,其前兩位均為0。
轉換后,我們用一個碼表來得到我們想要的字符串(也就是最終的Base64編碼),這個表是這樣的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
索引 對應字符
0 A
1 B
2 C
3 D
4 E
5 F
6 G
7 H
8 I
9 J
10 K
11 L
12 M
13 N
14 O
15 P
16 Q
17 R
18 S
19 T
20 U
21 V
22 W
23 X
24 Y
25 Z
26 a
27 b
28 c
29 d
30 e
31 f
32 g
33 h
34 i
35 j
36 k
37 l
38 m
39 n
40 o
41 p
42 q
43 r
44 s
45 t
46 u
47 v
48 w
49 x
50 y
51 z
52 0
53 1
54 2
55 3
56 4
57 5
58 6
59 7
60 8
61 9
62 +
63 /

 

雖然python解base64很方便
最好還是先寫個腳本加深對base64的理解
文末我會貼出我的調試腳本

那么base64隱寫到底是什么東西呢?

關鍵是base64解碼的時候
1.檢查base64編碼后面有幾個等於號
2.把字符串按照base64表轉換成46的倍數位數二進制
3.__刪除等於號的個數
8的bit__(等於號不在base64規定的范圍內,只是為了補足長度,所以解碼時要刪除)
4.按照6個bit一組轉成字符

此處的關鍵就是,解碼的時候,會刪除等於號的個數8的bit
且我們只用6個bit表示一個等於號(xxxxxx)
那么,意思就是我們可以控制__等於號個數
2bit__的字符

NJCTF2017有一道misc是base64隱寫
從里面抽出一條來

1
2
3
4
5
import base64
s = "QnkgcmVhc29uIG9mIGhpcyBmYWxsZW4gZGl2aW5pdHm="
print base64.b64decode(s)
print s
print base64.b64encode(base64.b64decode(s))

 

會發現返回

1
2
3
By reason of his fallen divinity
QnkgcmVhc29uIG9mIGhpcyBmYWxsZW4gZGl2aW5pdHm=
QnkgcmVhc29uIG9mIGhpcyBmYWxsZW4gZGl2aW5pdHk=

 

會發現有區別,但是並不影響正常顯示
來分析一下為啥,就拿我的昵稱舉例
首先是把每位字符轉化成ascii的二進制形式

1
2
c | 0 | 1 | 4
01100011 | 00110000 | 00110001 | 00110100

 

進行base64編碼時,按每6位分開,如果不能被6整除要用0補齊

1
2
011000 11|0011 0000|00 110001 | 001101 00
011000 | 110011 | 000000 | 110001 | 001101 | 000000

 

並且所有高位補兩個0至8位

1
2
011000 | 110011 | 000000 | 110001 | 001101 | 000000
00011000 | 00110011 | 00000000 | 00110001 | 00001101 | 00000000

 

這樣才能剛好用64個不同形式表示

1
2
00011000 | 00110011 | 00000000 | 00110001 | 00001101 | 00000000
Y | z | A | x | N | A

 

但是會發現字符只有6位數不夠被4整除
所以到字符后面添加2個等號

1
2
YzAxNA
YzAxNA==

 

解密
先找出YzAxNA==每位字符在base64碼表中的位置
由於等號沒有我就不表示了

1
2
Y | z | A | x | N | A
011000 | 110011 | 000000 | 110001 | 001101 | 000000

 

將二進制連接起來並按每八位划分

1
2
011000110011000000110001001101000000
01100011 | 00110000 | 00110001 | 00110100 | 0000

 

會發現末尾多了等號數*2bit的0
這些0在解密時是要被刪除的
所以base64隱寫的精髓就是這幾bit可控

1
2
3
4
5
YzAxNA==
YzAxNB==
YzAxNC==
......
YzAxNO==

 

解密的到的都是一樣的結果

調試腳本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# -*- coding:utf-8 -*-
 
 
base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
 
def encode(str):
s = str
s_bin = ""
for i in range(0,len(s)):
s_bin = s_bin + bin(ord(s[i])).replace( "0b","").rjust(8,"0") #把每個字符二進制化
print s_bin
 
if len(s_bin) % 6 != 0: #判斷二進制長度能否被6整除
for j in range(1,6):
if (len(s_bin) + j) % 6 == 0:
bin_length = len(s_bin) + j #獲取二進制長度
break
s_bin = s_bin.ljust(bin_length, "0") #把二進制長度補到能被6整除
print s_bin
 
encode = ""
for k in range(0,len(s_bin)/6):
index = k * 6
each_bin = s_bin[index:index+ 6].zfill(8) #把6Bit再添兩位高位0
print each_bin
each_enc = int(each_bin, 2) #轉化為10進制
print each_enc
encode = encode + base[each_enc]
 
# print encode
if len(encode) % 4 != 0: #判斷解密后字符串是否能被4整除,如果不能,要在末尾加=
for l in range(1,4):
if (len(encode) + l) % 4 == 0:
encode_length = len(encode) + l
print encode.ljust(encode_length,"=")
 
def raw(str): #獲取每位字符的二進制形式
s = str
for i in range(0,len(s)):
print s[i],bin(ord(s[i])).replace("0b","").rjust(8,"0")


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM