# Dreamhack (only reverse) Đây là writeup khi mình mới chập chững học reverse. Update ngày 23/12/2024: Hơn 2 tuần rồi mình chưa update gì, mình bắt đầu viết blog này từ giữa tháng 11. Mình vẫn còn làm trên [đấy](https://dreamhack.io/users/62830/), nhưng sẽ không update nữa. ## ptrace_block Link chall: https://dreamhack.io/wargame/challenges/1197 Đầu tiên mình check 7749 thứ, tới khi check strings thì được như sau: ``` > strings prob /lib64/ld-linux-x86-64.so.2 __gmon_start__ _ITM_deregisterTMCloneTable _ITM_registerTMCloneTable AES_set_encrypt_key AES_cbc_encrypt __cxa_finalize write __libc_start_main ptrace srand puts close open __isoc99_scanf __stack_chk_fail printf time libcrypto.so.3 libc.so.6 OPENSSL_3.0.0 GLIBC_2.34 GLIBC_2.7 GLIBC_2.4 GLIBC_2.2.5 PTE1 u+UH generate your flag! %255s ./out.txt :*3$" GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 .shstrtab .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt.got .plt.sec .text .fini .rodata .eh_frame_hdr .eh_frame .init_array .fini_array .dynamic .data .bss .comment ``` Mình thấy có AES_CBC, vậy thứ mình cần tìm ở bài này là key và iv, đặc biệt chú ý đến .init_array. Hiểu đơn giản ngắn gọn là nó chứa các hàm được chạy trước hàm main. Mở IDA lên check hàm main. (Nhớ sửa code cho dễ đọc trước nhé) ```c __int64 __fastcall main(int a1, char **a2, char **a3) { int fd; // [rsp+Ch] [rbp-514h] __int64 input[32]; // [rsp+10h] [rbp-510h] BYREF char buf[1032]; // [rsp+110h] [rbp-410h] BYREF unsigned __int64 v7; // [rsp+518h] [rbp-8h] v7 = __readfsqword(0x28u); memset(input, 0, sizeof(input)); puts("generate your flag!"); printf("> "); __isoc99_scanf("%255s", input); encrypt((__int64)input, (__int64)buf, 256); fd = open("./out.txt", 1); write(fd, buf, 0x100uLL); close(fd); return 0LL; } ``` Hàm encrypt: ```c __int64 __fastcall encrypt(__int64 input, __int64 output, int size) { char key[256]; // [rsp+20h] [rbp-120h] BYREF __int64 iv[4]; // [rsp+120h] [rbp-20h] BYREF iv[3] = __readfsqword(0x28u); iv[0] = 0LL; iv[1] = 0LL; AES_set_encrypt_key(to_key, 128LL, key); AES_cbc_encrypt(input, output, size, key, iv, 1LL); return 0LL; } ``` Ta thấy có một mảng được gán bằng key, và iv được gán bằng null hết. Ta check mảng gán cho key: ``` to_key db 41h, 28h, 19h, 4Eh, 0A5h, 7Ch, 0A1h, 41h, 13h, 0CFh .data:0000000000004010 ; DATA XREF: init_func1+83↑o .data:0000000000004010 ; init_func1+93↑o ... .data:000000000000401A db 88h, 0ACh, 2Ah, 0F0h, 0B7h, 0DAh ``` Đến đây ta được mảng như sau ``` 41 28 19 4E A5 7C A1 41 13 CF 88 AC 2A F0 B7 DA ``` Thử bỏ vào python decrypt luôn thì thấy hơi sai sai... Mình từng làm một bài trick lỏ overwrite .fini_array cho nó lặp vô tận rồi nên mình biết ngay là vấn đề nằm ở .init_array Có hai hàm đáng chú ý ở phần .init_array (ở đây mình đổi tên hàm rồi): ```c void init_func1() { unsigned int v0; // eax int v1; // ebx int v2; // [rsp+4h] [rbp-1Ch] int i; // [rsp+8h] [rbp-18h] int j; // [rsp+Ch] [rbp-14h] v0 = time(0LL); srand(v0); v2 = 1; for ( i = 0; i <= 4095; ++i ) { v1 = ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL); v2 *= v1 * rand(); } for ( j = 0; j <= 14; ++j ) to_key[j + 1] += to_key[j] + v2; } ``` ```c++ int init_func2() { int v0; // eax int result; // eax int i; // [rsp+8h] [rbp-8h] char v3; // [rsp+Ch] [rbp-4h] v0 = rand(); srand(v0); result = rand(); v3 = result; for ( i = 0; i <= 15; ++i ) { result = i; to_key[i] ^= v3; } return result; } ``` Hướng đầu tiên mọi người tiếp cận có vẻ là chạy debug để lấy ra key đúng, nhưng khi có ptrace thì mọi thứ có vẻ không như vậy. Mình hiểu là nếu như 1 process đang bị debug bởi debugger, thì debugger sẽ gọi ptrace tới process đó => process bị trace và ptrace(PTRACE_TRACEME, 0, 0, 0) sẽ return khác 0 (gửi lời cảm ơn đến anh d4rkn19ht vì đoạn này em từng đọc được trên github của anh) . Vậy mình rev chay đoạn này hết thôi, có thêm vấn đề nữa là hàm rand(). Ở đây vì ta không chạy debug nên ta biết luôn giá trị ptrace = 0, hàm init_func1() rev khá dễ. Đến hàm thứ 2 thì có rand() giống như hàm trên, nhưng giá trị nó chỉ nằm trong khoảng char nên cách hiệu quả nhất là Brute Force chứ còn gì nữa :D ```python #!/usr/bin/python3 from Crypto.Cipher import AES key = bytearray(bytes.fromhex('41 28 19 4E A5 7C A1 41 13 CF 88 AC 2A F0 B7 DA')) for i in range(0, len(key) - 1): key[i + 1] = (key[i + 1] + key[i]) % 256 iv = b'\x00' * 16 out = open("ans.txt", "rb").read() for i in range(256): new_key = key for j in range(len(new_key)): new_key[j] ^= i cipher = AES.new(new_key, AES.MODE_CBC, iv) x = cipher.decrypt(out) if b'DH' in x: print(x) ``` Đến đây thì ra flag rồi đó, bài đơn giản đúng không nào :D ## My_First_Game_v0.1 Link chall: https://dreamhack.io/wargame/challenges/1237 - Mình rev windows khá yếu (vì mình mới chơi rev gần đây :D), bài này thuộc dạng game hacking. Hướng đầu tiên của mình là mở IDA lên đọc code và sẽ cố patch sao cho nó di chuyển cái trục kia sang flag, nhưng mò đủ kiểu vẫn không được (phần này khá ngốn time của mình). - Trùng hợp là hôm nay toàn làm mấy bài game hacking. Nên mình sẽ tìm tool nào đó để hack game, ở Windows thì có Cheat Engine. Trước giờ mình chưa xài bao giờ, nhưng mình thích học những thứ mới :D. - Hướng đi bài này sẽ là search tìm giá trị của mấy cái vị trí đó trên memory, rồi dùng Cheat Engine thay đổi nó để đọc được flag. - Sau một khoảng thời gian tìm thì mình tìm được giá trị nó ở đây: ![Screenshot 2024-11-15 124511](https://hackmd.io/_uploads/Syz2XPEMyx.png) - Rồi chỉnh giá trị để ta đọc được flag thôi, ta biết 4 bytes đầu là X còn 4 bytes sau là giá trị Y. ![image](https://hackmd.io/_uploads/rJVnEP4Gyx.png) ![image](https://hackmd.io/_uploads/HkSGHvEGJl.png) ![image](https://hackmd.io/_uploads/rkKUHv4fye.png) Ghép lại là có flag thui :D Bài này làm t đau mắt vcl hic =)))) ## Casino-777 Link chal: https://dreamhack.io/wargame/challenges/143 - Bài này mới đọc vào mình thấy random thì thấy hơi rén nhẹ rồi, nhưng phải bình tĩnh phân tích thôi :))) - Mở IDA lên thì ta chú ý 2 hàm này, như mọi bài thì đổi tên biến cho dễ đọc. ``` c int choice_1() { __int64 v0; // rbx __int64 v1; // rbx char v3; // [rsp+Fh] [rbp-21h] int i; // [rsp+10h] [rbp-20h] int v5; // [rsp+14h] [rbp-1Ch] int j; // [rsp+18h] [rbp-18h] int size; // [rsp+1Ch] [rbp-14h] for ( i = 0; i <= 9; ++i ) { *((_QWORD *)&gen_arr + i) = malloc(0x10uLL); size = *(_DWORD *)&some_arr[4 * i] + 1; **((_DWORD **)&gen_arr + i) = size; v0 = *((_QWORD *)&gen_arr + i); *(_QWORD *)(v0 + 8) = malloc(size); v5 = 0; for ( j = 0; j < size; ++j ) { v3 = alphabet[(unsigned __int8)random_byte() % 0x5Eu]; *(_BYTE *)(j + *(_QWORD *)(*((_QWORD *)&gen_arr + i) + 8LL)) = v3; if ( v3 == 55 ) v5 = 1; } if ( !v5 ) { v1 = *(_QWORD *)(*((_QWORD *)&gen_arr + i) + 8LL); *(_BYTE *)(v1 + (unsigned __int8)random_byte() % size) = 55; } } slot_check = 1; return puts("slot generated !"); } ``` ``` c unsigned __int64 choice_2() { int i; // [rsp+Ch] [rbp-134h] int j; // [rsp+10h] [rbp-130h] int v3; // [rsp+14h] [rbp-12Ch] int k; // [rsp+18h] [rbp-128h] int fd; // [rsp+1Ch] [rbp-124h] int size; // [rsp+20h] [rbp-120h] int num_rotate; // [rsp+24h] [rbp-11Ch] unsigned __int64 num_inp; // [rsp+28h] [rbp-118h] BYREF __int64 src; // [rsp+30h] [rbp-110h] BYREF __int64 v10; // [rsp+38h] [rbp-108h] __int64 v11; // [rsp+40h] [rbp-100h] __int64 v12; // [rsp+48h] [rbp-F8h] __int64 v13; // [rsp+50h] [rbp-F0h] __int64 v14; // [rsp+58h] [rbp-E8h] __int64 v15; // [rsp+60h] [rbp-E0h] __int64 v16; // [rsp+68h] [rbp-D8h] __int64 v17; // [rsp+70h] [rbp-D0h] __int64 v18; // [rsp+78h] [rbp-C8h] __int64 v19; // [rsp+80h] [rbp-C0h] __int64 v20; // [rsp+88h] [rbp-B8h] __int64 v21; // [rsp+90h] [rbp-B0h] __int64 v22; // [rsp+98h] [rbp-A8h] __int64 v23; // [rsp+A0h] [rbp-A0h] __int64 v24; // [rsp+A8h] [rbp-98h] __int64 v25; // [rsp+B0h] [rbp-90h] __int64 v26; // [rsp+B8h] [rbp-88h] __int64 v27; // [rsp+C0h] [rbp-80h] __int64 v28; // [rsp+C8h] [rbp-78h] __int64 v29; // [rsp+D0h] [rbp-70h] __int64 v30; // [rsp+D8h] [rbp-68h] __int64 v31; // [rsp+E0h] [rbp-60h] __int64 v32; // [rsp+E8h] [rbp-58h] __int64 v33; // [rsp+F0h] [rbp-50h] __int64 v34; // [rsp+F8h] [rbp-48h] __int64 v35; // [rsp+100h] [rbp-40h] __int64 v36; // [rsp+108h] [rbp-38h] __int64 v37; // [rsp+110h] [rbp-30h] __int64 v38; // [rsp+118h] [rbp-28h] __int64 v39; // [rsp+120h] [rbp-20h] __int64 v40; // [rsp+128h] [rbp-18h] unsigned __int64 v41; // [rsp+138h] [rbp-8h] v41 = __readfsqword(0x28u); num_inp = 0LL; if ( slot_check ) { ++slot_check; puts("How many rotate the slots ?"); printf("> "); __isoc99_scanf("%lld", &num_inp); for ( i = 0; i <= 9; ++i ) { size = **(_DWORD **)&gen_arr[8 * i]; num_rotate = num_inp % size; src = 0LL; v10 = 0LL; v11 = 0LL; v12 = 0LL; v13 = 0LL; v14 = 0LL; v15 = 0LL; v16 = 0LL; v17 = 0LL; v18 = 0LL; v19 = 0LL; v20 = 0LL; v21 = 0LL; v22 = 0LL; v23 = 0LL; v24 = 0LL; v25 = 0LL; v26 = 0LL; v27 = 0LL; v28 = 0LL; v29 = 0LL; v30 = 0LL; v31 = 0LL; v32 = 0LL; v33 = 0LL; v34 = 0LL; v35 = 0LL; v36 = 0LL; v37 = 0LL; v38 = 0LL; v39 = 0LL; v40 = 0LL; for ( j = 0; j < size; ++j ) *((_BYTE *)&src + (j + num_rotate) % size) = *(_BYTE *)(*(_QWORD *)(*(_QWORD *)&gen_arr[8 * i] + 8LL) + j); memcpy(*(void **)(*(_QWORD *)&gen_arr[8 * i] + 8LL), &src, size); } v3 = 1; printf("Result: "); for ( k = 0; k <= 9; ++k ) { printf("%c ", (unsigned int)**(char **)(*(_QWORD *)&gen_arr[8 * k] + 8LL)); if ( **(_BYTE **)(*(_QWORD *)&gen_arr[8 * k] + 8LL) != '7' ) v3 = 0; } putchar(10); if ( v3 ) { puts("Jackpot ! Congratuations !!"); fd = open("flag", 0); src = 0LL; v10 = 0LL; v11 = 0LL; v12 = 0LL; v13 = 0LL; v14 = 0LL; v15 = 0LL; v16 = 0LL; v17 = 0LL; v18 = 0LL; v19 = 0LL; v20 = 0LL; v21 = 0LL; v22 = 0LL; v23 = 0LL; v24 = 0LL; v25 = 0LL; v26 = 0LL; v27 = 0LL; v28 = 0LL; v29 = 0LL; v30 = 0LL; v31 = 0LL; v32 = 0LL; v33 = 0LL; v34 = 0LL; v35 = 0LL; v36 = 0LL; v37 = 0LL; v38 = 0LL; v39 = 0LL; v40 = 0LL; read(fd, &src, 0x100uLL); puts((const char *)&src); } } else { puts("Generate the slots first !"); } return __readfsqword(0x28u) ^ v41; } ``` - Tóm lại flow chương trình là: gen 10 dãy số giá trị random, ta cần rotate sao cho mỗi phần tử đầu tiên của 10 dãy số này là 7 hết thì nó sẽ nhả flag ra. Biết rằng mỗi dãy đều có ít nhất 1 phần tử là số 7. - Ở đây thì ta thấy phép mod, cộng với việc biết trước size của 10 dãy số đó và phần tử được in ra sau mỗi lần rotate xong. - Mình có thuật toán như sau: Mình sẽ cố gắng giữ nhiều số 7 nhất, vì các tính chất ở trên nên ta hoàn toàn có thể giữ số 7 sau mỗi lần rotate bằng cách cho số lần rotate chia hết cho size của dãy số ta cần giữ lại số 7. Tóm lại là: giữ các số 7, random ra số 7 tiếp theo, rồi lại giữ nó, rồi lại tiếp tục như vậy. - Script bài này của mình (ủa rồi là rev dữ chưa ???): ``` python #!/usr/bin/python3 from math import lcm s = bytearray(bytes.fromhex("48 00 00 00 52 00 00 00 58 00 00 00 60 00 00 00 66 00 00 00 4E 00 00 00 64 00 00 00 7E 00 00 00 82 00 00 00 88 00 00 00")) size_arr = [] for i in range(0, len(s), 4): size_arr.append(s[i] + 1) print(size_arr) from pwn import * context.log_level = "debug" p = remote('host3.dreamhack.games', 20004) # p = process('./casino_777') p.sendlineafter(b'> ', b'1') num7 = 0 rotate = 1 while num7 != 10: if num7 == 0: rotate += 1 p.sendlineafter(b'> ', b'2') p.sendlineafter(b'> ', str(rotate)) arr = p.recvline()[8:-1].decode().split(" ") new_num7 = 0 new_rotate = 1 for i in range(10): if arr[i] == '7': new_num7 += 1 new_rotate *= size_arr[i] if new_num7 > num7: num7 = new_num7 rotate = new_rotate log.info(arr) p.interactive() ``` ## baseball Sau khi dành hơn 1 tuần bỏ CTF để ôn giữa kì thì mình đã trở lại :D mai thi nhập môn mạch số mà thôi kệ nhớ CTF quá làm luôn =))) Link chall: https://dreamhack.io/wargame/challenges/105 Bài này khá đơn giản, bỏ tí thời gian ra đọc là hiểu. Tốn thời gian implement hơn là đọc code. Ta chỉ cần quan tâm duy nhất hàm này trong chương trình (mình đã đổi tên biến cho dễ đọc) ``` c _BYTE *__fastcall sub_1289(_BYTE *input, int input_size) { _BYTE *v3; // rax _BYTE *idx_0; // rax _BYTE *v5; // rax _BYTE *v6; // rax _BYTE *v7; // rax _BYTE *output_idx; // [rsp+18h] [rbp-28h] _BYTE *idx_1; // [rsp+18h] [rbp-28h] _BYTE *v10; // [rsp+18h] [rbp-28h] _BYTE *inp_idx; // [rsp+20h] [rbp-20h] unsigned __int64 size; // [rsp+28h] [rbp-18h] _BYTE *v13; // [rsp+30h] [rbp-10h] _BYTE *v14; // [rsp+38h] [rbp-8h] size = (4 * input_size / 3 + 4) / 0x48uLL + 4 * input_size / 3 + 4 + 1; if ( size < input_size ) return 0LL; v13 = malloc(size); if ( !v13 ) return 0LL; v14 = &input[input_size]; inp_idx = input; output_idx = v13; while ( v14 - inp_idx > 2 ) { *output_idx = table_file[*inp_idx >> 2]; output_idx[1] = table_file[(inp_idx[1] >> 4) | (16 * *inp_idx) & 0x30]; output_idx[2] = table_file[(inp_idx[2] >> 6) | (4 * inp_idx[1]) & 0x3C]; v3 = output_idx + 3; output_idx += 4; *v3 = table_file[inp_idx[2] & 0x3F]; inp_idx += 3; } if ( v14 != inp_idx ) { idx_0 = output_idx; idx_1 = output_idx + 1; *idx_0 = table_file[*inp_idx >> 2]; if ( v14 - inp_idx == 1 ) { *idx_1 = table_file[(16 * *inp_idx) & 0x30]; v5 = idx_1 + 1; v10 = idx_1 + 2; *v5 = '='; } else { *idx_1 = table_file[(inp_idx[1] >> 4) | (16 * *inp_idx) & 0x30]; v6 = idx_1 + 1; v10 = idx_1 + 2; *v6 = table_file[(4 * inp_idx[1]) & 0x3C]; } v7 = v10; output_idx = v10 + 1; *v7 = '='; } *output_idx = 0; return v13; } ``` Tóm tắt một tí về bài này * Nó cho chúng ta text_in.txt, text_out.txt và flag_out.txt * Từ text_in.txt và text_out.txt ta khôi phục được bảng table * Từ bảng table ta reverse để lấy flag Bài này mình dùng z3 để giải, script của mình: ``` python #!/usr/bin/python3 from z3 import * table = [BitVec(f'table_{i}', 8) for i in range(64)] solver = Solver() inp = open("text_in.txt", "rb").read() out = open("text_out.txt", "rb").read() v14 = len(inp) inp_idx = 0 output_idx = 0 # *output_idx = table_file[*inp_idx >> 2]; # output_idx[1] = table_file[(inp_idx[1] >> 4) | (16 * *inp_idx) & 0x30]; # output_idx[2] = table_file[(inp_idx[2] >> 6) | (4 * inp_idx[1]) & 0x3C]; # v3 = output_idx + 3; # output_idx += 4; # *v3 = table_file[inp_idx[2] & 0x3F]; # inp_idx += 3; while v14 - inp_idx > 2: solver.add(out[output_idx] == table[inp[inp_idx] >> 2]) solver.add(out[output_idx + 1] == table[(inp[inp_idx + 1] >> 4) | (16 * inp[inp_idx]) & 0x30]) solver.add(out[output_idx + 2] == table[(inp[inp_idx + 2] >> 6) | (4 * inp[inp_idx + 1]) & 0x3c]) solver.add(out[output_idx + 3] == table[inp[inp_idx + 2] & 0x3f]) output_idx += 4 inp_idx += 3 # if ( v14 != inp_idx ) # { # idx_0 = output_idx; # idx_1 = output_idx + 1; # *idx_0 = table_file[*inp_idx >> 2]; # if ( v14 - inp_idx == 1 ) # { # *idx_1 = table_file[(16 * *inp_idx) & 0x30]; # idx_2 = idx_1 + 1; # idx_3 = idx_1 + 2; # *idx_2 = '='; # } # else # { # *idx_1 = table_file[(inp_idx[1] >> 4) | (16 * *inp_idx) & 0x30]; # v6 = idx_1 + 1; # idx_3 = idx_1 + 2; # *v6 = table_file[(4 * inp_idx[1]) & 0x3C]; # } # v7 = idx_3; # output_idx = idx_3 + 1; # *v7 = '='; # } # *output_idx = 0; if v14 != inp_idx: idx_0 = output_idx idx_1 = output_idx + 1 solver.add(out[idx_0] == table[inp[inp_idx] >> 2]) if v14 - inp_idx == 1: solver.add(out[idx_1] == table[(16 * inp[inp_idx]) & 0x30]) idx_2 = idx_1 + 1 idx_3 = idx_1 + 2 else: solver.add(out[idx_1] == table[(inp[inp_idx + 1] >> 4) | (16 * inp[inp_idx]) & 0x30]) v6 = idx_1 + 1 solver.add(out[v6] == table[(4 * inp[inp_idx + 1]) & 0x3c]) s = b'' from pwn import * if solver.check() == sat: m = solver.model() arr = [] for i in range(64): if m[table[i]] == None: s += b'\x00' arr.append(0) else: s += p8(int(str(m[table[i]]))) arr.append(m[table[i]]) print(s) open("table", "wb").write(s) table = s mp = [0] * 256 for i in range(len(arr)): if s[i] == 0: continue assert(mp[s[i]] == 0) mp[s[i]] = i flag = [BitVec(f'flag_{i}', 8) for i in range(30)] solver = Solver() out = open("flag_out.txt", "rb").read() v14 = 30 inp_idx = 0 output_idx = 0 while v14 - inp_idx > 2: x = mp[out[output_idx]] solver.add(flag[inp_idx] >> 2 == x) x = mp[out[output_idx + 1]] solver.add((flag[inp_idx + 1] >> 4) | (16 * flag[inp_idx]) & 0x30 == x) x = mp[out[output_idx + 2]] solver.add((flag[inp_idx + 2] >> 6) | (4 * flag[inp_idx + 1]) & 0x3c == x) x = mp[out[output_idx + 3]] solver.add(flag[inp_idx + 2] & 0x3f == x) output_idx += 4 inp_idx += 3 if v14 != inp_idx: idx_0 = output_idx idx_1 = output_idx + 1 x = mp[out[idx_0]] solver.add(flag[inp_idx] >> 2 == x) # solver.add(out[idx_0] == table[inp[inp_idx] >> 2]) if v14 - inp_idx == 1: # solver.add(out[idx_1] == table[(16 * inp[inp_idx]) & 0x30]) x = mp[out[idx_1]] solver.add((16 * flag[inp_idx]) & 0x30 == x) idx_2 = idx_1 + 1 idx_3 = idx_1 + 2 else: # solver.add(out[idx_1] == table[(inp[inp_idx + 1] >> 4) | (16 * inp[inp_idx]) & 0x30]) x = mp[out[idx_1]] solver.add((flag[inp_idx + 1] >> 4) | (16 * flag[inp_idx]) & 0x30 == x) v6 = idx_1 + 1 # solver.add(out[v6] == table[(4 * inp[inp_idx + 1]) & 0x3c]) x = mp[out[v6]] solver.add((4 * flag[inp_idx + 1]) & 0x3c == x) if solver.check() == sat: m = solver.model() arr = [m[flag[i]].as_long() for i in range(30)] print(bytes(arr)) ``` ## Times Link chall: https://dreamhack.io/wargame/challenges/247 Mở IDA lên coi hàm main: ``` c __int64 __fastcall main(int a1, char **a2, char **a3) { int v4; // ebx int v5; // ebx unsigned int *v6; // rbx int v7; // [rsp+1Ch] [rbp-54h] BYREF int i; // [rsp+20h] [rbp-50h] int j; // [rsp+24h] [rbp-4Ch] int k; // [rsp+28h] [rbp-48h] int m; // [rsp+2Ch] [rbp-44h] int input_size; // [rsp+30h] [rbp-40h] unsigned int seed; // [rsp+34h] [rbp-3Ch] char *input; // [rsp+38h] [rbp-38h] __int64 v15[6]; // [rsp+40h] [rbp-30h] BYREF v15[3] = __readfsqword(0x28u); if ( a1 == 2 ) { input = strdup(a2[1]); input_size = strlen(input); puts("Welcome to registration center"); seed = time(0LL); srand(seed); v4 = rand(); v7 = v4 + rand(); v15[0] = 0LL; v15[1] = 0LL; do_sth(&v7, 4uLL, (__int64)v15); for ( i = 0; i < input_size; ++i ) { input[i] ^= *((_BYTE *)v15 + ((4 * (_BYTE)i) & 0xF)); input[i] ^= *((_BYTE *)v15 + ((4 * (_BYTE)i + 1) & 0xF)); input[i] ^= *((_BYTE *)v15 + ((4 * (_BYTE)i + 2) & 0xF)); input[i] ^= *((_BYTE *)v15 + ((4 * (_BYTE)i + 3) & 0xF)); } for ( j = 0; j < input_size / 2; ++j ) *(_WORD *)&input[2 * j] ^= xor_value; seed = time(0LL); srand(seed); v5 = rand(); v7 = v5 + rand(); memset(v15, 0, 0x10uLL); do_sth(&v7, 4uLL, (__int64)v15); for ( k = 0; k < input_size; ++k ) { input[k] ^= *((_BYTE *)v15 + ((4 * (_BYTE)k) & 0xF)); input[k] ^= *((_BYTE *)v15 + ((4 * (_BYTE)k + 1) & 0xF)); input[k] ^= *((_BYTE *)v15 + ((4 * (_BYTE)k + 2) & 0xF)); input[k] ^= *((_BYTE *)v15 + ((4 * (_BYTE)k + 3) & 0xF)); } for ( m = 0; m < input_size / 4; ++m ) { v6 = (unsigned int *)&input[4 * m]; *v6 = enc(*v6); } if ( !memcmp(input, &cmp_arr, 0x29uLL) ) puts("Registration done !"); else puts("Registration failed.."); return 0LL; } else { printf("%s [registration code]\n", *a2); return 1LL; } } ``` Nhưng khi mình chạy thử thì nó hiện như này xong exit luôn. ``` ❯ ./times Not yet !!! Please wait more time. ``` Bài này y chang bài ptrace_block trên kia mình làm :))) Vấn đề ở hàm init thôi ``` c __int64 init_func() { int v0; // edx __int64 result; // rax if ( time(0LL) <= 1909094399 ) { puts("Not yet !!! Please wait more time."); exit(0); } v0 = 1234 * (ptrace(PTRACE_TRACEME, 29808LL, 24946LL, 25955LL) + 1); result = v0 ^ (unsigned int)(unsigned __int16)word_4048; word_4048 ^= v0; return result; } ``` Ở đây ta patch call _exit bằng nop hết là được. Xong là chạy được bth rùi nhé ``` ❯ ./times Not yet !!! Please wait more time. ./times [registration code] ``` Sau khi patch ``` c __int64 init_func() { int v0; // edx __int64 result; // rax if ( time(0LL) <= 1909094399 ) puts("Not yet !!! Please wait more time."); v0 = 1234 * (ptrace(PTRACE_TRACEME, 29808LL, 24946LL, 25955LL) + 1); result = v0 ^ (unsigned int)(unsigned __int16)xor_value; xor_value ^= v0; return result; } ``` ![image](https://hackmd.io/_uploads/HJWPuqImJx.png) Ta biết ptrace khi không debug = 0, giá trị của xor_value khi xor với cái kia là ra 0. Nên đoạn xor với giá trị này ở hàm main ta có thể bỏ qua. Check hàm enc: ``` c __int64 __fastcall enc(unsigned int a1) { return (unsigned int)__ROL4__( (((16 * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) << 8) & 0xFF00FF00 | (((16 * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) >> 8) & 0xFF00FF, 16); } ``` Để rev ROL4 thì ta thay bằng ROR4 là được. Từ chuỗi cmp_arr ta rev được chuỗi trước khi thực hiện hàm enc. Script của mình: ``` python #!/usr/bin/python3 from ctypes import * from pwn import * def _rol(val, bits, bit_size): return (val << bits % bit_size) & (2 ** bit_size - 1) | \ ((val & (2 ** bit_size - 1)) >> (bit_size - (bits % bit_size))) def _ror(val, bits, bit_size): return ((val & (2 ** bit_size - 1)) >> bits % bit_size) | \ (val << (bit_size - (bits % bit_size)) & (2 ** bit_size - 1)) __ROR4__ = lambda val, bits: _ror(val, bits, 32) __ROR8__ = lambda val, bits: _ror(val, bits, 64) __ROL4__ = lambda val, bits: _rol(val, bits, 32) __ROL8__ = lambda val, bits: _rol(val, bits, 64) def f(a1): return __ROL4__((((16 * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) << 8) & 0xFF00FF00 | (((16 * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) >> 8) & 0xFF00FF, 16) def revF(a1): return __ROR4__((((16 * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) << 8) & 0xFF00FF00 | (((16 * ((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333)) & 0xF0F0F0F0 | (((4 * ((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555)) & 0xCCCCCCCC | (((2 * a1) & 0xAAAAAAAA | (a1 >> 1) & 0x55555555) >> 2) & 0x33333333) >> 4) & 0xF0F0F0F) >> 8) & 0xFF00FF, 16) cmp = bytes.fromhex("66 0C 4C 86 A6 2C 1C 9C 1C 66 1C 2C 9C 6C A6 CC A6 6C 6C AC A6 A6 86 4C 2C 46 EC 8C EC 46 8C 9C 4C EC C6 66 4C 46 86 4C") input_final = [] ans = b'' for i in range(0, 40, 4): x = (cmp[i + 3] << 24) + (cmp[i + 2] << 16) + (cmp[i + 1] << 8) + cmp[i] x = revF(x) ans += p32(x) print(ans) ``` Ờm mình nhập thử thì nó là flag luôn ??? :D Rev có 1 hàm đã ra flag đúng là việc nhẹ lương cao =))) ## bitvm Link chall: https://dreamhack.io/wargame/challenges/1019 Check hàm main: ``` c __int64 __fastcall main(int a1, char **a2, char **a3) { if ( a1 == 2 ) { if ( (unsigned int)check_arg(a2[1]) == 1 ) { return 0LL; } else { if ( (unsigned __int8)check() ) puts("Wrong."); else puts("Correct!!!"); return 0LL; } } else { puts("Usage: ./chall command_file"); return 0LL; } } ``` Hàm check: ``` c __int64 check() { __int64 v1; // rdi unsigned __int8 v2; // [rsp+Fh] [rbp-11h] __int64 v3[2]; // [rsp+10h] [rbp-10h] v3[1] = __readfsqword(0x28u); v3[0] = 0LL; while ( 1 ) { v2 = txt[i_val++]; if ( v2 == 32 ) break; switch ( v2 ) { case 0x21u: putchar(txt[i_val]); ++i_val; break; case 0x22u: v1 = (unsigned __int8)getchar(); get_input(v1); break; case 0x23u: *((_BYTE *)v3 + txt[i_val]) = txt[i_val + 1]; i_val += 2; break; case 0x24u: *((_BYTE *)v3 + txt[i_val]) = *((_BYTE *)v3 + txt[i_val + 1]); i_val += 2; break; case 0x25u: *((_BYTE *)v3 + txt[i_val]) = *((_BYTE *)v3 + txt[i_val + 2]) & *((_BYTE *)v3 + txt[i_val + 1]); i_val += 3; break; case 0x26u: *((_BYTE *)v3 + txt[i_val]) = *((_BYTE *)v3 + txt[i_val + 2]) | *((_BYTE *)v3 + txt[i_val + 1]); i_val += 3; break; case 0x27u: *((_BYTE *)v3 + txt[i_val]) = *((_BYTE *)v3 + txt[i_val + 2]) ^ *((_BYTE *)v3 + txt[i_val + 1]); i_val += 3; break; case 0x28u: LOBYTE(v3[0]) = sub_55E1EC6C1315(); break; case 0x29u: get_input(*((_BYTE *)v3 + txt[i_val])); ++i_val; break; } } return LOBYTE(v3[0]); } ``` Mình viết script để xem nó làm gì: ``` python #!/usr/bin/python3 command = open("command", "rb").read() flagSize = 68 inp = [ord('a')] * 68 flag = [0] * 68 i = 0 inp_idx = 0 v3 = [0] * 16 while True: v2 = command[i] i += 1 if v2 == 0x20: break if v2 == 0x21: i += 1 elif v2 == 0x22: v1 = flag[inp_idx] inp_idx += 1 elif v2 == 0x23: v3[command[i]] = command[i + 1] print(f'v3[{command[i]}] = {command[i + 1]}') i += 2 elif v2 == 0x24: v3[command[i]] = v3[command[i + 1]] print(f'v3[{command[i]}] = v3[{command[i + 1]}]') i += 2 elif v2 == 0x25: v3[command[i]] = v3[command[i + 2]] & v3[command[i + 1]] print(f'v3[{command[i]}] = v3[{command[i + 1]}] & v3[{command[i + 2]}]') i += 3 elif v2 == 0x26: v3[command[i]] = v3[command[i + 2]] | v3[command[i + 1]] print(f'v3[{command[i]}] = v3[{command[i + 1]}] | v3[{command[i + 2]}]') i += 3 elif v2 == 0x27: v3[command[i]] = v3[command[i + 2]] ^ v3[command[i + 1]] print(f'v3[{command[i]}] = v3[{command[i + 1]}] ^ v3[{command[i + 2]}]') i += 3 elif v2 == 0x28: inp_idx -= 1 v3[0] = flag[inp_idx] print(f'v3[0] = flag[{inp_idx}]') elif v2 == 0x29: flag[inp_idx] = v3[command[i]] print(f'flag[{inp_idx}] = v3[{command[i]}]') i += 1 inp_idx += 1 ``` ``` python v3[1] = 243 v3[2] = 113 v3[3] = 255 v3[0] = flag[67] v3[4] = v3[0] & v3[1] v3[5] = v3[2] ^ v3[4] v3[7] = v3[5] | v3[7] v3[4] = v3[0] | v3[1] v3[5] = v3[3] ^ v3[4] v3[7] = v3[5] | v3[7] v3[1] = 193 v3[2] = 1 v3[3] = 245 v3[0] = flag[66] v3[4] = v3[0] & v3[1] v3[5] = v3[2] ^ v3[4] v3[7] = v3[5] | v3[7] v3[4] = v3[0] | v3[1] v3[5] = v3[3] ^ v3[4] v3[7] = v3[5] | v3[7] v3[1] = 38 v3[2] = 32 v3[3] = 63 v3[0] = flag[65] v3[4] = v3[0] & v3[1] v3[5] = v3[2] ^ v3[4] v3[7] = v3[5] | v3[7] v3[4] = v3[0] | v3[1] v3[5] = v3[3] ^ v3[4] v3[7] = v3[5] | v3[7] v3[1] = 128 v3[2] = 0 v3[3] = 229 v3[0] = flag[64] v3[4] = v3[0] & v3[1] v3[5] = v3[2] ^ v3[4] v3[7] = v3[5] | v3[7] v3[4] = v3[0] | v3[1] v3[5] = v3[3] ^ v3[4] v3[7] = v3[5] | v3[7] ... v3[1] = 20 v3[2] = 4 v3[3] = 84 v3[0] = flag[0] v3[4] = v3[0] & v3[1] v3[5] = v3[2] ^ v3[4] v3[7] = v3[5] | v3[7] v3[4] = v3[0] | v3[1] v3[5] = v3[3] ^ v3[4] v3[7] = v3[5] | v3[7] v3[0] = v3[7] Ta cần v3[0] = v[7] = 0 ``` Hmm nhìn phép bit này hơi khó rev, brute force được nên làm luôn nhé. ``` python #!/usr/bin/python3 arr = [243, 113, 255, 193, 1, 245, 38, 32, 63, 128, 0, 229, 151, 16, 183, 248, 96, 252, 123, 48, 123, 37, 36, 101, 42, 34, 58, 248, 96, 249, 224, 96, 227, 88, 24, 121, 219, 17, 255, 152, 16, 191, 62, 34, 126, 30, 4, 127, 236, 36, 255, 250, 56, 251, 53, 53, 55, 224, 96, 226, 72, 64, 109, 0, 0, 102, 93, 21, 127, 223, 24, 255, 199, 0, 255, 100, 100, 100, 188, 36, 254, 117, 49, 117, 13, 0, 63, 161, 32, 181, 204, 4, 252, 76, 68, 108, 155, 0, 255, 61, 53, 63, 192, 64, 228, 63, 56, 63, 116, 52, 119, 68, 0, 119, 251, 97, 251, 230, 36, 247, 43, 35, 107, 157, 0, 255, 251, 49, 251, 39, 33, 63, 213, 17, 253, 69, 0, 125, 231, 98, 231, 13, 8, 61, 127, 100, 127, 33, 32, 103, 153, 16, 189, 217, 65, 249, 64, 64, 102, 242, 50, 247, 168, 32, 185, 190, 50, 190, 174, 34, 238, 96, 96, 98, 236, 96, 237, 196, 0, 247, 87, 69, 119, 152, 16, 186, 184, 48, 188, 48, 32, 115, 57, 32, 123, 81, 81, 123, 4, 0, 76, 20, 4, 84] flag = [] v3 = [0] * 8 for i in range(0, len(arr), 3): v3[1] = arr[i] v3[2] = arr[i + 1] v3[3] = arr[i + 2] for c in range(33, 127): v3[0] = c v3[4] = v3[0] & v3[1] v3[5] = v3[2] ^ v3[4] v3[7] = v3[5] | v3[7] v3[4] = v3[0] | v3[1] v3[5] = v3[3] ^ v3[4] v3[7] = v3[5] | v3[7] if v3[7] == 0: flag.append(c) break else: v3[4] = v3[5] = v3[6] = v3[7] = 0 print(bytes(flag[::-1])) ``` ## public Link chall: https://dreamhack.io/wargame/challenges/91 Hàm main: ``` c __int64 __fastcall main(int a1, char **a2, char **a3) { int i; // [rsp+Ch] [rbp-174h] __int64 v5; // [rsp+18h] [rbp-168h] BYREF unsigned __int64 v6; // [rsp+20h] [rbp-160h] BYREF unsigned __int64 ptr; // [rsp+28h] [rbp-158h] BYREF __int64 v8; // [rsp+30h] [rbp-150h] __int64 v9; // [rsp+38h] [rbp-148h] unsigned __int64 n1; // [rsp+40h] [rbp-140h] unsigned __int64 n2; // [rsp+48h] [rbp-138h] FILE *stream; // [rsp+50h] [rbp-130h] unsigned __int64 v13; // [rsp+58h] [rbp-128h] char s[8]; // [rsp+60h] [rbp-120h] BYREF __int64 v15; // [rsp+68h] [rbp-118h] __int64 v16; // [rsp+70h] [rbp-110h] __int64 v17; // [rsp+78h] [rbp-108h] __int64 v18; // [rsp+80h] [rbp-100h] __int64 v19; // [rsp+88h] [rbp-F8h] __int64 v20; // [rsp+90h] [rbp-F0h] __int64 v21; // [rsp+98h] [rbp-E8h] __int64 v22; // [rsp+A0h] [rbp-E0h] __int64 v23; // [rsp+A8h] [rbp-D8h] __int64 v24; // [rsp+B0h] [rbp-D0h] __int64 v25; // [rsp+B8h] [rbp-C8h] __int64 v26; // [rsp+C0h] [rbp-C0h] __int64 v27; // [rsp+C8h] [rbp-B8h] __int64 v28; // [rsp+D0h] [rbp-B0h] __int64 v29; // [rsp+D8h] [rbp-A8h] __int64 v30; // [rsp+E0h] [rbp-A0h] __int64 v31; // [rsp+E8h] [rbp-98h] __int64 v32; // [rsp+F0h] [rbp-90h] __int64 v33; // [rsp+F8h] [rbp-88h] __int64 v34; // [rsp+100h] [rbp-80h] __int64 v35; // [rsp+108h] [rbp-78h] __int64 v36; // [rsp+110h] [rbp-70h] __int64 v37; // [rsp+118h] [rbp-68h] __int64 v38; // [rsp+120h] [rbp-60h] __int64 v39; // [rsp+128h] [rbp-58h] __int64 v40; // [rsp+130h] [rbp-50h] __int64 v41; // [rsp+138h] [rbp-48h] __int64 v42; // [rsp+140h] [rbp-40h] __int64 v43; // [rsp+148h] [rbp-38h] __int64 v44; // [rsp+150h] [rbp-30h] __int64 v45; // [rsp+158h] [rbp-28h] unsigned __int64 v46; // [rsp+168h] [rbp-18h] v46 = __readfsqword(0x28u); n1 = 0LL; stream = fopen("n.txt", "r"); __isoc99_fscanf(stream, "%llu %llu", &v5, &v6); fclose(stream); while ( n1 <= 0xFCFCFCFC ) { v8 = sub_12FE(v5); v9 = sub_12FE(v8 + 128); n1 = v9 * v8; } printf("n1 = %llu\n", n1); n2 = v6; v13 = (v8 - 1) * (v9 - 1); while ( n2 < v13 && sub_1249(n2, v13) != 1 ) ++n2; printf("n2 = %llu\n", n2); *(_QWORD *)s = 0LL; v15 = 0LL; v16 = 0LL; v17 = 0LL; v18 = 0LL; v19 = 0LL; v20 = 0LL; v21 = 0LL; v22 = 0LL; v23 = 0LL; v24 = 0LL; v25 = 0LL; v26 = 0LL; v27 = 0LL; v28 = 0LL; v29 = 0LL; v30 = 0LL; v31 = 0LL; v32 = 0LL; v33 = 0LL; v34 = 0LL; v35 = 0LL; v36 = 0LL; v37 = 0LL; v38 = 0LL; v39 = 0LL; v40 = 0LL; v41 = 0LL; v42 = 0LL; v43 = 0LL; v44 = 0LL; v45 = 0LL; stream = fopen("flag.txt", "rb"); __isoc99_fscanf(stream, "%255s", s); fclose(stream); if ( (strlen(s) & 3) != 0 ) { puts("invalid length!"); exit(-1); } stream = fopen("out.bin", "wb"); for ( i = 0; i < strlen(s) >> 2; ++i ) { ptr = encrypt(*(unsigned int *)&s[4 * i], n2, n1); fwrite(&ptr, 8uLL, 1uLL, stream); } fclose(stream); return 0LL; } ``` ``` c unsigned __int64 __fastcall sub_1289(__int64 a1, unsigned __int64 n2, unsigned __int64 n1) { unsigned __int64 v4; // [rsp+20h] [rbp-10h] unsigned __int64 i; // [rsp+28h] [rbp-8h] if ( !n1 ) exit(-1); v4 = 1LL; for ( i = 0LL; i < n2; ++i ) // v4 = a1 ^ n2 % n1 v4 = a1 * v4 % n1; return v4; } ``` Ok bài rất rõ ràng, dễ hiểu. Nhìn kiểu quái gì nó cũng là crypto đội lốt reverse (lol mình mới chơi CTF nên chưa đụng bài crypto nào). Từ ref đề bài cho ta đoán nó là RSA. Ta được cho 2 file out.txt và out.bin ![image](https://hackmd.io/_uploads/BJQbFGcXJe.png) Phân tích thừa số nguyên tố thì: * n1 = 65287 · 65419 * Vậy φ(n1) = (p-1)(q-1) * Vừa hay gcd(φ(n1), n2) = 1 luôn nên ta xem e = n2 * Để tiện cho việc tính toán ta xem n = n1, e = n2, p = 65287, q = 65419 v4 = a1 ^ n2 % n1 -> c = m ^ e (mod n) Ở đây ta có sẵn c = v4, tìm m: d = e ^ (-1) (mod φ(n)) m = c ^ d (mod n) ``` python #!/usr/bin/python3 from Crypto.Util.number import * from pwn import * n = 4271010253 e = 201326609 p = 65287 q = 65419 phi_n = (p - 1) * (q - 1) d = pow(e, -1, phi_n) out = open("out.bin", "rb").read() ans = b'' for i in range(0, len(out), 8): x = out[i] | (out[i + 1] << 8) | (out[i + 2] << 16) | (out[i + 3] << 24) m = pow(x, d, n) ans += p32(m) print(ans) ``` Bài này dễ nhưng nó là crypto nên mình xin để lại trên blog =))) (mong mấy bạn crypto đừng vào pressing mình hjx hjx) ## very-easy-wasm Mấy chall trên khá dễ, nên mình nghĩ mình làm những thứ mới mẻ hơn chút. Link chall: https://dreamhack.io/wargame/challenges/762 Script js nằm trong index.html: ``` javascript const btn = document.getElementById("btn") const input = document.getElementById("input") const out = document.getElementById("out") const memory = new WebAssembly.Memory({ initial: 1 }); const u8Arr = new Uint8Array(memory.buffer) WebAssembly.instantiateStreaming(fetch('simple.wasm'), { "imports": { memory } }) .then(obj => { btn.addEventListener("click", function () { const flag = input.value const res = new TextEncoder().encodeInto(flag, u8Arr) const len = res.written for (let i = 0; i < 9; i++) { obj.instance.exports.something(len) for (let j = 0; j < len / 2 | 0; j++) { let t = u8Arr[len - j - 1] u8Arr[len - j - 1] = u8Arr[j] u8Arr[j] = t } } out.innerText = Array.from(u8Arr.slice(0, len), function (byte) { return ('0' + (byte & 0xFF).toString(16)).slice(-2); }).join('') }) }) ``` Dùng wasm2wat hoặc wasm-decompile để đọc simple.wasm. wasm2wat: ``` wat (module (type (;0;) (func (param i32) (result i32))) (type (;1;) (func (param i32))) (import "imports" "memory" (memory (;0;) 1)) (func (;0;) (type 0) (param i32) (result i32) local.get 0 i32.const 192 i32.xor i32.const 175 i32.add i32.const 255 i32.and) (func (;1;) (type 0) (param i32) (result i32) local.get 0 i32.const 191 i32.xor i32.const 236 i32.add i32.const 255 i32.and) (func (;2;) (type 0) (param i32) (result i32) local.get 0 i32.const 196 i32.xor i32.const 171 i32.add i32.const 255 i32.and) (func (;3;) (type 1) (param i32) (local i32 i32 i32) i32.const 0 local.tee 3 local.set 1 loop ;; label = @1 local.get 1 i32.load8_u local.set 2 local.get 1 local.get 2 local.get 3 i32.const 3 i32.rem_u call_indirect (type 0) i32.store8 local.get 2 local.set 3 local.get 1 i32.const 1 i32.add local.tee 1 local.get 0 i32.lt_s br_if 0 (;@1;) end) (table (;0;) 3 funcref) (export "something" (func 3)) (elem (;0;) (i32.const 0) func 0 1 2)) ``` wasm-decompile: ``` wasm import memory imports_memory; table T_a:funcref(min: 3, max: 0); function f_a(a:int):int { return (a ^ 192) + 175 & 255 } function f_b(a:int):int { return (a ^ 191) + 236 & 255 } function f_c(a:int):int { return (a ^ 196) + 171 & 255 } export function something(a:int) { var d:int = 0; var b:int = d; loop L_a { var c:int = b[0]:ubyte; b[0]:byte = call_indirect(c, d % 3); d = c; b = b + 1; if (b < a) continue L_a; } } ``` Có vẻ cái wasm-decompile này đọc thuận mắt hơn, dễ hiểu hơn cái wasm2wat kia ? Vì mình thấy code không phức tạp lắm nên không cần giải thích nhiều đâu nhỉ, code lại thôi: ``` python3 #!/usr/bin/python3 from pwn import * def f1(a): return (a ^ 192) + 175 & 255 def f2(a): return (a ^ 191) + 236 & 255 def f3(a): return (a ^ 196) + 171 & 255 arr = bytearray(bytes.fromhex('aea9ee71d339c19b0e9c504213b105ebd8c78b9205d46ef347fe70ce4df8dcf4cd7c9f79')) n = len(arr) for k in range(9): for i in range(0, n // 2, 1): t = arr[n - i - 1] arr[n - i - 1] = arr[i] arr[i] = t d = 0 ans = [] for i in range(len(arr)): for c in range(256): if d % 3 == 0: if f1(c) == arr[i]: d = c ans.append(c) break if d % 3 == 1: if f2(c) == arr[i]: d = c ans.append(c) break if d % 3 == 2: if f3(c) == arr[i]: d = c ans.append(c) break arr = ans print(bytes(arr)) ``` Mình thấy script còn cải tiến được: ``` python3 #!/usr/bin/python3 from pwn import * def f1(a): return (a - 175 & 255) ^ 192 def f2(a): return (a - 236 & 255) ^ 191 def f3(a): return (a - 171 & 255) ^ 196 arr = bytearray(bytes.fromhex('aea9ee71d339c19b0e9c504213b105ebd8c78b9205d46ef347fe70ce4df8dcf4cd7c9f79')) n = len(arr) for k in range(9): for i in range(0, n // 2, 1): t = arr[n - i - 1] arr[n - i - 1] = arr[i] arr[i] = t d = 0 ans = [] for i in range(len(arr)): if d % 3 == 0: d = f1(arr[i]) ans.append(d) elif d % 3 == 1: d = f2(arr[i]) ans.append(d) elif d % 3 == 2: d = f3(arr[i]) ans.append(d) arr = ans print(bytes(arr)) ``` Không hiểu bài này cho cái server làm gì nhỉ :v đâu cần lắm đâu ta. ## passcode Link chall: https://dreamhack.io/wargame/challenges/228 Và lại là DG house... à nhầm WebAssembly. Check file main.js thì mình thấy mảng code chính là code WebAssembly, ta viết lại file: ``` python #!/usr/bin/python3 from pwn import * code = [...] # Phần code dài quá nên mình xin lược bỏ file = b'' for x in code: file += p8(x) open("main.wasm", "wb").write(file) ``` Bài trước tại đơn giản nên mới làm được vậy thôi :)) Mình tìm được cái này cũng khá xịn xò https://github.com/nneonneo/ghidra-wasm-plugin. Mở ghidra lên và đọc thôi: ``` c uint export::f(undefined *arr,int len) { undefined uVar1; byte bVar2; uint uVar3; byte *idx; uint uVar4; uVar3 = 0xff; if (len == 0x19) { uVar3 = 0; do { idx = arr + (uVar3 & 0xff) % 0x19; bVar2 = *idx ^ *(byte *)(uVar3 + 0x420); *idx = bVar2; if ((uVar3 & 1) == 0) { uVar4 = (uint)(bVar2 >> (uVar3 & 7)) | (uint)bVar2 << (-uVar3 & 7); } else { uVar4 = (uint)bVar2 << (uVar3 & 7) | (uint)(bVar2 >> (-uVar3 & 7)); } *idx = (byte)uVar4; *idx = *(byte *)((uVar4 & 0xff) + 0x520); uVar3 = uVar3 + 1; } while (uVar3 != 0x100); uVar1 = *arr; *arr = arr[0x18]; arr[0x18] = uVar1; uVar1 = arr[1]; arr[1] = arr[0x17]; arr[0x17] = uVar1; uVar1 = arr[2]; arr[2] = arr[0x16]; arr[0x16] = uVar1; uVar1 = arr[3]; arr[3] = arr[0x15]; arr[0x15] = uVar1; uVar1 = arr[4]; arr[4] = arr[0x14]; arr[0x14] = uVar1; uVar1 = arr[5]; arr[5] = arr[0x13]; arr[0x13] = uVar1; uVar1 = arr[6]; arr[6] = arr[0x12]; arr[0x12] = uVar1; uVar1 = arr[7]; arr[7] = arr[0x11]; arr[0x11] = uVar1; uVar1 = arr[8]; arr[8] = arr[0x10]; arr[0x10] = uVar1; uVar1 = arr[9]; arr[9] = arr[0xf]; arr[0xf] = uVar1; uVar1 = arr[10]; arr[10] = arr[0xe]; arr[0xe] = uVar1; uVar1 = arr[0xb]; arr[0xb] = arr[0xd]; arr[0xd] = uVar1; uVar3 = memcmp(arr,(void *)0x400,0x19); } return uVar3 & 0xff; } ``` Đọc file main.js thì biết được đây là hàm duy nhất chúng ta cần quan tâm. Thấy có vẻ rev chay được nên rev chay hết nhé (~~thật ra vì mình thấy dùng z3 không được~~). Đoạn dịch bit mình mò thì thấy làm ngược lại là được, mấy đoạn còn lại thì dễ rồi. ``` python #!/usr/bin/python3 bVar2 = 110 uVar3 = 2 uVar4 = (bVar2 >> (uVar3 & 7)) | bVar2 << (-uVar3 & 7) print(uVar4) uVar4 &= 0xff x = (uVar4 << (uVar3 & 7)) | uVar4 >> (-uVar3 & 7) x &= 0xff print(x) ``` Script của mình: ``` python #!/usr/bin/python3 arr = bytearray(bytes.fromhex('2d f8 01 81 6d 94 3d 86 f1 80 a9 1f ca 33 08 56 81 c5 2f 53 e9 5f 33 be 6e 00')) xor_arr = bytes.fromhex("8d 8b de d0 34 1e 55 2f ca 92 62 fd 6b 4e 24 89 6e 0b ef 8a 42 9a 1a 50 9d ff d1 b4 b5 cc a1 70 e6 b0 69 b8 22 5b 7b 41 ae e3 82 4f dc af 05 ee fe 47 71 4c 96 4a fa 21 53 85 cf c8 a2 c0 03 73 30 80 be 72 f9 c5 d3 fb 7d 25 14 b3 ba f1 12 11 c1 19 a0 98 7f 5d 90 bc aa 5a 09 e5 06 2d 6f ac 39 0e da 23 2a 27 01 84 91 29 0c b6 3d e8 ce c7 ab f3 59 66 28 1f 78 51 07 32 00 88 f7 60 d8 ec d4 4d 8e 9b 1d 1c 35 7c 97 61 3e 46 d9 eb e2 a8 36 3c 94 86 44 99 52 dd 8c 37 7a f5 f8 5e db 75 ea fc 4b df d2 45 3b 17 c2 a3 2c 13 40 7e 56 08 87 33 81 93 9f 31 bd 2b 48 0a 10 65 9e 64 1b 04 f0 c6 ed a4 79 a9 e7 9c a7 5f f4 3a 26 20 16 38 6a cd 76 c3 43 e4 49 f2 cb d7 6d 68 e0 0f a6 e9 58 77 b9 74 02 c9 d5 2e b1 d6 e1 95 18 67 3f 0d 57 ad 15 8f 83 63 bf bb f6 b7 54 5c c4 b2 6c a5 45 67 ec be c5 de 6d 23 e6 db f9 e3 a0 7e 7a 5e 41 e9 1f 17 29 c7 84 f3 1c d1 f2 3f ae 65 0c 1d e0 e8 6b 00 ed 18 31 55 5d 95 a5 5a e1 51 d5 b5 0e 50 b9 d9 9f f7 32 c3 d4 ef a9 62 98 9c c1 72 bb ea 60 38 d7 3e 01 2b 27 a2 99 86 bd 61 b7 7b a7 cc f4 aa c8 36 9a 4e 3b 52 0f 16 d8 9e dc 48 c0 2a 39 44 c9 c4 14 6f 64 02 cf 57 f5 c6 85 2e 75 e4 08 63 2c af 59 cb 47 7d 79 78 c2 8c ce 10 49 87 26 4b 8e 77 94 03 f6 97 4d 35 83 0a 1e 71 f0 3a 53 5f e7 15 69 90 cd 68 04 81 9d fb 42 22 24 a8 2f 82 b4 ab 1b 19 28 07 ad 74 30 8d b2 e5 ca fc a6 8a f1 09 0d 89 91 66 76 92 eb 88 46 fe d3 6e ee 58 40 6c b6 13 ac a4 0b b1 a1 7f 3c 8b 54 33 34 5b b0 bf d0 93 73 4a 70 bc 43 df 4c dd 37 ba d2 ff f8 e2 a3 3d 80 20 2d 1a 05 11 5c 06 b8 21 96 8f fa b3 12 7c d6 da 4f fd 25 9b 6a 56 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00") for i in range(0, 0x19 // 2, 1): arr[i], arr[0x19 - i - 1] = arr[0x19 - i - 1], arr[i] mp = {} for i in range(0x100, 0x100 + 0x100): assert(xor_arr[i] not in mp) mp[xor_arr[i]] = i for uVar3 in range(0xff, -1, -1): idx = (uVar3 & 0xff) % 0x19 # arr[idx] = xor_arr[0x100 + (uVar4 & 0xff)] uVar4 = mp[arr[idx]] - 0x100 if (uVar3 & 1) == 0: bVar2 = (uVar4 << (uVar3 & 7)) | uVar4 >> (-uVar3 & 7) else: bVar2 = (uVar4 >> (uVar3 & 7)) | uVar4 << (-uVar3 & 7) bVar2 &= 0xff arr[idx] = bVar2 ^ xor_arr[uVar3] print(arr) ``` ## pikachu_volleyball Link chall: https://dreamhack.io/wargame/challenges/1052 Điều đầu tiên làm mình ấn tượng là cái game này thật sự quá đáng............. yêu =))) Chủ yếu logic nằm hết ở file main.bundle.js (khi mình làm xong rồi thì biết nó gọi là webpack obfuscation ?), mình deobfuscate trước đã. Cũng may vì code khá đẹp, tên biến rất dễ đọc. Vì đây là game nên trước tiên mình Ctrl + F tìm các keyword mà ta quen thuộc khi chơi game. Tìm tới chữ "score" thì ra được hàm này có vẻ khá quan trọng trong chương trình, nó tác động tới số điểm người chơi: ``` javascript ['round']() { const _0x4b6216 = 0x1 === this.keyboardArray[0x0].powerHit || 0x1 === this.keyboardArray[0x1].powerHit; if (true === this.physics.player1.isComputer && true === this.physics.player2.isComputer && _0x4b6216) { this.frameCounter = 0x0; this.view.game.visible = false; return void (this.state = this.intro); } const _0x277fa9 = this.physics.runEngineForNextFrame(this.keyboardArray); this.playSoundEffect(); this.view.game.drawPlayersAndBall(this.physics); this.view.game.drawCloudsAndWave(); if (true === this.gameEnded) { if (true === this.gameFinished) { var _0x2c4c8c = new TextDecoder().decode(this.message); this.view.game.drawGameEndMessage(this.frameCounter, _0x2c4c8c); } else { this.view.game.drawGameEndMessage(this.frameCounter, ''); } this.frameCounter++; return void ((this.frameCounter >= this.frameTotal.gameEnd || this.frameCounter >= 0x46 && _0x4b6216) && (this.frameCounter = 0x0, this.view.game.visible = false, this.state = this.intro)); } if (_0x277fa9 && false === this._isPracticeMode && false === this.roundEnded && false === this.gameEnded) { if (this.physics.ball.punchEffectX < 216) { this.isPlayer2Serve = true; this.scores[0x1] += 0x1; this.calcMessage(); if (this.scores[0x1] > this.winningScore) { this.gameEnded = true; this.physics.player1.isWinner = false; this.physics.player2.isWinner = true; this.physics.player1.gameEnded = true; this.physics.player2.gameEnded = true; } if (this.scores[0x1] == this.winningScore) { this.gameEnded = true; if (0x0 == this.scores[0x0]) { this.gameFinished = true; } this.physics.player1.isWinner = false; this.physics.player2.isWinner = true; this.physics.player1.gameEnded = true; this.physics.player2.gameEnded = true; } } else { this.isPlayer2Serve = false; this.scores[0x0] += 0x1; if (this.scores[0x0] >= this.winningScore) { this.gameEnded = true; this.physics.player1.isWinner = true; this.physics.player2.isWinner = false; this.physics.player1.gameEnded = true; this.physics.player2.gameEnded = true; } } this.view.game.drawScoresToScoreBoards(this.scores); if (false === this.roundEnded && false === this.gameEnded) { this.slowMotionFramesLeft = this.SLOW_MOTION_FRAMES_NUM; } this.roundEnded = true; } if (true === this.roundEnded && false === this.gameEnded && 0x0 === this.slowMotionFramesLeft) { this.view.fadeInOut.changeBlackAlphaBy(0.0625); this.state = this.afterEndOfRound; } } ``` ``` javascript ["drawGameEndMessage"](_0x2097bc, _0x133a1a) { const _0xebf299 = this.messages.gameEnd; const _0xc813d0 = _0xebf299.texture.width; const _0x422e94 = _0xebf299.texture.height; if (0x0 === _0x2097bc) { _0xebf299.visible = true; this.score.text = _0x133a1a; if ('' != _0x133a1a) { this.score.visible = true; } } if (_0x2097bc < 0x32) { const _0x400011 = 0x2 * Math.floor((0x32 - _0x2097bc) * _0xc813d0 / 0x32); const _0x396c59 = 0x2 * Math.floor((0x32 - _0x2097bc) * _0x422e94 / 0x32); _0xebf299.x = 0xd8 - _0xc813d0 / 0x2 - _0x400011; _0xebf299.y = 0x32 - _0x396c59; _0xebf299.width = _0xc813d0 + 0x2 * _0x400011; _0xebf299.height = _0x422e94 + 0x2 * _0x396c59; } else { _0xebf299.x = 0xd8 - _0xc813d0 / 0x2; _0xebf299.y = 0x32; _0xebf299.width = _0xc813d0; _0xebf299.height = _0x422e94; } } ``` Khi score >= winningScore thì nghĩa là endGame, nó in ra cái message gì đó, mà message khi chúng ta win game thì khác gì flag ? Mảng message được khởi tạo trong chương trình: ``` javascript this.message = new Uint8Array([0x44, 0x49, 0x7b, 0x60, 0x63, 0x46, 0x73, 0x2, 0x22, 0x21, 0x50, 0x1a, 0xdb, 0xf6, 0xab, 0xd9, 0x146, 0x154, 0x171, 0x10a, 0x1f1, 0x1cd, 0x1c5, 0x27e, 0x22e, 0x22e, 0x2c3, 0x2e9, 0x37f, 0x30d, 0x3bb, 0x7d]); ``` Ok ta in ra thử thì thấy nó không có ý nghĩa gì cả, để ý một đoạn nhỏ ở code đầu tiên, đây cũng là đoạn gây thay đổi cho mảng message: ``` javascript this.scores[0x1] += 0x1; this.calcMessage(); ``` ``` javascript ["calcMessage"]() { var _0x5e4053 = this.scores[0x1]; if (0x2 != _0x5e4053 && 0x1f != _0x5e4053 && _0x5e4053 < 0x21) { this.message[_0x5e4053] ^= _0x5e4053 * _0x5e4053; } } ``` Vậy cái this.scores[0x1] là điểm bên phía người chơi, mỗi khi ta tăng từng điểm thì flag cũng sẽ thay đổi theo. Ta code giả lập lại quá trình tăng điểm. Script của mình: ``` python #!/usr/bin/python3 message = [0x44, 0x49, 0x7b, 0x60, 0x63, 0x46, 0x73, 0x2, 0x22, 0x21, 0x50, 0x1a, 0xdb, 0xf6, 0xab, 0xd9, 0x146, 0x154, 0x171, 0x10a, 0x1f1, 0x1cd, 0x1c5, 0x27e, 0x22e, 0x22e, 0x2c3, 0x2e9, 0x37f, 0x30d, 0x3bb, 0x7d] for i in range(0x20): if i == 2 or i == 0x1f: continue message[i] ^= i * i print(''.join(chr(x & 0xff) for x in message)) ``` ## Slooooow Link chall: https://dreamhack.io/wargame/challenges/1150 Chạy file: ``` linux ❯ ./main _____ __ / ___// /___ ____ ____ ____ ____ _ __ \__ \/ / __ \/ __ \/ __ \/ __ \/ __ \ | /| / / ___/ / / /_/ / /_/ / /_/ / /_/ / /_/ / |/ |/ / /____/_/\____/\____/\____/\____/\____/|__/|__/ Yes, it's actually slow! ...According to the master theorem from 'Introduction to Algorithms'. ``` Chạy xong nó đứng 1 hồi lâu, chắc phải tốn mấy năm mới xong nhỉ ? Nên mình đi phân tích chương trình vậy. Check hàm main: ``` c++ __int64 __fastcall main(__int64 a1, char **a2, char **a3) { sub_1249(a1, a2, a3); sub_1290(); sub_138B(0LL, &unk_1869F); sub_14A9(); return 0LL; } ``` Hàm sub_1290 chỉ in mấy cái chữ ra màn hình thôi, check 2 hàm kia: ``` c __int64 __fastcall sub_138B(unsigned int a1, unsigned int a2) { __int64 result; // rax unsigned int v3; // [rsp+18h] [rbp-8h] int v4; // [rsp+1Ch] [rbp-4h] if ( (unsigned int)sub_1313(dword_4020[a1], dword_4020[a2]) ) { v3 = dword_4020[a1]; dword_4020[a1] = dword_4020[a2]; dword_4020[a2] = v3; } result = a2 - a1; if ( (int)result > 1 ) { v4 = (int)(a2 - a1 + 1) / 3; sub_138B(a1, a2 - v4); sub_138B(v4 + a1, a2); return sub_138B(a1, a2 - v4); } return result; } ``` ``` c++ unsigned __int64 sub_14A9() { unsigned int v1; // [rsp+8h] [rbp-68h] BYREF unsigned int i; // [rsp+Ch] [rbp-64h] __int64 v3; // [rsp+10h] [rbp-60h] __int64 v4; // [rsp+18h] [rbp-58h] char v5[72]; // [rsp+20h] [rbp-50h] BYREF unsigned __int64 v6; // [rsp+68h] [rbp-8h] v6 = __readfsqword(0x28u); v3 = EVP_sha256(); v4 = EVP_MD_CTX_new(); EVP_DigestInit_ex(v4, v3, 0LL); EVP_DigestUpdate(v4, dword_4020, &unk_61A80); EVP_DigestFinal_ex(v4, v5, &v1); printf("Flag is: DH{"); for ( i = 0; i < v1; ++i ) printf("%02x", (unsigned __int8)v5[i]); puts("}"); return v6 - __readfsqword(0x28u); } ``` Ok thì mình thấy vấn đề chính của ta nằm ở hàm sub_138B, mình thấy nó chia để trị rất cồng kềnh nên độ phức tạp khá là cao. Hàm đó nó thực hiện các thao tác gì đó với mảng dword_4020, rồi mảng dword_4020 lại dùng để sha256 để ra flag. Hướng của mình là đọc lấy giá trị mảng dword_4020 trong chương trình, rồi tối ưu lại thuật toán sao cho đủ nhanh, rồi viết lại mảng dword_4020 trong chương trình chính của mình. Lúc này mảng dword_4020 đã được áp dụng thuật toán chia để trị trên rồi nên khúc gọi hàm sub_138B ta patch lại thành nop hết. Code của mình: ``` c++ #include <bits/stdc++.h> using namespace std; unsigned int dword_4020[0x1869f + 5]; bool sub_1313(unsigned int a1, unsigned int a2) { return ((a1 >> 25) | (a1 >> 9) & 0xFF80 | (a1 << 6) & 0x3F0000 | (a1 << 22)) <= ((a2 >> 25) | (a2 >> 9) & 0xFF80 | (a2 << 6) & 0x3F0000 | (a2 << 22)); } void sub_138B(unsigned int a1, unsigned int a2) { long long result; // rax unsigned int v3; // [rsp+18h] [rbp-8h] int v4; // [rsp+1Ch] [rbp-4h] if ( (unsigned int)sub_1313(dword_4020[a1], dword_4020[a2]) ) { v3 = dword_4020[a1]; dword_4020[a1] = dword_4020[a2]; dword_4020[a2] = v3; } result = a2 - a1; if ( (int)result > 1 ) { v4 = (int)(a2 - a1 + 1) / 3; sub_138B(a1, a2 - v4); sub_138B(v4 + a1, a2); sub_138B(a1, a2 - v4); } // return; } int main() { int size = 0x1869f + 1; FILE *file; file = fopen("main", "rb"); fseek(file, 0x3020, SEEK_SET); size_t dwordRead = fread(dword_4020, 4, size, file); fclose(file); sort(dword_4020, dword_4020 + size, sub_1313); file = fopen("main.cp", "r+b"); fseek(file, 0x3020, SEEK_SET); fwrite(dword_4020, 4, size, file); return 0; } ``` Thuật toán chia để trị trên mình thấy nó giống như một hàm sort, nên mình tối ưu theo hướng đó. Rồi viết lại mảng dword_4020 ở chương trình thôi. Fun fact: trong quá trình làm mình quên mất cộng size thêm 1, làm mình mất mấy ngày để nhận ra ??? ## instrs Link chall: https://dreamhack.io/wargame/challenges/1275 Hàm main: ``` c __int64 __fastcall main(__int64 a1, char **a2, char **a3) { int v4; // [rsp+1Ch] [rbp-24h] char *lineptr; // [rsp+20h] [rbp-20h] BYREF size_t n; // [rsp+28h] [rbp-18h] BYREF FILE *stream; // [rsp+30h] [rbp-10h] unsigned __int64 v8; // [rsp+38h] [rbp-8h] v8 = __readfsqword(0x28u); sub_12EA(a1, a2, a3); memset(input, 0, sizeof(input)); memset(byte_4060, 0, 8uLL); idx_2 = 0; idx_1 = 0; puts("Enter Your Program"); read(0, input, 8uLL); byte_4058 = 0; v4 = sub_136D(); printf("Result: %d\n", (unsigned int)v4); if ( v4 > 99999 ) { lineptr = 0LL; n = 0LL; stream = fopen("./flag", "r"); getline(&lineptr, &n, stream); printf("Good, get the flag: %s", lineptr); free(lineptr); fclose(stream); } return 0LL; } ``` ``` c __int64 sub_136D() { unsigned int v0; // eax unsigned int v2; // [rsp+8h] [rbp-8h] int v3; // [rsp+Ch] [rbp-4h] v2 = 0; while ( 1 ) { // open while ( 1 ) { v3 = 0; ++v2; v0 = input[idx_1]; if ( v0 > 0x72 ) { LABEL_17: puts("No Hack!"); exit(-1); } if ( input[idx_1] < 0x61u ) break; switch ( input[idx_1] ) { case 'a': if ( !byte_4060[idx_2] ) goto LABEL_18; idx_1 = 0; break; case 'e': v3 = 1; goto LABEL_18; case 'l': if ( --idx_2 < 0 ) idx_2 = 0; goto LABEL_18; case 'n': goto LABEL_18; case 'r': if ( ++idx_2 > 7 ) idx_2 = 7; goto LABEL_18; default: goto LABEL_17; } } // close if ( v0 == '+' ) { ++byte_4060[idx_2]; } else { if ( v0 != '-' ) goto LABEL_17; --byte_4060[idx_2]; } LABEL_18: if ( v3 ) return v2; if ( ++idx_1 > 7 ) idx_1 = 7; } } ``` Chương trình bắt nhập một chuỗi chỉ gồm các kí tự 'nalr+e-' và chuỗi này có độ dài tối đa là 8. Từ các kí tự này nó sẽ thực hiện thao tác gì đó, mỗi lần loop nó tăng biến đếm v4, mục tiêu của chúng ta là làm cho v4 > 99999 thì nó nhả flag. Sau khi đọc hàm trên thì ta thấy kí tự cuối cùng của chuỗi chắc chắn là chữ 'e'. Giờ mình có 2 hướng: - Brute force 7 kí tự còn lại, đpt O(6 ^ 7), nhưng thực tế là ta phải tương tác với chương trình nên độ phức tạp này tăng lên rất nhiều lần :D - Tự craft chuỗi sao cho nó thỏa mãn là được, cách này khá khó vì chương trình có kiểm tra giới hạn thời gian. Bằng một phép màu nào đó mình đã tìm ra được chuỗi thỏa mãn là +r++alae... ## ROVM Link chall: https://dreamhack.io/wargame/challenges/113 Bài này debug + đọc assembly khá là nhọc nhằn nên mình sẽ không nói quá chi tiết. Mình có viết script để dịch file chain sang assembly. ``` python #!/usr/bin/python3 from capstone import Cs, CS_ARCH_X86, CS_MODE_64 from pwn import * mp = {} def disassemble_code(binary_code) -> None: disassembler = Cs(CS_ARCH_X86, CS_MODE_64) for instruction in disassembler.disasm(binary_code, 0x1224000): mp[f'0x{instruction.address:07x}'] = f'{instruction.mnemonic} {instruction.op_str}' # print(f"0x{instruction.address:08x}: {instruction.mnemonic} {instruction.op_str}") return None opcode = open("opcode", "rb").read() disassemble_code(opcode) chain = open("chain", "rb").read() for i in range(len(chain) // 8): x = hex(u64(chain[8 * i: 8 * i + 8])) print(hex(0x1225000 + 8 * i), end = ": ") print(x, end = "") if x in mp: print(':', end = "") idx = u64(chain[8 * i: 8 * i + 8]) while True: if hex(idx) in mp: asm_code = mp[hex(idx)] print(f' {asm_code};', end = "") if "ret" in asm_code: break idx += 1 print() else: value = u64(chain[8 * i: 8 * i + 8]) print() ``` Giờ có code assembly của file chain rồi, kết hợp cả rev chay và debug thì nó dễ hơn mỗi debug đúng không. Hướng tiếp theo của mình đã quá rõ ràng rồi nên mình cũng lười nói tiếp hehe =)))) ## Theori of Relativity Link chall: https://dreamhack.io/wargame/challenges/638 Bài này thuộc dạng flag checker cơ bản thôi. Hàm main: ``` c __int64 __fastcall main(int a1, char **a2, char **a3) { char s[104]; // [rsp+0h] [rbp-70h] BYREF unsigned __int64 v5; // [rsp+68h] [rbp-8h] v5 = __readfsqword(0x28u); printf("Key: "); fgets(s, 100, stdin); s[strcspn(s, "\n")] = 0; if ( (unsigned __int8)check(&byte_6020, s) ) printf("Congrats! Flag is DH{%s} ...if you didn't cheat!\n", s); else puts("Hmm... :thinking_face:"); return 0LL; } ``` ``` c bool __fastcall check(_BYTE *a1, unsigned __int8 *a2) { unsigned __int8 v5; // [rsp+12h] [rbp-2Eh] unsigned __int8 v6; // [rsp+13h] [rbp-2Dh] char i; // [rsp+18h] [rbp-28h] __int128 v8; // [rsp+20h] [rbp-20h] v8 = 0uLL; for ( i = 0; ; ++i ) { v5 = *a1 + i * i; v6 = *a2; if ( !__popcnt(v5) ) break; if ( (i & 1) != 0 ) v8 += (unsigned __int8)(v6 ^ v5); else v8 += (unsigned __int8)(v5 - v6); ++a1; ++a2; } return (int)__popcnt(v6) + v8 == 0; } ``` Ta biết rằng popcnt >= 0 và giá trị unsigned luôn >= 0. Nên để biểu thức thỏa mãn thì chỉ có v8 = 0 và popcnt(v6) = 0 (uhm vì v5 lúc đó = 0 nên giá trị cuối v6 = 0 thì cũng là đương nhiên). Nghĩa là ở mọi vòng lặp v6 = v5, ta viết script đơn giản như sau: ``` python a1 = bytes.fromhex('...') for i in range(len(a1)): v5 = (a1[i] + i * i) & 0xff if v5 == 0: break print(chr(v5), end = "") ``` Nhưng để ý kĩ thì có 2 hàm init được gọi trước main: ``` c++ __int64 init_func1() { if ( ptrace(PTRACE_TRACEME, 0LL, 0LL, 0LL) >= 0 ) return (unsigned int)(unsigned __int8)byte_6020-- - 1; else return (unsigned int)(unsigned __int8)byte_6020++ + 1; } ``` Nếu không debug thì nó trả về nhánh trên, nếu có debug thì trả về nhánh dưới. ``` c++ unsigned __int64 init_func2() { _BYTE *v0; // rax int i; // [rsp+4h] [rbp-2Ch] _BYTE *v3; // [rsp+8h] [rbp-28h] __int64 v4[3]; // [rsp+10h] [rbp-20h] unsigned __int64 v5; // [rsp+28h] [rbp-8h] v5 = __readfsqword(0x28u); v4[0] = (__int64)init_func1; v4[1] = (__int64)init_func2; v4[2] = (__int64)check; for ( i = 0; i <= 2; ++i ) { v3 = (_BYTE *)v4[i]; while ( *v3 != 0xC3 ) { v0 = v3++; if ( *v0 == 0xCC ) { byte_6020 += 2; return v5 - __readfsqword(0x28u); } } } byte_6020 -= 2; return v5 - __readfsqword(0x28u); } ``` 0xc3 chính là opcode của ret, vậy là vòng lặp while đó có ý nghĩa duyệt từ đầu tới cuối hàm. Còn 0xcc thì nếu ta đặt breakpoint ở hàm đó rồi debug nó thì sẽ có thêm int3 (0xcc) ở hàm đó, nếu chúng ta debug mà không để ý hàm này thì sẽ bị sai. Vậy giờ mình rev hết thì chỉ cần a1[0] -= 3 (a1 = byte_6020) là giải quyết được vấn đề... Ok nhưng nó vẫn sai là sao ??? Tới khúc này mình đặt breakpoint trước khi thực hiện cả 2 hàm init đó. Mình nhận thấy mảng byte_6020 có giá trị hoàn toàn khác với lúc chúng ta không debug... ![image](https://hackmd.io/_uploads/SkI93OF4Jx.png) Ok vì mình đặt breakpoint trước khi 2 hàm init kịp làm gì, nên mảng của mình sẽ còn nguyên vẹn chưa bị tác động gì. Script của mình: ```python a1 = bytearray(bytes.fromhex('...')) a1[0] -= 3 for i in range(len(a1)): v5 = (a1[i] + i * i) & 0xff if v5 == 0: break print(chr(v5), end = "") ``` Sau khi làm xong, mình đọc writeup của người khác thì biết vấn đề nằm ở ELF RELA Relocation Table, nếu mình chỉ dựa vào static analysis thôi thì sẽ không giải được bài này, mình cũng học được cách giấu giá trị ở ELF RELA Relocation Table =)))