base64隐写

mac2022-06-30  27

由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")

转载于:https://www.cnblogs.com/Elope/p/6725824.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)