关于Base64隐写那点事儿

2022-11-11 20:22:17 浏览数 (1)

咕咕咕的我又回来了,学校这几天在放“五一七天小长假”,加上今天的ISCC有点奇奇怪怪,所以来更一篇博客吧!今天我们来讲关于base64隐写那点事儿……

前言

在了解base64隐写之前,我们先来了解一下base64编码。

base64编码是现在网络上最常见的用于传输8Bit字节码的编码方式之一。在最初有的网络并不支持所有的字节,比如某些系统只支持可见字符的传送,比如图片二进制流的每个字节不可能全部是可见字符,所以就传送不了。所以由于不能传送ASCII的控制字符,它的用途就受到了很大的限制。

而Base64旨在通过一种方法,把所有的字符都用64个特定的可打印字符表示来实现传送。选定的64个字符如下:

索引

对应字符

索引

对应字符

索引

对应字符

索引

对应字符

0

A

17

R

34

i

51

z

1

B

18

S

35

j

52

0

2

C

19

T

36

k

53

1

3

D

20

U

37

l

54

2

4

E

21

V

38

m

55

3

5

F

22

W

39

n

56

4

6

G

23

X

40

o

57

5

7

H

24

Y

41

p

58

6

8

I

25

Z

42

q

59

7

9

J

26

a

43

r

60

8

10

K

27

b

44

s

61

9

11

L

28

c

45

t

62

12

M

29

d

46

u

63

/

13

N

30

e

47

v

14

O

31

f

48

w

15

P

32

g

49

x

16

Q

33

h

50

y

加密过程

由于只用到了64个字符,所以使用6个二进制位(2^6 = 64)完全可以把所有的字符表示出来,于是原来的1个字节8位在base64编码中变成了1个字节6位。

换言之:把原本的3个字节变成现在的4个字节,因为(3*8 == 4*6)

所以在加密的时候首先写出原字符ASCII码对应的二进制数字,每个字符都可以得到一个8位的01串,再把该01串重新按照每6位一组划分即可得到一个新的数字,对照上图给出的表格可以得到一个新的字符。

但是这里有一个问题了:如果明文的字节数刚好是3的倍数那没有问题,按照6位一组划分肯定是刚刚好的;但是如果明文的字节数不是3的倍数,那按照6位一组划分不是就有剩余了吗?具体可见下图:

这样是没有剩余的:

而这样是有剩余的:

而base64给出的解决方法是在二进制数串后面加0,一直到二进制数串变成8和6的公倍数,然后把只有0的字节编码成"=",如下图:

所以LoV3被编码成了TG9WMw== ,同时base64编码后面最多只可能出现2个==

解密过程

解密的过程可视作加密的逆过程

由于"="是最后为了补齐填充的,所以解密的时候首先把"="删去,然后写出二进制数串,然后从左往右每8位一组,剩余的不足8位丢掉,然后根据转换表获得相应字符:

以上图为例,TG9WMw==首先变成了TG9WMw,对照上图的表写出来二进制数串:

然后每8位一组,剩余不足的丢弃:

所以这里牵涉到了一个地方,由上面的过程我们可以看成,TG9WMw在解密回LoV3的时候,按每8位一组剩余的丢弃来算,最后的110000中的0000是没有用到的。所以换句话说,这剩下的4位无论是0000还是1111,都是要被丢弃的,所以这就提供了一个可以隐藏信息的地方:

TG9WMx==解密后依然是LoV3,但已经隐藏进了一个1进去,那么这就是base64隐写

那么,在平时我们如何判断有没有信息被隐藏进去了呢

最简单的就是你是否隐藏信息,解密得到的明文是不变的,那么你重新按照正确的加密流程计算一遍,如果发现结果不一样,那么就说明隐藏进了信息。

一般CTF题目中出现一大堆base64编码字符串的时候,更需要考虑base64隐写

这里附上一个base64提取隐藏信息的脚本:

代码语言:javascript复制
b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 /'
with open('flag_encode.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('=')
        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)])

感谢中南大学极光网安实验室,部分图片源自其公众号:bowtie:

0 人点赞