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))
posted @ 2022-01-03 19:31  iPlayForSG  阅读(492)  评论(0编辑  收藏  举报