首页
论坛
课程
招聘
[原创]2020KCTF秋季赛第八题wp
2020-12-6 22:12 2602

[原创]2020KCTF秋季赛第八题wp

2020-12-6 22:12
2602

分析:

做题过程一波三折,还是直接写结论吧

  • 固定模式的花指令,有些花指令涉及字节码的复用,挺有新意
  • 反调试,检测了进程名和调试器
  • key的长度限定为448[0-9a-f],name长度小于0xff
  • 两次md5算name的两个hash
  • bytes.fromhex(key),然后分为0x20和0xc0的两部分进行独立的验证

第一部分:

  • key_part1转成8个uint_32t,依次传进一个虚拟机内加密(关键)
  • aes_256_cbc_encrypt,iv = b'\x00'*16' key = sha256(b'1_L0V3_BXS_F0REVER!').digest()
  • 两两前后做差==0x7f,取偶数位字节
  • 改了sbox的 aes_256_ecb_encrypt key = b'Wo YongYuan XiHuan KanXun LunTan'。但是把论坛名字拼错了
  • 与第一个hash对比,至此第一部分验证结束

第二部分:

  • 0xc0 = 3x8x8,循环64次,每次取3个字节转成8个字节*3bit,与固定系数依次相乘完求和,要求结果为8或-8,依次设置4个bit位,总共输出256bit。(此处会多解??没试)
  • 256bit转成16个uint16_t,与一些乱七八糟的系数进行一些乱七八糟的运算(高斯消元?),然后又算了一些模0xff8f的乘法,然后输出128bit(这里好像也会多解?)
  • 和第二个hash与一堆常数运算,组成一个char[16]的buffer,要求buffer的MD5为预期值。第二部分验证结束。

解决:

第一部分:

  • 把固定模式的花指令替换成等长的nop
  • memdump出aes的sbox,算出inv_sbox
  • 虚拟机的字节码在tls里改了4个字节
  • 虚拟机支持移位,亦或,加法,与,赋值5种操作,把vm自己实现一遍然后trace字节码。
  • 加法操作只是自己加自己,等价于v<<=1 猜测vm输出32bit是输入32bit的线性组合。调试取几组数据验证通过。

第二部分:

  • 根据公开的key得到预期的buffer
  • 系数只和系数运算,不和输入运算,是固定的常数,16个输入uint16_t运算过程是线性的。然后算个模逆就差不多了。
  • 64*4个bit,每4个bit对应3个字节。

代码写的很烂,不想改了

去花:

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
import re
patterns = [
    (rb"\x81\xFC\x00\x10\x00\x00\x7C\x05\xE9\x06\x00\x00\x00\x81\xC4\x04\x01\x00\x00\x40\x48",21),
    (rb"\x66\xb8\xeb\x05\x31\xc0\x74\xfa\xe8",9),
    (rb"\x33\xc0\x74\x01\xe9",5),
    (rb"\xeb\xff\xc0\x48",4),
    (rb"\x74\x03\x75\x01\xe8",5),
    (rb"\xe8\x01\x00\x00\x00\xe9\x80\x04\x24\x06\xc3",11),
    (rb"\xe8\x03\x00\x00\x00\xf8\x73\x07\x50\x8b\x44\x24\04\xff\xd0\x8b\x44\x24\x04\x83\xc4\x0c",22),
    (rb"\x8D\x45\xF8\xC7\x45\xF8\x00\x00\x00\x00\x6A\x00\x6A\x04\x50\x6A\x07\xFF\x15\x40\xA4\x57\x00\x50\xFF\x15\x20\xA9\x57\x00\x83\x7D\xF8\xFF\x0F\x84\xA1\x03\x00\x00",40),
    (rb"\xFF\x15\x3C\xA4\x57\x00\x8D\x8D\x2C\xFD\xFF\xFF\xC7\x85\x2C\xFD\xFF\xFF\x10\x00\x01\x00\x51\x50\xFF\x15\x38\xA4\x57\x00\x83\xBD\x30\xFD\xFF\xFF\x00\x0F\x85\xD0\x00\x00\x00\x83\xBD\x34\xFD\xFF\xFF\x00\x0F\x85\xC3\x00\x00\x00\x83\xBD\x38\xFD\xFF\xFF\x00\x0F\x85\xB6\x00\x00\x00\x83\xBD\x3C\xFD\xFF\xFF\x00\x0F\x85\xA9\x00\x00\x00",82)
]
 
functins_to_be_patched = [
    (0x402EE0,0x4033A0),
    (0x404010,0x404010+0x2000)
]
 
class Patcher:
    def __init__(self):
        with open('origin.exe','rb') as fp:
            self.file_data = list(fp.read())
 
    def va_to_offset(self,va):
        return va - 0x400000 + 0x400 - 0x1000
 
    def patch_all(self):
        for f in functins_to_be_patched:
            start_va = f[0]
            end_va = f[1]
            start_offset = self.va_to_offset(start_va)
            end_offset = self.va_to_offset(end_va)
 
            data = self.file_data[start_offset:end_offset]         
            opcode = bytes(data)
            for p in patterns:
                for m in re.finditer(p[0],opcode):
                    pos = m.start(0)
                    data[pos:pos+p[1]] = [0x90 for _ in range(p[1])]
                opcode = bytes(data)
 
            self.file_data[start_offset:end_offset] = data
    def output(self):
        with open('patched.exe','wb') as fp:
            fp.write(bytes(self.file_data))
 
 
if __name__ == "__main__":
    patcher = Patcher()
    patcher.patch_all()
    patcher.output()

inv_sbox

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static uint8_t inv_s_box[256] = {
    0xbb, 0xda, 0x21, 0x9c, 0xca, 0x9b, 0x31, 0x71, 0x51, 0x33, 0xb5, 0xc2, 0x8e, 0xa6, 0xeb, 0x99,
    0x1e, 0xea, 0xb1, 0x48, 0xb4, 0x89, 0x29, 0x18, 0xd4, 0x2a, 0x1a, 0x76, 0x4f, 0xaa, 0x9d, 0xdf,
    0x3e, 0x73, 0xcc, 0xd2, 0x58, 0x3d, 0xa8, 0x5, 0xa1, 0xc9, 0x6e, 0x40, 0x3f, 0x10, 0x17, 0x87 ,
    0x23, 0xbe, 0x26, 0x4d, 0xf5, 0x5e, 0xb, 0x98, 0xde, 0xee, 0xc4, 0x14, 0xb7, 0x6b, 0xa4, 0x36 ,
    0x5d, 0x42, 0xe6, 0x8f, 0xa2, 0x41, 0xb9, 0x80, 0x3c, 0x49, 0x57, 0x9a, 0x83, 0x34, 0xf7, 0x28,
    0x1d, 0xe2, 0xb0, 0x7b, 0x67, 0x19, 0x7e, 0x2e, 0x39, 0x30, 0xc1, 0x16, 0x7f, 0x95, 0x45, 0x63,
    0xba, 0xe7, 0xe5, 0x0, 0x78, 0xae, 0x96, 0x1c, 0x3a, 0x4b, 0x56, 0xe, 0x72, 0xa9, 0xb8, 0xed,
    0x82, 0x2b, 0xf0, 0x7, 0x68, 0x50, 0xe4, 0x5b, 0x32, 0x5c, 0xd0, 0xe3, 0x1, 0xc6, 0x2, 0x4,
    0xab, 0xcd, 0x88, 0x24, 0x85, 0x44, 0x4e, 0x12, 0xa5, 0x81, 0x3, 0xc, 0x8d, 0x27, 0xad, 0x9,
    0xfb, 0xd, 0x7d, 0x3b, 0xcf, 0x94, 0x61, 0x6, 0xf9, 0xdb, 0xdd, 0xfa, 0xe0, 0xc3, 0xd9, 0x91,
    0xf, 0xe8, 0x75, 0x93, 0xc0, 0xc8, 0xf3, 0x6d, 0x5a, 0xf2, 0xd1, 0x4a, 0xd7, 0x65, 0x22, 0xd8,
    0x6a, 0xe9, 0x15, 0xf8, 0xc7, 0xbd, 0xd5, 0x8c, 0xb2, 0x70, 0x55, 0xf4, 0xd3, 0x47, 0x86, 0x53,
    0x9f, 0x13, 0x7c, 0xb3, 0x66, 0xaf, 0x69, 0xdc, 0xff, 0x90, 0x46, 0x4c, 0x54, 0xec, 0x97, 0xa3,
    0xbf, 0x62, 0x7a, 0xa, 0x37, 0xfd, 0x20, 0x25, 0x43, 0x52, 0xbc, 0x1b, 0x9e, 0x11, 0xd6, 0x6c,
    0x84, 0x35, 0x5f, 0xe1, 0x92, 0x1f, 0x74, 0xac, 0xef, 0xf6, 0x2f, 0xcb, 0x60, 0x2c, 0x64, 0x8b,
    0x6f, 0x59, 0x77, 0x38, 0x2d, 0xc5, 0x79, 0xf1, 0xce, 0xfc, 0xa0, 0xb6, 0xfe, 0x8a, 0xa7, 0x8
};

解第一部分:

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import struct
from Crypto.Cipher import AES
from hashlib import sha256
u32 = lambda s:struct.unpack('<I',s)[0]
p32 = lambda x:struct.pack("<I",x)
def get_opcode():
    with open('opcode.bin','rb') as fp:
        data = fp.read()
    opcode = data[6160:6160+0x111F71]
    assert(opcode[0] == 0x69)
    return opcode
 
 
def get_bit(v,n):
    return 1 if (v>>n)&1 else 0
 
def get_ops():
    opcode = get_opcode()
    pc = 0
    vals_a = [0x6978,0x4b5a,0x2d3c,0xf1e,0xcdef,0x89ab,0x4567,0]
    vals_b = [0x23,0x01,0x67,0x45,0xAB,0x89,0xEF,0xCD,0x1e,0x0f,0x3c,0x2d,0x5a,0x4b,0x78,0x69]
    operations = []
 
    cnt = 0
    while pc < 0x111F71:
        andval = u32(opcode[pc:pc+4])
        k3 = get_bit(andval,7) + 2*get_bit(andval,16)
        idx = get_bit(andval,8) + get_bit(andval,19)*2 + get_bit(andval,27)*4
 
        a = opcode[pc+4]
        b = opcode[pc+k3+5]
        c = opcode[pc+k3+6]
        idxb = a ^^ vals_b[2*idx+1]
        idxa = idxb >> 4
        idxb = idxb & 0xf
        op = vals_b[2*idx]^^b
        shval = c
 
        operations.append((op,idxa,idxb,andval,shval))
 
        pc = pc + k3 + 7
 
        vals_a[7] = b + (a<<8)
        vals_b[0] = vals_a[6]&0xff
        vals_b[1] = (vals_a[6]>>8)&0xff
        vals_b[2] = vals_a[5]&0xff
        vals_b[3] = (vals_a[5]>>8)&0xff
        vals_b[4] = vals_a[4]&0xff
        vals_b[5] = (vals_a[4]>>8)&0xff
        vals_b[6] = vals_a[3]&0xff
        vals_b[7] = (vals_a[3]>>8)&0xff
        vals_b[8] = vals_a[2]&0xff
        vals_b[9] = (vals_a[2]>>8)&0xff
        vals_b[10] = vals_a[1]&0xff
        vals_b[11] = (vals_a[1]>>8)&0xff
        vals_b[12] = vals_a[0]&0xff
        vals_b[13] = (vals_a[0]>>8)&0xff
 
 
        vals_a[7] = b + (a<<8)
        vals_a[6] = vals_a[5]
        vals_a[5] = vals_a[4]
        vals_a[4] = vals_a[3]
        vals_a[3] = vals_a[2]
        vals_a[2] = vals_a[1]
        vals_a[1] = vals_a[0]
        vals_a[0] = vals_a[7]
 
        vals_b[14] = vals_a[7]&0xff
        vals_b[15] = (vals_a[7]>>8)&0xff
    return operations
def vm_calc(v,operations):
    data = [0]*16
    # data[15] = 0xD55D8CDD
    data[15] = v
    # wanted = 0xEE95C1D4
 
    for ops in operations:
 
        op,idxa,idxb,andval,shval = ops
        if op&0x40:
            data[idxa] &= andval
            data[idxa] &= 0xffffffff
        elif op&0x20:
            # print(hex(shval))
            data[idxa] >>= shval
            data[idxa] &= 0xffffffff
        elif op&0x10:
            # print(hex(shval))
            data[idxa] <<= shval
            data[idxa] &= 0xffffffff
        elif op&8:
            data[idxa] ^^= data[idxb]
        elif op&4:
            # print(cnt)
            data[idxa] = data[idxb]
            data[idxa] &= 0xffffffff
        elif op&2:
            data[idxa] += data[idxb]
            data[idxa] &= 0xffffffff
 
    return data[14]
 
def bits_to_val(v):
    res = 0
    for b in v[::-1]:
        res <<= 1
        res += b
    return res
 
def val_to_bits(val):
    return [(val>>(31-i))&1 for i in range(32)]
 
def recover_matrix():
    ops = get_ops()
    res = [[0 for i in range(32)] for j in range(32)]
    for i in range(32):
        v = [0 for _ in range(32)]
        v[i] = 1
        x = bits_to_val(v)
        o = vm_calc(x,ops)
        bits = val_to_bits(o)
        for j in range(32):
            res[j][i] = bits[j]
    return res
 
def solve_part3(wanted):
    assert(len(wanted)==16)
    res = []
    for n in wanted:
        res.append(n)
        res.append((n+0x7f)&0xff)
    return bytes(res)
 
def solve_part1(wanted):
    res = []
    A = recover_matrix()
    A = Matrix(GF(2),A)
    _A = A.inverse()
    for i in range(8):
        y = u32(wanted[4*i:4*i+4])
        bits = val_to_bits(y)
        y = vector(GF(2),bits)
        x = _A*y
        x = [int(b) for b in x]
        # print(x)
        x = bits_to_val(x)
 
        res.append(x)
    res = b''.join([p32(x) for x in res])
    return res.hex()
 
def solve_part2(wanted):
    iv = b'\x00'*16
    key = sha256(b"1_L0V3_BXS_F0REVER!").digest()
    ci = AES.new(key,AES.MODE_CBC,iv=iv)
    res = ci.decrypt(wanted)
    return res
wanted = bytes.fromhex("1f 93 c5 33 27 f9 2a d7 91 98 af 2e f5 97 ca 4a")
wanted = solve_part3(wanted)
# wanted = bytes.fromhex("60 DF E9 68 4A C9 73 F2 59 D8 AF 2E 52 D1 93 12 E7 66 C7 46 55 D4 3F BE 80 FF 5F DE E4 63 8A 09")
 
wanted = solve_part2(wanted)
# wanted = bytes.fromhex("D4 C1 95 EE 99 43 3A 33 DF 55 A4 E2 F1 FC D6 A3 31 CD 01 AB 9F 50 D4 2E 2D 94 B7 52 11 18 E2 6E")
flag = solve_part1(wanted)
 
print(flag)

解第二部分:

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
from hashlib import md5
 
def recover_matrix():
    A = [[0 for i in range(16)] for j in range(16)]
    for q in range(16):
        input_data = [0 for _ in range(16)]
        input_data[q] = 1
        N = 0xFF8F
        g_vals = [0xe9,0x88,0xbd,0x84,0x9d,0x64,0xc4,0xb9,0x8a,0xde,0x5a,0x65,0x73,0xe5,0xa1,0x61]
 
        res_vals = []
        for i,v in enumerate(g_vals):
            tmp = [pow(v,i,N) for i in range(15,-1,-1)]
            tmp.append(input_data[i])
            res_vals.append(tmp)
 
        for i in range(15):
            c1 = res_vals[i][i]
            for j in range(i+1,16):
                c2 = res_vals[j][i]
                for k in range(i,17):
                    res_vals[j][k] = (c1*res_vals[j][k] - c2*res_vals[i][k])%N
 
        for j in range(16):
            A[j][q] = res_vals[j][16]
    return A
 
def recover_delta(wanted):
    assert(len(wanted)==16)
    res = [0 for _ in range(16)]
    input_data = [0 for _ in range(16)]
    N = 0xFF8F
    g_vals = [0xe9,0x88,0xbd,0x84,0x9d,0x64,0xc4,0xb9,0x8a,0xde,0x5a,0x65,0x73,0xe5,0xa1,0x61]
 
    res_vals = []
    for i,v in enumerate(g_vals):
        tmp = [pow(v,i,N) for i in range(15,-1,-1)]
        tmp.append(input_data[i])
        res_vals.append(tmp)
 
    for i in range(15):
        c1 = res_vals[i][i]
        for j in range(i+1,16):
            c2 = res_vals[j][i]
            for k in range(i,17):
                res_vals[j][k] = (c1*res_vals[j][k] - c2*res_vals[i][k])%N
 
    for i in range(14,-1,-1):
        d = 0
        for j in range(15-i):
            d += wanted[i+j+1]*res_vals[i][i+j+1]
        res[i] = d
    return res
def recover_const_a():
    input_data = [0 for _ in range(16)]
    N = 0xFF8F
    g_vals = [0xe9,0x88,0xbd,0x84,0x9d,0x64,0xc4,0xb9,0x8a,0xde,0x5a,0x65,0x73,0xe5,0xa1,0x61]
 
    res_vals = []
    for i,v in enumerate(g_vals):
        tmp = [pow(v,i,N) for i in range(15,-1,-1)]
        tmp.append(input_data[i])
        res_vals.append(tmp)
 
    for i in range(15):
        c1 = res_vals[i][i]
        for j in range(i+1,16):
            c2 = res_vals[j][i]
            for k in range(i,17):
                res_vals[j][k] = (c1*res_vals[j][k] - c2*res_vals[i][k])%N
    return [res_vals[i][i] for i in range(16)]
 
def solve_linear(wanted):
    N = 0xff8f
    k = 0x7fc6
    const_as = recover_const_a()
    A = Matrix(GF(N),recover_matrix())
    delta = recover_delta(wanted)
    y = []
    for i in range(16):
        a = int(power_mod(const_as[i],2*k+1,N))
        # print(hex(a),hex(N))
        q = inverse_mod(a,N)
        y.append(q*wanted[i]+delta[i])
    _A = A.inverse()
    y = vector(GF(N),y)
    x = _A*y
    res = [hex(v)[4:6]+hex(v)[2:4] for v in x]
    return ''.join(res)
 
def rol(v,n):
    a = v<<n
    b = v>>(8-n)
    return (a|b)&0xff
 
def solve_hash(name):
    res = []
    h = md5(name).digest()[8:16]
    h = md5(h).digest()
    expected = bytes.fromhex("15 09 0C 1F 1C 13 16 19 1D 03 10 0F 01 02 1E 06")
    v_add = [0x03,0xee,0xec,0x11,0x14,0x0e,0x05,0x0c,0x03,0xed,0xf7,0x05,0x00,0xf6,0x00,0xe7]
    v_xor = [0x15,0x17,0x16,0x14,0x13,0x0c,0x04,0x12,0x03,0x06,0x10,0x0e,0x0a,0x18,0x1c,0x0f]
    v_ror = [0x1a,0x1c,0x11,0x18,0x08,0x0c,0x19,0x01,0x20,0x0b,0x07,0x10,0x17,0x02,0x1d,0x15]
    for i in range(16):
        tmp = (expected[i]-v_add[i]+h[i])&0xff
        res.append(rol(tmp,v_ror[i]&7)^^v_xor[i])
    return bytes(res)
 
def split_64bits(n):
    res = [(n>>(63-i))&1 for i in range(64)]
    return res
 
def solve_encode(wanted):
    lookup = {1: '6d1052', 10: '48c68b', 8: '653454', 2: '6d34c2', 14: '2538d2', 0: '89a28b', 7: '2d14d0', 4: '6614d2', 13: '251462', 6: '49a709', 9: '48a21b', 12: '41a69b', 3: '50a289', 5: '498299', 11: '2c3452', 15: '08a699'}
    v = [int(wanted[16*i:16*i+16],16) for i in range(4)]
    v = [split_64bits(x) for x in v]
    keys = [8*v[0][i]+4*v[1][i]+2*v[2][i]+v[3][i] for i in range(64)]
    flag_part2 = ''.join([lookup[k] for k in keys])
    return flag_part2
 
# A = recover_matrix()
# A = Matrix(GF(0xff8f),A)
# input_val = bytes.fromhex("6B 28 6A 2E 96 20 0B 7E 01 5B 67 A1 21 27 34 D9 55 41 65 16 33 87 B6 AE 80 48 8D 39 D3 EA 6D 49")
# x = [input_val[2*i]+256*input_val[2*i+1] for i in range(16)]
# x = vector(GF(0xff8f),x)
# y = A*x
# print([hex(v) for v in y])
# wanted = bytes.fromhex("30 AC 57 28 63 E4 F4 9B D9 C9 BB 41 BC E8 55 BD")
username = b'KCTF'
wanted = solve_hash(username)
# print([hex(x) for x in wanted])
wanted = solve_linear(wanted)
# print(wanted)
flag_part2 = solve_encode(wanted)
print(flag_part2)

看雪侠者千人榜,看看你上榜了吗?

最后于 2020-12-7 10:40 被srp8ve7ou2编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回