美文网首页
TSCTF202-re

TSCTF202-re

作者: SamiraG | 来源:发表于2020-10-26 15:24 被阅读0次

    碎碎念

    第一次出题,前两个题是我出的,后两个是师父出的,师父好厉害。感觉最后一道题是我最不擅长的题,大概算算法题?想加强一下了==

    easyre

    有两层加密,第一层是AES,第二层是RC5
    前面是padding,填充方式是PKCS#7

    AES

    sub_FBE这个函数中有明显的AES加密特征:

    image
    有十轮加密,四个步骤,第十轮少了中间的步骤。对应的是AES的:字节代换、行移位、列混淆和轮密钥加,第十轮没有列混淆。
    sub_DA2是密钥扩展步骤。代码中AES的Sbox被修改了,在文件中给出了Sbox和InvSbox。
    密钥扩展部分也进行了一点修改,所以生成的子密钥会有不同。
    AES代码:
    for (i = 1; i <= 10; i++)
    {
        for (j = 0; j < 4; j++)
        {
            unsigned char t[4];
            for (r = 0; r < 4; r++)
            {
                t[r] = j ? w[i][r][j - 1] : w[i - 1][r][3];
            }
            if (j == 0)
            {
                unsigned char temp = t[0];
                for (r = 0; r < 3; r++)
                {
                    t[r] = Sbox[t[(r + 1) % 4]];
                }
                t[3] = Sbox[temp];
                t[0] ^= rc[i - 1];
            }
            for (r = 0; r < 4; r++)
            {
                w[i][r][j] = w[i - 1][r][j] ^ t[r];
            }
        }
    }
    

    修改后的:

    if ( !l )
    {
        for ( n = 0; n <= 3; ++n )
            v12[n] = *(_BYTE *)(a1 + (unsigned __int8)v12[(n + 1) % 4] + 8);
        v12[0] ^= *(&v13 + k - 1);
    }
    

    等于3的这个部分有一点更改,但是可以不用管这个更改的细节,直接dump出文件中扩展出来的密钥解密即可。

    RC5

    根据0xB7E15163, 0x9E3779B9这两个长度可以判断猜测是RC5,密钥长度是16,轮数是12,块大小是32.同样可以找个RC5的代码进行加密验证一下

    solve

    class AES:
    
        MIX_C  = [[0x2, 0x3, 0x1, 0x1], [0x1, 0x2, 0x3, 0x1], [0x1, 0x1, 0x2, 0x3], [0x3, 0x1, 0x1, 0x2]]
        I_MIXC = [[0xe, 0xb, 0xd, 0x9], [0x9, 0xe, 0xb, 0xd], [0xd, 0x9, 0xe, 0xb], [0xb, 0xd, 0x9, 0xe]]
        RCon   = [0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000]
    
        S_BOX = [[54, 79, 98, 216, 181, 132, 205, 246, 220, 42, 230, 237, 171, 82, 1, 175], [208, 10, 104, 20, 39, 161, 219, 135, 156, 231, 41, 102, 53, 233, 180, 145], [139, 206, 243, 52, 86, 94, 35, 97, 112, 195, 167, 50, 45, 128, 12, 196], [245, 44, 114, 164, 201, 6, 185, 110, 25, 18, 7, 34, 214, 211, 0, 113], [152, 14, 107, 202, 100, 61, 186, 254, 136, 2, 227, 70, 239, 31, 47, 143], [46, 58, 49, 155, 240, 226, 123, 153, 187, 168, 72, 99, 212, 141, 244, 105], [191, 232, 75, 222, 218, 118, 176, 16, 184, 151, 198, 159, 22, 247, 172, 221], [69, 188, 197, 225, 173, 124, 91, 252, 204, 174, 89, 154, 111, 229, 103, 163], [88, 199, 140, 101, 129, 73, 83, 131, 64, 57, 93, 55, 177, 116, 142, 77], [207, 51, 248, 162, 117, 115, 250, 215, 74, 203, 130, 120, 23, 224, 149, 125], [8, 241, 189, 65, 183, 4, 15, 138, 209, 81, 62, 56, 147, 194, 137, 210], [150, 148, 26, 251, 95, 126, 96, 36, 84, 234, 127, 11, 27, 67, 38, 43], [28, 179, 78, 133, 63, 90, 192, 30, 80, 119, 255, 9, 228, 158, 37, 157], [5, 19, 238, 213, 48, 170, 40, 121, 134, 76, 249, 87, 109, 235, 71, 178], [217, 24, 182, 190, 122, 193, 160, 13, 92, 200, 165, 68, 106, 60, 32, 166], [3, 17, 59, 21, 144, 253, 146, 236, 169, 33, 85, 29, 223, 108, 66, 242]]
    
        I_SBOX = [[62, 14, 73, 240, 165, 208, 53, 58, 160, 203, 17, 187, 46, 231, 65, 166], [103, 241, 57, 209, 19, 243, 108, 156, 225, 56, 178, 188, 192, 251, 199, 77], [238, 249, 59, 38, 183, 206, 190, 20, 214, 26, 9, 191, 49, 44, 80, 78], [212, 82, 43, 145, 35, 28, 0, 139, 171, 137, 81, 242, 237, 69, 170, 196], [136, 163, 254, 189, 235, 112, 75, 222, 90, 133, 152, 98, 217, 143, 194, 1], [200, 169, 13, 134, 184, 250, 36, 219, 128, 122, 197, 118, 232, 138, 37, 180], [182, 39, 2, 91, 68, 131, 27, 126, 18, 95, 236, 66, 253, 220, 55, 124], [40, 63, 50, 149, 141, 148, 101, 201, 155, 215, 228, 86, 117, 159, 181, 186], [45, 132, 154, 135, 5, 195, 216, 23, 72, 174, 167, 32, 130, 93, 142, 79], [244, 31, 246, 172, 177, 158, 176, 105, 64, 87, 123, 83, 24, 207, 205, 107], [230, 21, 147, 127, 51, 234, 239, 42, 89, 248, 213, 12, 110, 116, 121, 15], [102, 140, 223, 193, 30, 4, 226, 164, 104, 54, 70, 88, 113, 162, 227, 96], [198, 229, 173, 41, 47, 114, 106, 129, 233, 52, 67, 153, 120, 6, 33, 144], [16, 168, 175, 61, 92, 211, 60, 151, 3, 224, 100, 22, 8, 111, 99, 252], [157, 115, 85, 74, 204, 125, 10, 25, 97, 29, 185, 221, 247, 11, 210, 76], [84, 161, 255, 34, 94, 48, 7, 109, 146, 218, 150, 179, 119, 245, 71, 202]]
    
        def SubBytes(self, State):
            # 字节替换
            return [self.S_BOX[i][j] for i, j in 
                   [(_ >> 4, _ & 0xF) for _ in State]]
    
        def SubBytes_Inv(self, State):
            # 字节逆替换
            return [self.I_SBOX[i][j] for i, j in
                   [(_ >> 4, _ & 0xF) for _ in State]]
    
        def ShiftRows(self, S):
            # 行移位
            return [S[ 0], S[ 5], S[10], S[15], 
                    S[ 4], S[ 9], S[14], S[ 3],
                    S[ 8], S[13], S[ 2], S[ 7],
                    S[12], S[ 1], S[ 6], S[11]]
    
        def ShiftRows_Inv(self, S):
            # 逆行移位
            return [S[ 0], S[13], S[10], S[ 7],
                    S[ 4], S[ 1], S[14], S[11],
                    S[ 8], S[ 5], S[ 2], S[15],
                    S[12], S[ 9], S[ 6], S[ 3]]
    
        def MixColumns(self, State):
            # 列混合
            return self.Matrix_Mul(self.MIX_C, State)
    
        def MixColumns_Inv(self, State):
            # 逆列混合
            return self.Matrix_Mul(self.I_MIXC, State)
    
        def RotWord(self, _4byte_block):
            # 用于生成轮密钥的字移位
            return ((_4byte_block & 0xffffff) << 8) + (_4byte_block >> 24)
    
        def SubWord(self, _4byte_block):
            # 用于生成密钥的字节替换
            result = 0
            for position in range(4):
                i = _4byte_block >> position * 8 + 4 & 0xf
                j = _4byte_block >> position * 8 & 0xf
                result ^= self.S_BOX[i][j] << position * 8
            return result
    
        def mod(self, poly, mod = 0b100011011):  
            # poly模多项式mod
            while poly.bit_length() > 8:
                poly ^= mod << poly.bit_length() - 9
            return poly
    
        def mul(self, poly1, poly2):
            # 多项式相乘
            result = 0
            for index in range(poly2.bit_length()):
                if poly2 & 1 << index:
                    result ^= poly1 << index
            return result
    
        def Matrix_Mul(self, M1, M2):  # M1 = MIX_C  M2 = State
            # 用于列混合的矩阵相乘
            M = [0] * 16
            for row in range(4):
                for col in range(4):
                    for Round in range(4):
                        M[row + col*4] ^= self.mul(M1[row][Round], M2[Round+col*4])
                    M[row + col*4] = self.mod(M[row + col*4])
            return M
    
        def round_key_generator(self, _16bytes_key):
            # 轮密钥产生
            w = [_16bytes_key >> 96, 
                 _16bytes_key >> 64 & 0xFFFFFFFF, 
                 _16bytes_key >> 32 & 0xFFFFFFFF, 
                 _16bytes_key & 0xFFFFFFFF] + [0]*40
            for i in range(4, 44):
                temp = w[i-1]
                if not i % 4:
                    temp = self.SubWord(self.RotWord(temp)) ^ self.RCon[i//4-1]
                w[i] = w[i-4] ^ temp
            return [self.num_2_16bytes(
                        sum([w[4 * i] << 96, w[4*i+1] << 64, 
                             w[4*i+2] << 32, w[4*i+3]])
                        ) for i in range(11)]
    
        def AddRoundKey(self, State, RoundKeys, index):
            # 异或轮密钥
            return self._16bytes_xor(State, RoundKeys[index])
    
        def _16bytes_xor(self, _16bytes_1, _16bytes_2):
            return [_16bytes_1[i] ^ _16bytes_2[i] for i in range(16)]
    
        def _16bytes2num(cls, _16bytes):
            # 16字节转数字
            return int.from_bytes(_16bytes, byteorder = 'big')
    
        def num_2_16bytes(cls, num):
            # 数字转16字节
            return num.to_bytes(16, byteorder = 'big')
    
        def aes_encrypt(self, plaintext_list, RoundKeys):
            State = plaintext_list
            State = self.AddRoundKey(State, RoundKeys, 0)
            for Round in range(1, 10):
                State = self.SubBytes(State)
                State = self.ShiftRows(State)
                State = self.MixColumns(State)
                State = self.AddRoundKey(State, RoundKeys, Round)
            State = self.SubBytes(State)
            State = self.ShiftRows(State)
            State = self.AddRoundKey(State, RoundKeys, 10)
            return State
    
        def aes_decrypt(self, ciphertext_list, RoundKeys):
            State = ciphertext_list
            State = self.AddRoundKey(State, RoundKeys, 10)
            for Round in range(1, 10):
                State = self.ShiftRows_Inv(State)
                State = self.SubBytes_Inv(State)
                State = self.AddRoundKey(State, RoundKeys, 10-Round)
                State = self.MixColumns_Inv(State)
            State = self.ShiftRows_Inv(State)
            State = self.SubBytes_Inv(State)
            State = self.AddRoundKey(State, RoundKeys, 0)
            return State
    
    class RC5:
    
        def __init__(self, w, R, key, strip_extra_nulls=False):
            self.w = w  # block size (32, 64 or 128 bits)
            self.R = R  # number of rounds (0 to 255)
            self.key = key  # key (0 to 2040 bits)
            self.strip_extra_nulls = strip_extra_nulls
            # some useful constants
            self.T = 2 * (R + 1)
            self.w4 = w // 4
            self.w8 = w // 8
            self.mod = 2 ** self.w
            self.mask = self.mod - 1
            self.b = len(key)
    
            self.__keyAlign()
            self.__keyExtend()
            self.__shuffle()
    
        def __lshift(self, val, n):
            n %= self.w
            return ((val << n) & self.mask) | ((val & self.mask) >> (self.w - n))
    
        def __rshift(self, val, n):
            n %= self.w
            return ((val & self.mask) >> n) | (val << (self.w - n) & self.mask)
    
        def __const(self):  # constants generation
            if self.w == 16:
                return 0xB7E1, 0x9E37  # return P, Q values
            elif self.w == 32:
                return 0xB7E15163, 0x9E3779B9
            elif self.w == 64:
                return 0xB7E151628AED2A6B, 0x9E3779B97F4A7C15
    
        def __keyAlign(self):
            if self.b == 0:  # key is empty
                self.c = 1
            elif self.b % self.w8:
                self.key += b'\x00' * (self.w8 - self.b % self.w8)  # fill key with \x00 bytes
                self.b = len(self.key)
                self.c = self.b // self.w8
            else:
                self.c = self.b // self.w8
            L = [0] * self.c
            for i in range(self.b - 1, -1, -1):
                L[i // self.w8] = (L[i // self.w8] << 8) + self.key[i]
            self.L = L
    
        def __keyExtend(self):
            P, Q = self.__const()
            self.S = [(P + i * Q) % self.mod for i in range(self.T)]
    
        def __shuffle(self):
            i, j, A, B = 0, 0, 0, 0
            for k in range(3 * max(self.c, self.T)):
                A = self.S[i] = self.__lshift((self.S[i] + A + B), 3)
                B = self.L[j] = self.__lshift((self.L[j] + A + B), A + B)
                i = (i + 1) % self.T
                j = (j + 1) % self.c
    
        def encryptBlock(self, data):
            A = int.from_bytes(data[:self.w8], byteorder='little')
            B = int.from_bytes(data[self.w8:], byteorder='little')
            A = (A + self.S[0]) % self.mod
            B = (B + self.S[1]) % self.mod
            for i in range(1, self.R + 1):
                A = (self.__lshift((A ^ B), B) + self.S[2 * i]) % self.mod
                B = (self.__lshift((A ^ B), A) + self.S[2 * i + 1]) % self.mod
            return (A.to_bytes(self.w8, byteorder='little')
                    + B.to_bytes(self.w8, byteorder='little'))
    
        def decryptBlock(self, data):
            A = int.from_bytes(data[:self.w8], byteorder='little')
            B = int.from_bytes(data[self.w8:], byteorder='little')
            for i in range(self.R, 0, -1):
                B = self.__rshift(B - self.S[2 * i + 1], A) ^ A
                A = self.__rshift(A - self.S[2 * i], B) ^ B
            B = (B - self.S[1]) % self.mod
            A = (A - self.S[0]) % self.mod
            return (A.to_bytes(self.w8, byteorder='little')
                    + B.to_bytes(self.w8, byteorder='little'))
    
        def encryptFile(self, inpFileName, outFileName):
            with open(inpFileName, 'rb') as inp, open(outFileName, 'wb') as out:
                run = True
                while run:
                    text = inp.read(self.w4)
                    if not text:
                        break
                    if len(text) != self.w4:
                        text = text.ljust(self.w4, b'\x00')
                        run = False
                    text = self.encryptBlock(text)
                    out.write(text)
    
        def decryptFile(self, inpFileName, outFileName):
            with open(inpFileName, 'rb') as inp, open(outFileName, 'wb') as out:
                while True:
                    text = inp.read(self.w4)
                    if not text:
                        break
                    text = self.decryptBlock(text)
                    if self.strip_extra_nulls:
                        text = text.rstrip(b'\x00')
                    out.write(text)
    
        def encryptBytes(self, data):
            res, run = b'', True
            while run:
                temp = data[:self.w4]
                if len(temp) != self.w4:
                    data = data.ljust(self.w4, b'\x00')
                    run = False
                res += self.encryptBlock(temp)
                data = data[self.w4:]
                if not data:
                    break
            return res
    
        def decryptBytes(self, data):
            res, run = b'', True
            while run:
                temp = data[:self.w4]
                if len(temp) != self.w4:
                    run = False
                res += self.decryptBlock(temp)
                data = data[self.w4:]
                if not data:
                    break
            return res.rstrip(b'\x00')
    
    if __name__ == '__main__':
        enc = [0x70, 0x24, 0x76, 0xfd, 0xc7, 0x29, 0xc5, 0x97, 0xef, 0xee, 0xb6, 0x22, 0x5e, 0xb5, 0x46, 0xf2, 0x39, 0x47, 0x8f, 0xc2, 0x9e, 0x9c, 0x88, 0x2b, 0xfa, 0xd8, 0x7f, 0xd3, 0xeb, 0x6c, 0x9c, 0xa6, 0x5e, 0x30, 0x18, 0xd9, 0xdb, 0x96, 0xc2, 0x2b, 0xa5, 0x57, 0x36, 0x47, 0xd5, 0x72, 0xa6, 0xd5]
    
        ## 解RC5
        rc5 = RC5(32, 12, b'welcometotsctf\x00\x00')
        data = bytes(enc)
        result = rc5.decryptBytes(data)
        invRC5 = [int(t) for t in result]
    
        # 解AES
        aes = AES()
    
        # 子密钥(从程序中dump)
        w = [0x77,0x6f,0x6f,0x74,0x65,0x6d,0x74,0x66,0x6c,0x65,0x73,0x0,0x63,0x74,0x63,0x0,0xc6,0xa9,0xc6,0xb2,0x53,0x3e,0x4a,0x2c,0x5a,0x3f,0x4c,0x4c,0xf5,0x81,0xe2,0xe2,0xe9,0x40,0x86,0x34,0xbc,0x82,0xc8,0xe4,0xec,0xd3,0x9f,0xd3,0x75,0xf4,0x16,0xf4,0x97,0xd7,0x51,0x65,0x69,0xeb,0x23,0xc7,0x7c,0xaf,0x30,0xe3,0x2c,0xd8,0xce,0x3a,0x81,0x56,0x7,0x62,0xd7,0x3c,0x1f,0xd8,0x7b,0xd4,0xe4,0x7,0x98,0x40,0x8e,0xb4,0x17,0x41,0x46,0x24,0x21,0x1d,0x2,0xda,0x24,0xf0,0x14,0x13,0xcb,0x8b,0x5,0xb1,0xce,0x8f,0xc9,0xed,0x35,0x28,0x2a,0xf0,0xb0,0x40,0x54,0x47,0xea,0x61,0x64,0xd5,0x8d,0x2,0xcb,0x26,0xcb,0xe3,0xc9,0x39,0x1a,0x5a,0xe,0x49,0x32,0x53,0x37,0xe2,0x1f,0x1d,0xd6,0xf0,0xc9,0x2a,0xe3,0xda,0xac,0xf6,0xf8,0xb1,0x5a,0x9,0x3e,0xdc,0xfd,0xe0,0x36,0xc6,0x5d,0x77,0x94,0x4e,0xc1,0x37,0xcf,0x7e,0x7b,0x72,0x4c,0x90,0xe4,0x4,0x32,0xf4,0x3a,0x4d,0xd9,0x97,0xe,0x39,0xf6,0x88,0xbf,0xcd,0x81,0x11]
        
        RoundKeys = []
        for i in range(11):
            temp = w[16*i:16*(i+1)]
            r = []
            for j in range(4):
                for k in range(4):
                    r.append(temp[4*k+j])
            RoundKeys.append(bytes(r))
        
        flag = []
        for i in range(0, len(invRC5), 16):
            ciphertext = bytes(invRC5[i:i+16])
            plaintext = aes.aes_decrypt(ciphertext, RoundKeys)
            flag = flag + plaintext
        print("".join(chr(i) for i in flag))
    

    something else

    作为re第一题,是我面向源码做题了...我自己感觉AES改的少应该还可以,但是其实拿到题改动未知的时候会想很多,这个代码又很复杂。我不应该改动它的,或者换一个简单的算法 (跪

    babywasm

    题目描述中出现了wasi,稍微搜一下应该能搜到这份使用教程: https://docs.wasmer.io/integrations/c/setup 里面包含了很多c程序调用wasm代码的示例。这道题用了wasmer-c-api来构建,主程序为re, program.wasm为子程序。
    对照着示例(eg: https://docs.wasmer.io/integrations/c/examples/host-functions)不难看出,ELF的main函数中首先导入boom函数到wasm的环境变量中,然后调用了wasm的start函数。

    对program.wasm逆向分析.基础教程:
    https://xz.aliyun.com/t/5170
    反汇编的话,可以把wasm转成c语言的格式,用wasm2c

    $ ./wasm2c wasm.wasm -o wasm.c
    ==> 得到wasm.c和wasm.h
    

    但是因为生成的c语言很长而且基本跟看wat没什么区别,所以需要再编译成二进制文件放到ida里面去看
    将之前反编译出来的wasm.c,wasm.h,以及wabt项目内的wasm-rt.h,wasm-rt-impl.c,wasm-rt-impl.h三个文件放到同一个文件夹。
    直接gcc wasm.c会报错,因为很多wasm的函数没有具体的实现。但是我们可以只编译不链接,我们关心的只是程序本身的逻辑,不需要真正编译出能运行的elf来。
    $ gcc -c wasm.c -o wasm.o 得到的还未连接的elf文件wasm.o, 将wasm.o放到ida里面分析会比较清楚一些。

    得到二进制文件后首先可以搜索一下字符串,可以发现一个特殊的字符串There is a fire in each one's heart,查找引用可以看到在init_memory中找到,这部分主要是初始化字符串,这个字符串存储在了w2c_memory + 2176的偏移处。

    查看start函数,这个函数中引用了四个函数,在w2c_f9中调用了Z_envZ_boomZ_ii,这个函数是在ELF中导入的,所以这里应该是程序的关键点。

    __int64 __fastcall w2c_f9(__m128i a1)
    {
      int v1; // ST30_4
      int v2; // ST34_4
      unsigned int v3; // ST38_4
      unsigned int v4; // ST3C_4
      unsigned int v5; // ST6C_4
      unsigned int v7; // [rsp+Ch] [rbp-64h]
    
      if ( ++wasm_rt_call_stack_depth > 0x1F4u )
        wasm_rt_trap(7LL);
      w2c_g0 -= 16;
      v7 = w2c_g0;
      i32_store(&w2c_memory, (unsigned int)w2c_g0 + 12LL, 0);
      i32_store(&w2c_memory, v7, 2752);
      w2c_f11(1024u, v7, a1);
      v1 = w2c_f106(2752u);
      i32_store(&w2c_memory, v7 + 8LL, v1);
      v2 = w2c_f63(256LL);
      i32_store(&w2c_memory, v7 + 4LL, v2);
      v3 = i32_load(&w2c_memory, v7 + 4LL);
      w2c_f7(v3, 2176LL, 36LL);
      v4 = i32_load(&w2c_memory, v7 + 4LL);
      v5 = i32_load(&w2c_memory, v7 + 8LL);
      w2c_f8(v4, 2752LL, v5);
      if ( (unsigned int)Z_envZ_boomZ_ii(2752LL) == 0 )
        w2c_f103(1034LL, 0LL);
      else
        w2c_f103(1027LL, 0LL);
      w2c_g0 = v7 + 16;
      --wasm_rt_call_stack_depth;
      return 0LL;
    }
    

    在这个函数中首先调用了w2c_f11函数,参数是1024,猜测是字符串内容,可以找到1024偏移处字符串的值(0x39480 - 2147 + 1024 -> 0x3901d),这个地方的字符串是%s所以这个地方应该是scanf函数。输入的字符串存储在2752处。w2c_f106的参数是输入字符串的位置,里面的代码很明显时求输入字符串的长度。长度存储在v7 + 8LL处。
    然后将一个与输入无关的函数w2c_f63返回值存储在v7 + 4LL处,然后w2c_f7(v3, 2176LL, 36LL);这个函数第一个参数时w2c_f63的返回值,第二个参数是There is a fire in each one's heart这个字符串,第三个参数是字符串的长度加1.
    进入w2c_f7这个函数内部可以发现,进行了两个循环,每个循环执行256次,第一个循环中对长度取模(第三个参数),第二个循环中对256取模。这时候应该猜测出来是RC4(当然不猜直接看也还比较容易看懂,就是数据赋值变成了store和load而已,给了流密钥的提示就更容易看了)。
    所以w2c_f7这个函数是密钥的初始化,随后调用w2c_f8(v4, 2752LL, v5),第一个参数是初始化变换后的密钥,第二个参数是输入的字符串,第三个参数是字符串的长度(这个函数中的加密也有对256取模 & 数据交换的各种操作)
    在加密完成过后调用Z_envZ_boomZ_ii函数,通过打印Right

    solve

    from Crypto.Cipher import ARC4
    def myRC4(data,key):
        rc41 = ARC4.new(key)
        encrypted = rc41.encrypt(data)
        return encrypted.encode('hex').upper()
    enc = [0xbf, 0xcf, 0x61, 0x4c, 0xed, 0x4c, 0x29, 0x24, 0x5, 0x8a, 0x60, 0x87, 0x35, 0x81, 0x73, 0xf, 0xde, 0x96, 0x65, 0xa5, 0x41, 0x18, 0xac, 0xf5, 0x1c, 0x42, 0xda, 0x26, 0x96, 0xad, 0x35, 0xde, 0xf4, 0xc3, 0xcd, 0x1c, 0x96, 0xeb]
    s = "".join(chr(i) for i in enc)
    key = "There is a fire in each one's heart\0"# 因为初始化密钥的长度比这个字符串的长度长1,所以记得补个0,当然如果找的是实现代码可以直接用长度36
    enc = myRC4(s, key)
    print enc.decode('hex')
    

    babybios

    这道题在uboot arm的bios里添加了一个getflag指令。在指令的回调函数里实现了对flag的校验逻辑。动态调试可以使用gdb-multiarch+qemu去调试,静态直接用IDA分析即可。
    这道题的关键是找到指令的回调函数,而线索就是uboot在添加和搜索指令的机制。https://blog.csdn.net/itxiebo/article/details/50991049
    观察cmd_tbl结构体可以发现,结构体包含了命令名字符串指针name,回调函数指针cmd,命令帮助字符串指针help

    struct cmd_tbl {
        char        *name;      /* Command Name         */
        int     maxargs;    /* maximum number of arguments  */
                        /*
                         * Same as ->cmd() except the command
                         * tells us if it can be repeated.
                         * Replaces the old ->repeatable field
                         * which was not able to make
                         * repeatable property different for
                         * the main command and sub-commands.
                         */
        int     (*cmd_rep)(struct cmd_tbl *cmd, int flags, int argc,
                       char *const argv[], int *repeatable);
                        /* Implementation function  */
        int     (*cmd)(struct cmd_tbl *cmd, int flags, int argc,
                       char *const argv[]);
        char        *usage;     /* Usage message    (short) */
    #ifdef  CONFIG_SYS_LONGHELP
        char        *help;      /* Help  message    (long)  */
    #endif
    #ifdef CONFIG_AUTO_COMPLETE
        /* do auto completion on the arguments */
        int     (*complete)(int argc, char *const argv[],
                        char last_char, int maxv, char *cmdv[]);
    #endif
    };
    

    那么在IDA中搜索命令名字符串指针,也就是getflag字符串的指针0x88EEA,就找到了下图的位置(IDA按D键可以修改数据的类型)。

    image

    对照cmd_tbl结构体,能够确定命令回调函数的地址为0x11A00。反编译这个函数。

    image

    逆一下几个函数,基本能够确定流程,sub_116D8函数对输入做base64解码,sub_118A8函数之后的部分首先填充了一个9*9的数独,之后对数独进行校验。
    把数独从ida中抠出来放进在线数独求解器,再做base64编码,就得到了flag。

    wbenc

    这道题来自某IoT设备中的白盒加密(white box encrypt)。
    加密分为三个部分,第一和第三部分进行了一些简单的变换,可以直接逆向。
    中间的大段代码需要花功夫逆一下。
    逆向之后,整个中间部分共有10轮,每一轮的操作如下。

    v13 = int32_split(magic_tbl[buf[5]  | 0x100] ^ magic_tbl[buf[0]]          ^ magic_tbl[buf[10] | 0x200] ^ magic_tbl[buf[15] | 0x300])
    v15 = int32_split(magic_tbl[buf[4]  | 0x400] ^ magic_tbl[buf[9] | 0x500]  ^ magic_tbl[buf[14] | 0x600] ^ magic_tbl[buf[3]  | 0x700])
    v17 = int32_split(magic_tbl[buf[13] | 0x900] ^ magic_tbl[buf[8] | 0x800]  ^ magic_tbl[buf[2]  | 0xA00] ^ magic_tbl[buf[7]  | 0xB00])
    v18 = int32_split(magic_tbl[buf[11] | 0xF00] ^ magic_tbl[buf[1] | 0xD00]  ^ magic_tbl[buf[12] | 0xC00] ^ magic_tbl[buf[6]  | 0xE00])
    

    分析可知,这四行代码的是独立的,也就是每行的4byte输入转换为4byte输出。那么对这样的4byte变换直接暴破的话,时间复杂度高。2^32很难在普通计算机下暴破。
    我们可以利用中间相遇攻击的思想对暴力破解进行优化。例如我们对下面的第一行进行逆向(暴破)。

    v13 = int32_split(magic_tbl[buf[5]  | 0x100] ^ magic_tbl[buf[0]]          ^ magic_tbl[buf[10] | 0x200] ^ magic_tbl[buf[15] | 0x300])
    

    首先枚举所有可能的magic_tbl[buf[5] | 0x100] ^ magic_tbl[buf[0]]这样一个异或和,也就是0xff * 0xff种可能,存储在一张有序表当中。
    再枚举所有的magic_tbl[buf[10] | 0x200] ^ magic_tbl[buf[15] | 0x300],也就是后面两个字节的异或和,把异或的结果和v13去做异或,得到的值在前面存储的表中查询是否存在,有序表的查询操作时间复杂度是O(logn)。
    如果存在,那么说明我们找到了一组(buf[5],buf[0]],buf[10],buf[15]),满足magic_tbl[buf[5] | 0x100] ^ magic_tbl[buf[0]] ^ magic_tbl[buf[10] | 0x200] ^ magic_tbl[buf[15] | 0x300]=v13,也就是从输出的4byte逆向得到了输入的4byte。
    利用这样一个空间换时间的优化,能够在可以接受的时间复杂度下逆向得到输入。
    之后编写解密脚本就能够从输出获得输入。
    解密脚本如下

    import sys
    
    magic_tbl = []
    
    def loadTable(filename):
        global magic_tbl
    
        fp = open(filename, 'r')
        content = fp.read()
        fp.close()
    
        content = content.split(' ')
        content = [int(content[i], 16) for i in xrange(len(content))]
        tbl = [content[i] | content[i+1]<<8 | content[i+2]<<16 | content[i+3]<<24 for i in xrange(0, len(content), 4)]
        
        print hex(len(tbl))
        magic_tbl = tbl
    
    
    def listDump(l):
        for x in xrange(len(l)):
            print hex(l[x]),
        print ''
        
    def int32_split(x):
        return [x & 0xff, (x >> 8) & 0xff, (x >> 16) & 0xff, (x >> 24) & 0xff]
    
    def ROTR(ch, n, sz):
        tmp = ((ch >> (n%sz)) | (ch << (sz - (n % sz) ))) & ((1 << sz) - 1)
        return tmp
    
    def ROTL(ch, n, sz):
        tmp = ((ch << (n%sz)) | (ch >> (sz - (n % sz) ))) & ((1 << sz) - 1)
        return tmp
    
    def decode(input):
        buf = [0] * 16
    
    # ROT decode
        v64 = 0
        v66 = 0
        v68 = 0
        v70 = 0
        
        for i in xrange(16):
    
            tmp = ord(input[i]) ^ v64
            buf[i] = ROTR(tmp, i*2, 8)
            v64 += 0xd
            v66 += 0xca
            v68 += 0x73
            v70 += 0x9b
    
        #listDump(buf)
        #print buf
    
        v56 = int32_split(buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24))
        v57 = int32_split(buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24))
        v67 = int32_split(buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24))
        v71 = int32_split(buf[12] | (buf[13] << 8) | (buf[14] << 16) | (buf[15] << 24))
    
        # MITM attack
    
        # init
        def get_dict(x, y):
            dic = {}
            for i in xrange(256):
                for j in xrange(256):
                    tmp = magic_tbl[i | x] ^ magic_tbl[j | y]
                    dic[tmp] = [i, j]
    
            return dic
    
        def mitm(dic, res, u, v):
            res = res[0] | res[1] << 8 | res[2] << 16 | res[3] << 24
    
            for i in xrange(256):
                for j in xrange(256):
                    tmp = magic_tbl[i | u] ^ magic_tbl[j | v]
                    tmp ^= res
                    if dic.has_key(tmp):
                        [k, l] = dic[tmp]
                        #print [i, j, k, l]
                        return [i, j, k, l]
    
        # round 9
        v51 = [0] * 4
        v52 = [0] * 4
        v53 = [0] * 4
        v55 = [0] * 4
        v55[3], v53[2], v52[1], v51[0] = mitm(get_dict(0x9100, 0x9000), v56, 0x9300, 0x9200)
        v52[0], v53[1], v55[2], v51[3] = mitm(get_dict(0x9600, 0x9700), v57, 0x9400, 0x9500)
        v51[2], v55[1], v53[0], v52[3] = mitm(get_dict(0x9800, 0x9B00), v67, 0x9A00, 0x9900)
        v51[1], v55[0], v52[2], v53[3] = mitm(get_dict(0x9E00, 0x9F00), v71, 0x9D00, 0x9C00)
    
        # round 8
        v47 = [0] * 4
        v48 = [0] * 4
        v49 = [0] * 4
        v50 = [0] * 4
        v49[2], v48[1], v47[0], v50[3] = mitm(get_dict(0x8000, 0x8300), v51, 0x8200, 0x8100)
        v48[0], v49[1], v50[2], v47[3] = mitm(get_dict(0x8600, 0x8700), v52, 0x8400, 0x8500)
        v49[0], v50[1], v47[2], v48[3] = mitm(get_dict(0x8A00, 0x8B00), v53, 0x8800, 0x8900)
        v49[3], v50[0], v47[1], v48[2] = mitm(get_dict(0x8D00, 0x8E00), v55, 0x8F00, 0x8C00)
    
        # round 7
        v42 = [0] * 4
        v43 = [0] * 4
        v44 = [0] * 4
        v46 = [0] * 4
        v44[2], v43[1], v42[0], v46[3] = mitm(get_dict(0x7000, 0x7300), v47, 0x7200, 0x7100)
        v43[0], v44[1], v46[2], v42[3] = mitm(get_dict(0x7600, 0x7700), v48, 0x7400, 0x7500)
        v43[3], v44[0], v46[1], v42[2] = mitm(get_dict(0x7900, 0x7A00), v49, 0x7B00, 0x7800)
        v46[0], v42[1], v43[2], v44[3] = mitm(get_dict(0x7E00, 0x7F00), v50, 0x7C00, 0x7D00)
    
        # round 6
        v37 = [0] * 4
        v38 = [0] * 4
        v40 = [0] * 4
        v41 = [0] * 4
        v38[1], v37[0], v40[2], v41[3] = mitm(get_dict(0x6200, 0x6300), v42, 0x6100, 0x6000)
        v38[0], v40[1], v41[2], v37[3] = mitm(get_dict(0x6600, 0x6700), v43, 0x6400, 0x6500)
        v40[0], v41[1], v37[2], v38[3] = mitm(get_dict(0x6A00, 0x6B00), v44, 0x6800, 0x6900)
        v41[0], v37[1], v38[2], v40[3] = mitm(get_dict(0x6E00, 0x6F00), v46, 0x6C00, 0x6D00)
    
        # round 5
        v33 = [0] * 4
        v34 = [0] * 4
        v35 = [0] * 4
        v36 = [0] * 4
        v34[1], v33[0], v35[2], v36[3] = mitm(get_dict(0x5200, 0x5300), v37, 0x5100, 0x5000)
        v34[0], v35[1], v36[2], v33[3] = mitm(get_dict(0x5600, 0x5700), v38, 0x5400, 0x5500)
        v35[0], v36[1], v33[2], v34[3] = mitm(get_dict(0x5A00, 0x5B00), v40, 0x5800, 0x5900)
        v36[0], v33[1], v34[2], v35[3] = mitm(get_dict(0x5E00, 0x5F00), v41, 0x5C00, 0x5D00)
    
        # round 4
        v28 = [0] * 4
        v30 = [0] * 4
        v31 = [0] * 4
        v32 = [0] * 4
        v28[0], v30[1], v31[2], v32[3] = mitm(get_dict(0x4200, 0x4300), v33, 0x4000, 0x4100)
        v31[1], v30[0], v32[2], v28[3] = mitm(get_dict(0x4600, 0x4700), v34, 0x4500, 0x4400)
        v31[0], v32[1], v28[2], v30[3] = mitm(get_dict(0x4A00, 0x4B00), v35, 0x4800, 0x4900)
        v32[0], v28[1], v30[2], v31[3] = mitm(get_dict(0x4E00, 0x4F00), v36, 0x4C00, 0x4D00)
        
        # round 3
        v24 = [0] * 4
        v25 = [0] * 4
        v26 = [0] * 4
        v27 = [0] * 4
        v25[1], v24[0], v26[2], v27[3] = mitm(get_dict(0x3200, 0x3300), v28, 0x3100, 0x3000)
        v25[0], v26[1], v27[2], v24[3] = mitm(get_dict(0x3600, 0x3700), v30, 0x3400, 0x3500)
        v26[0], v27[1], v24[2], v25[3] = mitm(get_dict(0x3A00, 0x3B00), v31, 0x3800, 0x3900)
        v27[0], v24[1], v25[2], v26[3] = mitm(get_dict(0x3E00, 0x3F00), v32, 0x3C00, 0x3D00)
    
        # round 2
        v19 = [0] * 4
        v21 = [0] * 4
        v22 = [0] * 4
        v23 = [0] * 4
        v19[0], v21[1], v22[2], v23[3] = mitm(get_dict(0x2200, 0x2300), v24, 0x2000, 0x2100)
        v22[1], v21[0], v23[2], v19[3] = mitm(get_dict(0x2600, 0x2700), v25, 0x2500, 0x2400)
        v22[0], v23[1], v19[2], v21[3] = mitm(get_dict(0x2A00, 0x2B00), v26, 0x2800, 0x2900)
        v23[0], v19[1], v21[2], v22[3] = mitm(get_dict(0x2E00, 0x2F00), v27, 0x2C00, 0x2D00)
    
        # round 1
        v13 = [0] * 4
        v15 = [0] * 4
        v17 = [0] * 4
        v18 = [0] * 4
        v17[2], v13[0], v15[1], v18[3] = mitm(get_dict(0x1100, 0x1300), v19, 0x1200, 0x1000)
        v15[0], v17[1], v18[2], v13[3] = mitm(get_dict(0x1600, 0x1700), v21, 0x1400, 0x1500)
        v17[0], v18[1], v13[2], v15[3] = mitm(get_dict(0x1A00, 0x1B00), v22, 0x1800, 0x1900)
        v18[0], v13[1], v15[2], v17[3] = mitm(get_dict(0x1E00, 0x1F00), v23, 0x1C00, 0x1D00)
    
        # round 0
        buf = [0] * 16
        buf[5], buf[0], buf[10], buf[15] = mitm(get_dict(0x200, 0x300), v13, 0x100, 0x000)
        buf[4], buf[9], buf[14], buf[3] =  mitm(get_dict(0x600, 0x700), v15, 0x400, 0x500)
        buf[13], buf[8], buf[2], buf[7] =  mitm(get_dict(0xA00, 0xB00), v17, 0x900, 0x800)
        buf[11], buf[1], buf[12], buf[6] = mitm(get_dict(0xC00, 0xE00), v18, 0xF00, 0xD00)
    
        #print buf
    
        #ROT Decode
        v4 = 0
        v5 = 0
        v6 = 0
        v7 = 0
        msg = [0] * 16
        idx = 0
    
        while True:
            c = 0
            for i in xrange(256):
                v9 = ROTL(i, v6, 8)
                v10 = ROTL(i, v5, 8) ^ v9
                v11 = ROTL(i, v4, 8)
    
                v12 = v11 ^ v10 ^ v7
                v12 &= 0xff
                #print v12, buf[idx]
                if v12 == buf[idx]:
                    c = i
                    break
            
            v4 += 0xF1
            v5 += 0x54
            v6 += 0xC3
            v7 += 0x6C
            
            msg[idx] = c
            idx += 1
        
            if v7 == 0x6C0:
                break
        
        #msg = [chr(msg[i]) for i in xrange(len(msg))]
        #msg = ''.join(msg)
        return msg
    
    # 从IDA中导出magic_tbl
    loadTable('export_results.txt')
    
    res = decode("4bf7f78ef088f4c94472f9f61f2c3331".decode('hex'))
    print res
    a = ''.join(["%c" % res[x] for x in xrange(len(res))])
    
    res = decode("e68ac8bbcbac1e982cc246cd9dd34308".decode('hex'))
    a += ''.join(["%c" % res[x] for x in xrange(len(res))])
    print "TSCTF{%s}" % a
    

    相关文章

      网友评论

          本文标题:TSCTF202-re

          本文链接:https://www.haomeiwen.com/subject/nmiamktx.html