Base64

Base64 是一种基于 64 个可打印字符来表示二进制数据的表示方法。其要求将3个8bit转换为4个6bit,转换后,每6bit选择ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/中对应的字符进行编码。当数据不足3字节时,其余bit用0补足3字节,输出字符使用=,故经过base64编码过的数据,末尾可能会出现一个或者两个=,这也是base64编码过后的数据的一个显著特征。

Base64 编码表

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

Base64的代码实现(python2)

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
79
#-*- coding: utf-8 -*-
import re


class Base64():
def __init__(self):
self.table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

def encode(self, data):
if len(data) == 0:
return

sbin = ''
for i, d in enumerate(data):
# print(i,d)
t = bin(ord(d))[2:]
# print(t)
t = '0' * (8 - len(t)) + t
# print(t)
sbin = sbin + t

npad = 0
if len(data) % 3 != 0:
npad = 3 - len(data) % 3
sbin = sbin + '0' * 8 * npad

# print(npad)
# print(sbin)
lst = re.findall(r'.{1,6}', sbin)
# print(lst)
out = ''
for i in range(0, len(lst) - npad):
table_i = int(lst[i], 2)
out = out + self.table[table_i]
out = out + '=' * npad
return out

def decode(self, data):
s = data.replace('=', '')
# print(s)
sbin = ''
for i, d in enumerate(s):
talbe_i = self.table.find(d)
if -1 == talbe_i:
print('error data :%c', d)
return

t = bin(talbe_i)[2:]
t = '0' * (6 - len(t)) + t
sbin = sbin + t

# print(sbin)
# print(len(sbin)%8)
end = len(sbin) - len(sbin) % 8
sbin = sbin[0:end]
lst = re.findall(r'.{1,8}', sbin)
# print(lst)
out = ''
for i, d in enumerate(lst):
# print(i,d)
ch = chr(int(d, 2))
# print(ch)
out = out + ch
# print(out)
# print(bin(ord(out[len(out)-1]))[2:])
return out


def main():
s = raw_input("Input:\n")
print('加密:')
d = Base64().encode(s)
print(d)
print('解密:')
print(Base64().decode(d))


if __name__ == '__main__':
main()

变种Base64以及选择明文攻击

在很多ctf赛事中,常常会使用变种的Base64编码,即将编码表修改或者打乱后的Base64编码。这时候,我们通过生成一段较长的密文,也难以获取到修改后的完整替换表。但在明文可控的情况下,我们可以尝试构造一段明文,直接获取到这个替换表。
根据base64的原理,编码表与6bit的二进制数一一对应,也就是说我们从0开始,一直到63,按照6bit构造原文。
即:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
00 00 00 00 00 01 00 00 10 00 00 11
00 01 00 00 01 01 00 01 10 00 01 11
00 10 00 00 10 01 00 10 10 00 10 11
00 11 00 00 11 01 00 11 10 00 11 11
01 00 00 01 00 01 01 00 10 01 00 11
01 01 00 01 01 01 01 01 10 01 01 11
01 10 00 01 10 01 01 10 10 01 10 11
01 11 00 01 11 01 01 11 10 01 11 11
10 00 00 10 00 01 10 00 10 10 00 11
10 01 00 10 01 01 10 01 10 10 01 11
10 10 00 10 10 01 10 10 10 10 10 11
10 11 00 10 11 01 10 11 10 10 11 11
11 00 00 11 00 01 11 00 10 11 00 11
11 01 00 11 01 01 11 01 10 11 01 11
11 10 00 11 10 01 11 10 10 11 10 11
11 11 00 11 11 01 11 11 10 11 11 11

按照8bit计算,也就是:

1
\x00\x10\x83\x10\x51\x87\x20\x92\x8B\x30\xD3\x8F\x41\x14\x93\x51\x55\x97\x61\x96\x9B\x71\xD7\x9F\x82\x18\xA3\x92\x59\xA7\xA2\x9A\xAB\xB2\xDB\xAF\xC3\x1C\xB3\xD3\x5D\xB7\xE3\x9E\xBB\xF3\xDF\xBF

将上述数据作为输入,经过Base64编码后,由于base64按6bit进行处理,然后从编码表中选取对应字符进行替换,也就是从0开始到63枚举一遍编码表,因此可以直接得到魔改后的base64编码表。
拿到编码表后,我们就可以直接编码和解码变种的base64了。

参考资料

  1. FreeBuf 爆破非默认Base64编码表
  2. CTF Wiki Base64
  3. 看雪论坛 Base64编码/解码(python)