Article Summary
GPT 4

BASE64

ZG==
YY==
aW==
ZF==
cm==
aM==
b2==
dc==
c2==
Zf==

解码得到 daidrhouse,然而编码得到

ZA==
YQ==
aQ==
ZA==
cg==
aA==
bw==
dQ==
cw==
ZQ==

发现每一串的第二位都发生了改变,但是结果没变。

base64原理

顾名思义,base64编码就是用64个ascii字符作为基础来编码二进制内容的一种编码方式。相信各位一定在网页中看到过base64编码的内嵌图片,甚至QQ音乐传输歌词文件时,也采用了base64编码。将二进制编码为ascii字符,使数据在某些场景下更便于阅读、便于传输。当然,将所有二进制「浓缩」到区区64个字符来表示,一定会在体积上作出妥协。字符在编码完成后,会增大1/3倍。

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

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

索引表

base64有一张标准编码表,为64个ascii字符排序并赋予索引。

索引 字符 索引 字符 索引 字符 索引 字符
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在解码的时候,会按照字符串末尾的 = 数量来删除相应字节数。或许你已经发现了,当一组字符的数量为1字节或2字节的时候,会有4位或2位二进制在解码时被忽略

即解码时:

解密的时候首先把"="删去,然后写出二进制数串,然后从左往右每8位一组,剩余的不足8位丢掉,然后根据转换表获得相应字符

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

也就是说红色部分会被忽略,所以即使对其进行改变,解码得到的结果依然不变。

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

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

base64的解密脚本

import re
import base64

b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

# ccc.txt为待解密的base64隐写字符串所在的文件
f = open('base.txt','r')
base64str = f.readline()

# pattern2用于匹配两个等号情况时,等号前的一个字符
# pattern2用于匹配一个等号情况时,等号前的一个字符
pattern2 = r'(\S)==$'
pattern1 = r'(\S)=$'

# 提取后的隐写二进制字符加入binstring中
binstring = ''

# 逐行读取待解密的base64隐写字符串,逐行处理
while(base64str):
    # 先匹配两个等号的情况,如果匹配不上,再配置一个等号的情况
    # 如果无等号,则没有隐藏,无需处理
    if re.compile(pattern2).findall(base64str):
        # mstr为等号前的一个字符,该字符为隐写二进制信息所在的字符
        mstr = re.compile(pattern2).findall(base64str)[0]
        # 确认mstr字符对应的base64二进制数,赋值给mbin
        mbin = bin(b64chars.find(mstr))
        # mbin格式如0b100,mbin[0:2]为0b
        # mbin[2:].zfill(6)为将0b后面的二进制数前面补0,使0b后面的长度为6
        mbin2 = mbin[0:2] + mbin[2:].zfill(6)
        # 两个等号情况隐写了4位二进制数,所以提取mbin2的后4bit
        # 赋值给stegobin,这就是隐藏的二进制信息
        stegobin = mbin2[-4:]
        binstring += stegobin
    elif re.compile(pattern1).findall(base64str):
        mstr = re.compile(pattern1).findall(base64str)[0]
        mbin = bin(b64chars.find(mstr))
        mbin2 = mbin[0:2] + mbin[2:].zfill(6)
        # 一个等号情况隐写了2位二进制数,所以提取mbin2的后2bit
        stegobin = mbin2[-2:]
        binstring += stegobin
    base64str = f.readline()

# stegobin将各行隐藏的二进制字符拼接在一起
# 从第0位开始,8bit、8bit处理,所以range的步进为8
for i in range(0,len(binstring),8):
    # int(xxx,2),将二进制字符串转换为10进制的整数,再用chr()转为字符
    print(chr(int(binstring[i:i+8],2)),end='')

加密脚本:

import base64
import re

b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

with open('3.txt', 'r') as f:
    lines = f.readlines()  # 读取所有行

flag = 'c2tzZWN7QmFzZTY0aXNGNG59'
bin_str = ''.join([bin(ord(c)).replace('0b', '').zfill(8) for c in flag])

add = 0

pattern2 = r'(\S)==$'
pattern1 = r'(\S)=$'

with open('0.txt', 'w') as w:
    for line in lines: 
        line = line.strip()
        if add >= len(bin_str):  # 检查是否已处理完flag
            break
        if re.search(pattern2, line):
            tem = re.search(pattern2, line).group(1)
            tembin = bin(b64chars.find(tem))
            tembin2 = tembin[0:2] + tembin[2:].zfill(6)
            steg = tembin2[-4:]
            tembin1 = tembin2[0:4] + bin_str[add:add+4]
            stag = b64chars[int(tembin1, 2)]
            w.write(line[0:-4] + stag + '==\n')  # 使用 write 而不是 writelines,并添加换行符
            add += 4
        elif re.search(pattern1, line):
            tem = re.search(pattern1, line).group(1)
            tembin = bin(b64chars.find(tem))
            tembin2 = tembin[0:2] + tembin[2:].zfill(6)
            steg = tembin2[-2:]
            tembin1 = tembin2[0:6] + bin_str[add:add+2]
            stag = b64chars[int(tembin1, 2)]
            w.write(line[0:-3] + stag + '=\n')  # 使用 write 而不是 writelines,并添加换行符
            add += 2

NewstarCtf 2023

base!

脚本解码得到

iDMb6ZMnTFMtFuouYZHwPTYAoWjC7Hjca8

即flag{b4se_1s_4_g0od_c0d3}

base家族

http://www.atoolbox.net/Tool.php?Id=934

https://ctf.bugku.com/tools