-
-
[原创]2020KCTF秋季赛第八题wp
-
2020-12-6 22:12 5975
-
分析:
做题过程一波三折,还是直接写结论吧
- 固定模式的花指令,有些花指令涉及字节码的复用,挺有新意
- 反调试,检测了进程名和调试器
- 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) |
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。
最后于 2020-12-7 10:40
被afa9040e编辑
,原因:
赞赏
他的文章
看原图