Base64加密算法学习
NCTF的wp考完高数线代就即刻补全(认真脸)
概述
Base64是一种基于64个可打印字符来表示二进制数据的表示方法。由于\(\log_{2}64=6\),所以每6 bit为一个单元,对应某个可打印字符。3个字节相当于24 bit,对应于4个Base64单元,即3个字节可由4个可打印字符来表示。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9,这样共有62个字符,此外两个可打印符号在不同的系统中而不同(一般是+/)。
实现原理
一份常见的Base64索引表为:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/,每一个可打印字符分别对印着数值0-63。
在进行Base64加密时,每次取出明文3字节的数据,先后放入一个24 bit的缓冲区中,优先占据高位。若数据不足3字节,则剩余的bit用0填充。这之后,每次从缓冲区提取6 bit,按照其二进制对应的数值选择索引表中的可打印字符进行加密。循环该操作直到明文被完全提取。
特别地,当缓冲区中某一个Base64单元的bit全为0时,令该单元编码的字符为'='。这也是为什么经过Base64加密的密文的一个显著特征是末位可能会出现1到2个'='字符。
一次手动模拟
没有用0填充的情况
我们令明文为"msg",进行一次手动模拟:
上色图可以更好地帮助我们理解这个过程
(有个傻逼搞了个html版本的表格搞了半天发现自己的blog显示不全最后发现可以直接excel截图贴在这但还是要不死心地放一个源代码在下面)
<table>
<tr height="24" style="height:14.40pt;">
<td class="xl65" height="24" style="height:14.40pt;" x:str="">明文</td>
<td class="xl65" colspan="8" x:str="" align="center">m</td>
<td class="xl65" colspan="8" x:str="" align="center">s</td>
<td class="xl65" colspan="8" style="border-right:none;border-bottom:none;" x:str="">g</td>
</tr>
<tr height="24" style="height:14.40pt;">
<td class="xl65" height="24" style="height:14.40pt;" x:str="">ASCII</td>
<td class="xl65" colspan="8" x:num="">109</td>
<td class="xl65" colspan="8" x:num="">115</td>
<td class="xl65" colspan="8" style="border-right:none;border-bottom:none;" x:num="">103</td>
</tr>
<tr height="24" style="height:14.40pt;">
<td class="xl65" height="24" style="height:14.40pt;" x:str="">二进制编码</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
</tr>
<tr height="24" style="height:14.40pt;">
<td class="xl65" height="24" style="height:14.40pt;" x:str="">索引</td>
<td class="xl65" colspan="6" x:num="">27</td>
<td class="xl65" colspan="6" x:num="">23</td>
<td class="xl65" colspan="6" x:num="">13</td>
<td class="xl65" colspan="6" style="border-right:none;border-bottom:none;" x:num="">39</td>
</tr>
<tr height="24" style="height:14.40pt;">
<td class="xl65" height="24" style="height:14.40pt;" x:str="">Base64加密</td>
<td class="xl65" colspan="6" x:str="">b</td>
<td class="xl65" colspan="6" x:str="">X</td>
<td class="xl65" colspan="6" x:str="">N</td>
<td class="xl65" colspan="6" style="border-right:none;border-bottom:none;" x:str="">n</td>
</tr>
</table><table>
<tr height="24" style="height:14.40pt;">
<td class="xl65" height="24" style="height:14.40pt;" x:str="">明文</td>
<td class="xl65" colspan="8" x:str="" align="center">m</td>
<td class="xl65" colspan="8" x:str="" align="center">s</td>
<td class="xl65" colspan="8" style="border-right:none;border-bottom:none;" x:str="">g</td>
</tr>
<tr height="24" style="height:14.40pt;">
<td class="xl65" height="24" style="height:14.40pt;" x:str="">ASCII</td>
<td class="xl65" colspan="8" x:num="">109</td>
<td class="xl65" colspan="8" x:num="">115</td>
<td class="xl65" colspan="8" style="border-right:none;border-bottom:none;" x:num="">103</td>
</tr>
<tr height="24" style="height:14.40pt;">
<td class="xl65" height="24" style="height:14.40pt;" x:str="">二进制编码</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">0</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
<td class="xl65" x:num="">1</td>
</tr>
<tr height="24" style="height:14.40pt;">
<td class="xl65" height="24" style="height:14.40pt;" x:str="">索引</td>
<td class="xl65" colspan="6" x:num="">27</td>
<td class="xl65" colspan="6" x:num="">23</td>
<td class="xl65" colspan="6" x:num="">13</td>
<td class="xl65" colspan="6" style="border-right:none;border-bottom:none;" x:num="">39</td>
</tr>
<tr height="24" style="height:14.40pt;">
<td class="xl65" height="24" style="height:14.40pt;" x:str="">Base64加密</td>
<td class="xl65" colspan="6" x:str="">b</td>
<td class="xl65" colspan="6" x:str="">X</td>
<td class="xl65" colspan="6" x:str="">N</td>
<td class="xl65" colspan="6" style="border-right:none;border-bottom:none;" x:str="">n</td>
</tr>
</table>
有用0填充的情况
我们令明文为"sb"(为了纪念写上面那一坨html的sb),进行一次手动模拟:
上色图可以更好地帮助我们理解这个过程
代码实现
Python的base64模块可以很好地帮助我们进行实现
# python 2
import base64
test_str = 'flag{this_is_a_sample_flag}'
enc_str = base64.b64encode(test_str)
print(enc_str.decode())
test_str2 = 'ZmxhZ3t0aGlzX2lzX2Ffc2FtcGxlX2ZsYWd9'
dec_str = base64.b64decode(test_str2)
print(dec_str.decode())
(好像python实现的base64如果出现一些符号得到的密文就会不太一样,平常用的都是在线加解密,不是很懂呜呜呜)
Base64换表
在魔改的Base64算法中,替换索引表是最简单的一种实现方式
这里直接放一个轮子在这
"""
Customized base64 algorithm
You can set you own indexing string using the config() method.
Usage:
b = CusBase64()
b.encode('binary\x00string') # Output: YmluYXJ5AHN0cmluZw==
b.decode('YmluYXJ5AHN0cmluZw==') # Output: binary\x00string
b.config('aABCDEFGHIJKLMNOPQRSTUVWXYZbcdefghijklmnopqrstuvwxyz0123456789+/')
b.decode('c2UsYi1kYWM0cnUjdFlvbiAjb21wbFU0YP==') # Output: self-destruction complete
"""
class CusBase64(object):
DEFAULT = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
def __init__(self):
self.idx_str = CusBase64.DEFAULT
def encode(self, str):
"""
Encode string using the customized indexing string.
- args:
str: String to be encoded
"""
# Get the binary string
binary = ''.join([format(ord(c),'0>8b') for c in str])
# Add additional zero
binary = self.padding(binary)
# Get the index in indexing string
idxs = [int(binary[6*i:6*i+6], 2) for i in range(len(binary)//6)]
result = ''.join([self.idx_str[i] for i in idxs])
# add '='
if len(str)%3 != 0:
result = result + (3-len(str)%3)*'='
print("%r" % result)
def decode(self, str):
"""
Decode string using the customized indexing string.
- args:
str: String to be decoded
"""
if len(str) == 0:
return
# remove '='
while str[-1]=='=':
str = str[:-1]
try:
# Get the binary string
binary = ''.join([format(self.idx_str.index(c), '0>6b') for c in str])
# Remove additional zero
binary = self.remove(binary)
result = ''.join([chr(int(binary[8*i:8*i+8], 2)) for i in range(len(binary)//8)])
except ValueError:
result = "Please check again!"
print("%r" % result)
def remove(self, binary):
"""
Remove additional zero while decoding string.
- args:
binary: Binary format of the index.
- returns:
Binary string without additional zero.
"""
if len(binary)%8 == 0:
return binary
else:
return binary[:-(len(binary)%8)]
def padding(self, binary):
"""
Add additional zero while encoding string.
- args:
binary: Binary format of the string.
- returns:
Binary string with additional zero.
"""
if len(binary)%6 == 0:
return binary
n = 6 - len(binary)%6
binary = binary + n * '0'
return binary
def config(self, str):
"""
Set customized indexing string.
"""
self.idx_str = str
print("New indexing string is %r" % self.idx_str)
b = CusBase64()
b.config('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/')
b.encode('flag{this_is_a_sample_flag}')
b.decode('zMXHz3T0AgLZx2LZx2fFC2fTCgXLx2zSywD9')
其中config内的字符串是自定义的索引表
但是上面这个轮子如果有一些奇奇怪怪的字符就会出锅
那么我们可以用下面的轮子(打NCTF的时候嫖出来的)
import base64
flag = "U>F2UsQXN`5sXMELT=:7M_2<X]^1ThaWF0=KM?9IUhAsTM5:T==_Ns&<Vhb!"
std_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
my_table = '#$%&\x27()*+,-.s0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[h]^_`ab'
my_table2 = 'ba`_^]h[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210s.-,+*)(\x27&%$#'
flag = flag.translate(str.maketrans(my_table2, std_table))
flag = flag.replace("!", '=').encode() # "6G074JP+s)WV:Z+T<&(Q18`Ks)WV:Y4hs9[h:YCS?&0`"
flag = base64.b64decode(flag).decode()
flag = flag.translate(str.maketrans(my_table, std_table))
print(base64.b64decode(flag))