--- tags: re, ctf --- # ISITDTU FINAL 2021 - RE ## Protector Chall này cùng ý tưởng với một bài mình từng làm ơ SVATTT năm nay. Đây là wu của mình cho bài đó: [SVATTT 2021 - UNLOCK (re) - HackMD](https://hackmd.io/@lanleft/svattt21-unlock) Reverse các opcode trong hàm `VM` thì mình có được script sau: ```python registers = ['TESTTT', 'EAX', 'EBX', 'ECX', 'EDX', 'ESI', 'EDI', 'ESP', 'EIP', 'EBP'] instructions = [ [0x00, 2, "MOV {}, {}"], # mov eax, ebx [0x01, 2, "MOV {}, {}"], # MOV EAX, 0X20 [0x02, 2, "XOR {}, {}"], # xor byte1, byte2 [0x03, 2, "SUB {}, {}"], [4, 2, "ADD {}, {}"], [5, 1, "PUSH {}"], # PUSH EAX [6, 1, "POP {}"], # POP EAX [7, 3, "MOV {}, BYTE PTR [{}+{}]"], # MOV BYTE1, [BYTE2+BYTE3] [8, 1, "JMP {}"], # JMP CURR+BYTE1 [9, 1, "JNZ {}"], # FLAGS 0X40 [10, 2, "CMP {}, {}"], # CMP AND SET FLAGS 0X40 [11, 2, "AND {}, {}"], [12, 2, "SHL {}, {}"], # SHF EAX, EBX [13, 2, "SHR {}, {}"], # SHR EAX, EBX [14, 2, "CALL {}"], # CALL EAX [15, 1, "JMP {}"], # JMP EAX [16, 3, "MOV {}, DWORD PTR [{}+{}]"], # MOV BYTE1, [BYTE2+BYTE3] [17, 1, "JZ {}"] ] def main(): with open("out.bin", "rb") as vm: opcodes = vm.read() vm.close() opcodes = opcodes[5:] pc = 0 asm_text = "" value = [[] for _ in range(18)] bitmath = [[] for _ in range(18)] idx = 0 pre_text = "" while pc < 0x1dc3: ins = instructions[opcodes[pc]] # print (ins) # text = hex(pc) + ":\t" text = "" if ins[1]==1: if ins[0]==8 or ins[0]==9 or ins[0]==17: text += ins[2].format(hex(pc -1 + int(opcodes[pc+1]))) else: text += ins[2].format(registers[int(opcodes[pc+1])]) # asm_text.append(text) pc += 2 elif ins[1]==2: if int(opcodes[pc+1]) > 9 : print (int(opcodes[pc+1]) ) text += "CORRUPTED {} {} {}".format(int(opcodes[pc]), int(opcodes[pc+1]), int(opcodes[pc+2])) if ins[0]==1: text += ins[2].format(registers[int(opcodes[pc+1])], hex(int(opcodes[pc+2]))) elif int(opcodes[pc+1]) <= 9: text += ins[2].format(registers[int(opcodes[pc+1])], registers[int(opcodes[pc+2])]) pc += 3 elif ins[1]==3: text += ins[2].format(registers[int(opcodes[pc+1])], registers[int(opcodes[pc+2])], registers[int(opcodes[pc+3])]) pc += 4 text += "\n" # print (text) asm_text += text print (asm_text) # print (asm_text) if __name__=="__main__": main() ``` Đoạn này sẽ dump ra được code ASM Revere tương tự như wu trên thì mình có được password *Note* Trong quá trình làm challenge này thì bạn teammate có chỉ cho mình một tools để emulate ASM khá là hay, đó là: [Assembly x86 Emulator](https://carlosrafaelgn.com.br/Asm86/) ## YAG Transform Chall này cho một binary khá là ngắn, và tên hàm đều không bị strip. Nhưng điều đáng buồn là file được compile trên MAC arm64, vậy nên chúng mình hoàn toàn không thể debug được. Đầu tiên, đi từ flow của hàm `main` ```c bzero(enc_pt, 0x1000uLL); memset(arr_int_secret, 0, sizeof(arr_int_secret)); fp_pt = fopen("plaintext.txt", "r"); if ( !fp_pt ) { stdout_ = "No plaintext.txt for you!"; LABEL_33: puts(stdout_); return 1; } fp_pt_ = fp_pt; idx0 = 0LL; idx1 = 1LL; do { one_char = fgetc(fp_pt_); enc_pt[idx0] = (109330345517422uLL >> ((unsigned __int8)idx0 - 47 * (unsigned __int8)(idx1 / 47) + 1)) ^ one_char ^ fib(idx0); putchar('.'); fflush((FILE *)&unk_100000CFE); ++idx0; ++idx1; } while ( idx0 != 4096 ); ``` Đoạn này chương trình đọc dữ liệu từ file `plaintext.txt` và thực hiện `encrypt` (xor) với key là `cothan` Tiếp theo: ```c fp_secret = fopen("secret.txt", "r"); if ( !fp_secret ) { stdout_ = "No secret.txt for you!"; goto LABEL_33; } fp_secret_ = fp_secret; for ( i = 0LL; i != 32; ++i ) fscanf(fp_secret_, "%d,", &arr_int_secret[i]); fclose(fp_secret_); puts( "There are 2 types of code reverser, the brave reverses everything and fall for traps, the coward only reverses what needed"); idx2 = 0LL; while ( 2 ) { value_int = arr_int_secret[idx2]; if ( value_int > 29 ) return 1; if ( idx2 <= 4 && (unsigned __int8)arr_int_secret[idx2] % 5u ) return 2; switch ( (int)idx2 ) { case 16: if ( value_int == 13 ) goto LABEL_21; result = 3; break; case 17: if ( value_int == 11 ) goto LABEL_21; result = 4; break; case 18: if ( value_int == 25 ) goto LABEL_21; result = 5; break; case 19: if ( value_int == 16 ) goto LABEL_21; result = 6; break; case 20: if ( value_int == 28 ) goto LABEL_21; result = 7; break; default: LABEL_21: if ( ++idx2 != 32 ) ``` Chương trình đọc 32 byte từ file `secret.txt` và lưu vào một mảng `int` - Check 1: tất cả 32 bytes đều <29 - check 2: các bytes từ 0→4 phải chia hết cho 5 - check3: secret[16] = 13, secret[17] = 11 , secret[18] = 25, secret[19] = 16, secret[20] = 28. Sau đó: ```c idx3 = 0LL; idx4 = 0uLL; do { idx4 = vaddq_s32(*(int32x4_t *)&arr_int_secret[idx3], idx4); idx3 += 4LL; } while ( idx3 != 16 ); if ( vaddvq_s32(idx4) != 251 ) return 1; idx5 = 0LL; idx6 = 0uLL; do { idx6 = vaddq_s32(*(int32x4_t *)&arr_int_secret[idx5 + 16], idx6); idx5 += 4LL; } while ( idx5 != 16 ); if ( vaddvq_s32(idx6) != 263 ) return 1; ``` Chương trình thực hiện cộng mỗi nửa của `secret` và so sánh với 1 số là 251 và 263 Cuối cùng: ```c idx7 = 0LL; idx8 = 0; do { transpose(&enc_pt[idx8], arr_int_secret[idx7], arr_int_secret[idx7 + 16]); idx8 += arr_int_secret[idx7 + 16] * arr_int_secret[idx7]; ++idx7; } while ( idx7 != 16 ); fp_ct = fopen("out.enc", "w"); fwrite(enc_pt, 1uLL, 0x1000uLL, fp_ct); fclose(fp_ct); result = 0; ``` Chương trình thực hiện `transpose` để thay đổi dữ liệu của `plaintext` vừa bị encrypt ở trên sau đó ghi vào file `out.enc` Chúng ta có file `out.enc` và sẽ phải đi ngược lại tìm `plaintext.txt` Sau khi đọc đống dữ liệu trên mình đã liên tưởng đến z3, nhưng z3 trả về một số lượng lớn các nghiệm và nó khiến mình rất hoang mang. Script z3 của mình: ```python from z3 import * s = Solver() secret = [BitVec("secret%s" % i, 8) for i in range(32)] for i in range(32): s.add(secret[i]<29) s.add(secret[i]>0) s.add(secret[16] == 13) s.add(secret[17] == 11) s.add(secret[18] == 25) s.add(secret[19] == 16) s.add(secret[20] == 28 ) x = 0 for i in range(16): x += secret[i] s.add(x == 251) x = 0 for i in range(16, 32): x += secret[i] s.add(x == 263) a = 0 for i in range(16): a += secret[i]*secret[i+16] s.add(a==4096) idx2 = 0 while (True): if s.check() == sat: m = s.model() answer = [] for i in secret: answer.append(m[i].as_long()) # print("ANSWER {}: {}".format(idx2, answer)) print (answer) # print (idx2) idx2 += 1 # secret_value = answer[6] # s.add(secret[6] != secret_value) else: print (unsat) break ``` Cuối cùng chúng mình đã quyết định bruteforce `secret` Sau khi reverse hàm `transpose` mình nhận thấy hàm thực hiện chức năng giống transpose matrix với size là 2 tham số được truyền vào ngay sau (enc, row, column) Script để brutefoce của mình: ```python import numpy as np fib = [0 for i in range(4096)] fib[0] = 0 fib[1] = 1 for i in range(2, 4096): fib[i] = (fib[i-1] + fib[i-2]) & 0xff with open("./out.enc", "rb") as fp: enc_data = list(fp.read()) fp.close() def reverse(begin, row, column): arr = enc_data[begin:begin+row*column] # print (arr) B = np.array(arr).reshape(row, column) # print (B) C = np.transpose(B) # print (C) out = np.array(C).reshape(1, row*column) # (row, column) # print (out) #### part2 # print ("-----------------------------------------------") idx = begin out2 = [] const = 0x636F7468616E while (idx < begin+row*column): tmp_idx = (idx%47+1) # if tmp_idx == 47: # tmp1 = const >> # else: tmp1 = const >> tmp_idx # print (idx%47+1) tmp = tmp1^fib[idx]^out[0][idx-begin] out2.append(tmp&0xff) idx += 1 # print (out2) print (bytearray(out2)) with open("out_test.bin", "wb") as ot: ot.write(bytearray(out2)) ot.close() ''' --> brute 4 pairs of secretkeys reverse(0, 5, 13) reverse(5*13, 20, 11) curr = 5*13+20*11 reverse(curr, 25, 25) ################################# curr += 25*25 reverse(curr, 20, 16) ################################# curr += 20*16 reverse(curr, 25, 28) # ''' test[6], test[6+16] = 19, 27 test[7], test[7+16] = 24, 7 test[8], test[8+16] = 11, 28 test[9], test[9+16] = 11, 20 test[10], test[10+16] = 10, 20 for ii in range(29): test[11] = ii for j in range(29): test[11+16] = j curr = 0 print("********************** TRY TEST {} ******************".format(idx_test)) for i in range(16): print("KEY: {} {}".format(test[i], test[i+16])) try: reverse(curr, test[i], test[i+16]) curr += test[i]*test[i+16] except: print("FAILED: " + str(idx_test)) break print("********************** END TEST {} ******************".format(idx_test)) idx_test += 1 # ISITDTU{The_algorithm_is_inplace_matrix_transpose_do_you_know_that?} ``` Chúng mình nhận ra bài này là một bài trên wiki: [The Toyota Way | Wikiwand](https://www.wikiwand.com/en/The_Toyota_Way) Vừa bruteforce vừa kết hợp với wiki, với chỉ cần 8 cặp key là mình có thể lấy đc flag nên ko tính là lâu lắm :v: <style> html, body, .ui-content { background-color: #333; color: #ddd; } .markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { color: #ddd; } .markdown-body h1, .markdown-body h2 { border-bottom-color: #ffffff69; } .markdown-body h1 .octicon-link, .markdown-body h2 .octicon-link, .markdown-body h3 .octicon-link, .markdown-body h4 .octicon-link, .markdown-body h5 .octicon-link, .markdown-body h6 .octicon-link { color: #fff; } .markdown-body img { background-color: transparent; } .ui-toc-dropdown .nav>.active:focus>a, .ui-toc-dropdown .nav>.active:hover>a, .ui-toc-dropdown .nav>.active>a { color: white; border-left: 2px solid white; } .expand-toggle:hover, .expand-toggle:focus, .back-to-top:hover, .back-to-top:focus, .go-to-bottom:hover, .go-to-bottom:focus { color: white; } .ui-toc-dropdown { background-color: #333; } .ui-toc-label.btn { background-color: #191919; color: white; } .ui-toc-dropdown .nav>li>a:focus, .ui-toc-dropdown .nav>li>a:hover { color: white; border-left: 1px solid white; } .markdown-body blockquote { color: #bcbcbc; } .markdown-body table tr { background-color: #5f5f5f; } .markdown-body table tr:nth-child(2n) { background-color: #4f4f4f; } .markdown-body code, .markdown-body tt { color: #eee; background-color: rgba(230, 230, 230, 0.36); } a, .open-files-container li.selected a { color: #5EB7E0; } </style>