# WOLVCTF 2024 WRITE UP REVERSE Credit: Trohan0x00 ## 1. BabyRE * Bài này dành cho beginner nên ta chỉ cần strings hoặc quăng vô IDA để disas và search strings là ra flag > wctf{n1c3_oNe_y0u_Found_m3} ## 2. Shredded * Đề cho ta 3 file `shred.py`, `output.txt` và `out.zip`. Tiến hành unzip `out.zip` trước ta sẽ được thêm 1 file `output.txt` và 32 file `shred.txt` ![image](https://hackmd.io/_uploads/B1GWsnr0p.png) * Đọc đề bài thì ta cũng biết được file source gốc đã bị xé và trộn thành nhiều file nhỏ như này theo từng cột dộc. * Ta có thể sắp lại các file bằng tay vì ta biết được source được code bằng C nên chắc chắn phải khai báo thư viên `#include <stdio.h` vì thế kí tự đầu tiên của cột đầu tiên chắc chăn là `#` và ta ghép lần lượt các file ta sẽ có thứ tự các file như sau ``` 2, 4, 18, 31, 19, 21, 13, 5, 12, 30, 27, 28, 25, 9, 16, 6, 26, 24, 17, 29, 11, 14, 1, 3, 15, 7, 32, 0, 20, 23, 10, 8, 22 ``` ``` #include<stdio.h> #include<string.h> intmain(){ charflag[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"; charinter[51]; intlen=strlen(flag); for(inti=0;i<len;i++){ inter[i]=flag[i]; } for(inti=len;i<50;i++){ inter[i]=inter[(i*2)%len]; } inter[50]='\0'; chara; for(inti=0;i<50;i++){ a=inter[i]; inter[i]=inter[((i+7)*15)%50]; inter[((i+7)*15)%50]=a; } for(inti=0;i<50;i++){ a=inter[i]; inter[i]=inter[((i+3)*7)%50]; inter[((i+3)*7)%50]=a; } for(inti=0;i<50;i++){ inter[i]=inter[i]^0x20; inter[i]=inter[i]^0x5; } for(inti=0;i<50;i++){ a=inter[i]; inter[i]=inter[((i+83)*12)%50]; inter[((i+83)*12)%50]=a; } for(inti=0;i<50;i++){ printf("\\x%X",inter[i]); } return0; } ``` * Đây là chương trình dùng để encoded flag của chúng ta. Từ file output đã cho và code C đã được merge lại ta có thể dễ dàng reverse lại để lấy flag ban đầu ``` # include <stdio.h> int main() { unsigned char encrypted[] = {0x14, 0x5D, 0x14, 0x57, 0x16, 0x43, 0x46, 0x7A, 0x56, 0x16, 0x57, 0x17, 0x4B, 0x16, 0x52, 0x4C, 0x61, 0x1C, 0x1C, 0x7A, 0x1D, 0x7A, 0x11, 0x51, 0x52, 0x16, 0x5E, 0x62, 0x6D, 0x5E, 0x61, 0x7A, 0x16, 0x17, 0x61, 0x16, 0x6B, 0x61, 0x4E, 0x69, 0x14, 0x6B, 0x6D, 0x51, 0x57, 0x6D, 0x6D, 0x58, 0x5D, 0x4B}; int len = sizeof(encrypted) / sizeof(encrypted[0]); for(int i = 0; i < len; i++){ encrypted[i] = encrypted[i] ^ 0x5; encrypted[i] = encrypted[i] ^ 0x20; printf("%c", encrypted[i]); } return 0; } ``` * Ta được chuỗi `1x1r3fc_s3r2n3wiD99_8_4tw3{GH{D_32D3NDkL1NHtrHH}xn` ``` def reverse_xor(encrypted_bytes): return bytes([b ^ 0x5 ^ 0x20 for b in encrypted_bytes]) shuffled_hex = "54537F507661674763416D5D68484C697D5246714355424F646C4E6B62606E6A57406F73724B7775745170667C56494D444A" shuffled_bytes = bytes.fromhex(shuffled_hex) decrypted_bytes = reverse_xor(shuffled_bytes) decrypted_output = decrypted_bytes.decode('latin1') original_sequence = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx" positions = [decrypted_output.index(char) for char in original_sequence] encrypted_message = "1x1r3fc_s3r2n3wiD99_8_4tw3{GH{D_32D3NDkL1NHtrHH}xn" unshuffled_message = "".join(encrypted_message[positions[i]] for i in range(50)) print("Decrypted message:", unshuffled_message) ``` > wctf{sHr3DDinG_L1k3_H3NDr1x_93284}wt{H3Dn_13HNrx92 ## 3. doubledelete's revenge * Đề cho ta 2 file .enc và 1 file ELF, file .enc thì chưa flag sau khi đã được enc bằng chương trình. Tiến hành quăng ELF vào IDA để disas ![image](https://hackmd.io/_uploads/SyPMz6ICa.png) * Ở hàm main chương trình đơn giản với hai con trỏ là `*stream` và `s` tương đương trỏ vào input để đọc flag ban đầu và vào output để ghi flag sau khi được enc lúc này là file .enc * Ở bên dưới `if (argc == 3)` Kiểm tra xem số lượng đối số truyền vào là 3 hay không. `stream = fopen(argv[1], "r")` Mở tệp được chỉ định bởi đối số thứ nhất để đọc (mode "r") và `fread(ptr, 1uLL, 0x30uLL, stream)` Đọc 0x30 byte từ tệp đã mở và lưu vào mảng ptr. * Vòng lặp `for ( i = 0; i <= 0xB; ++i )` chạy từ 0 - 11 và thực hiện `ptr[i] = ROL4(ptr[i], 0xD)` để xoay trái bit với số lượng bit được chỉ định (0xD = 13 bits) vào từng phần tử của mảng ptr. ``` def rotr(value, bits): # rotate right return ((value & 0xffffffff) >> bits) | (value << (32 - bits) & 0xffffffff) def decrypt_flag(input_file_path, output_file_path): with open(input_file_path, "rb") as input_file: file_data = input_file.read() decrypted_data = bytearray() for i in range(0, len(file_data), 4): block = int.from_bytes(file_data[i:i+4], "little") decrypted_block = rotr(block, 13) decrypted_data.extend(decrypted_block.to_bytes(4, "little")) # convert back to bytes and> with open(output_file_path, "wb") as output_file: output_file.write(decrypted_data) if __name__ == "__main__": decrypt_flag("flag.txt.enc", "flag_decrypted.txt") ``` > wctf{i_th1nk_y0u_m1sund3rst00d_h0w_r0t13_w0rk5} ## 4. Assembled * Phân tích file prog bằng IDA. Bài này khá giống với giải HTB đợt trước ta chỉ chú ý kỹ một chút ở phần check Input của chương trình ![image](https://hackmd.io/_uploads/ryt4BjqC6.png) * Ban đầu yêu cầu user nhập input là flag vào để check. Trong hàm check input của người dùng được xử lý để kiểm tra tính chính xác, vòng lặp duyệt qua từng byte của đầu vào và thực hiện một loạt các phép toán bit sau mỗi lần lập sẽ thay đổi gía trị `ax` là 0x003148 dựa trên các điều kiện. Ta có được giá trị cuối là > 0x3148, 0x18a4, 0xc52, 0x629, 0x127c, 0x93e, 0x49f,0xdde, 0x6ef, 0x14ce, 0xa67, 0x1f36,0xf9b, 0x2ed2, 0x1769, 0x463c, 0x231e, 0x118f, 0x34ae, 0x1a57, 0x4f06, 0x2783, 0x768a, 0x3b45, 0xb1d0 * Sau đó chương trình lấy giá trị này xor với kí tự và compare với data. Nếu để ý kĩ thì nó chỉ cmp dil tức là 8 bit cuối vậy ta chỉ cần xor ngược lại và lấy flag ``` #include <iostream> #include <vector> #include <string> using namespace std; int main() { vector<int> data = {0xF2, 0x0F, 0x6C, 0xEC, 0x1A, 0xE2, 0x57, 0x70, 0x70, 0x86, 0xEA, 0xBA, 0xB5, 0x63, 0xCF, 0x8C, 0xF8, 0x0B, 0x6C, 0xE0, 0x6E, 0x15, 0x53, 0x45}; vector<int> key = {0x49, 0x89, 0xC6, 0x41, 0xB8, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8D, 0x1C, 0x25, 0x0E, 0x11, 0x40, 0x00, 0x66, 0xB8, 0x48, 0x31, 0xF9, 0xFF, 0xE3, 0x41, 0x88, 0xC1, 0x4D, 0x0F, 0xB6, 0xC9, 0x42, 0x8A, 0x0C, 0x0C, 0x43, 0x8A, 0x3C, 0x10, 0x48, 0x8D, 0x1D, 0x0A, 0x00, 0x00, 0x00, 0x4C, 0x8D, 0x0D, 0xDE, 0xFF, 0xFF, 0xFF, 0x41, 0xFF, 0xE1, 0x41, 0x8A, 0xB8, 0x83, 0x11, 0x40, 0x00, 0x40, 0x38, 0xF9, 0x0F, 0x85, 0x41, 0xFF, 0xFF, 0xFF, 0x66, 0xA9, 0x01, 0x00, 0x74, 0x18, 0x66, 0x89, 0xC3, 0x66, 0xD1, 0xE0, 0x66, 0x01, 0xD8, 0x66, 0xFF, 0xC0, 0x49, 0xFF, 0xC0, 0x49, 0x83, 0xF8, 0x18, 0x7C, 0xB5, 0x41, 0xFF, 0xE6, 0x66, 0xD1, 0xE8, 0x49, 0xFF, 0xC0, 0x49, 0x83, 0xF8, 0x18, 0x7C, 0xA6, 0x41, 0xFF, 0xE6, 0x66, 0x6C, 0x61, 0x67, 0x3F, 0x20, 0x63, 0x6F, 0x72, 0x72, 0x65, 0x63, 0x74, 0x21, 0x0A, 0x77, 0x72, 0x6F, 0x6E, 0x67, 0x20, 0x3A, 0x28, 0x0A, 0xF2, 0x0F, 0x6C, 0xEC, 0x1A, 0xE2, 0x57, 0x70, 0x70, 0x86, 0xEA, 0xBA, 0xB5, 0x63, 0xCF, 0x8C, 0xF8, 0x0B, 0x6C, 0xE0, 0x6E, 0x15, 0x53, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; vector<int> final_key = {0x3148, 0x18a4, 0xc52, 0x629, 0x127c, 0x93e, 0x49f, 0xdde, 0x6ef, 0x14ce, 0xa67, 0x1f36, 0xf9b, 0x2ed2, 0x1769, 0x463c, 0x231e, 0x118f, 0x34ae, 0x1a57, 0x4f06, 0x2783, 0x768a, 0x3b45, 0xb1d0}; string flag; for (int i = 0; i < data.size(); ++i) { flag += char(key[final_key[i] & 0xff] ^ data[i]); } cout << flag << std::endl; return 0; } ``` > wctf{h4ppy_d3c0mp1l1ng!} ## 5. befudged up * Ta được 3 file `befunge.py`, `runner.py` và `prog.befunged` ta check file `runnet.py` ``` from befunge import read_in, befunge prog = read_in('prog.befunge') print('flag? ') befunge(prog) print('') ``` * đoạn mã trên đọc befunge từ file `prog.befunge` và check điều kiện nếu đúng từ file befunge sẽ in ra sai hoặc đúng ``` vRRRRRRRR >>>>>>>>>>>>>>>>>>>v >>v ^ 1 ^ -# ^ : ^v_$ > >00+70pv ^>v v<<<<<<< ^ 1 ^ -# ^ : ^v_$ > >70gv ^>v v<<<< ^ 1 ^ -# ^ : ^v_$ >20pv ^>v v<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*4+8*5+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : v < ^v_$ >06+^v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >~20pv ^>v v<<<<< ^ 1 ^ -# ^ : v < ^v_$ >01+8*0+^v ^>v v<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >10gv ^>v v<<<< ^ 1 ^ -# ^ : ^v_$ >80pv ^>v v<<<< ^ 1 ^ -# ^ : ^v_$ >70gv ^>v v<<<< ^ 1 ^ -# ^ : ^v_$ >10pv ^>v v<<<< ^ 1 ^ -# ^ : v < ^v_$ >01+8*5+^v ^>v v<<<<<<<<< ^ 1 ^ -# ^ : v < ^v_$ >10g80g-!|v ^>v v<<<<<<<<<< ^ 1 ^ -# ^ : v < ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ > >01+80pv ^>v v<<<<<<< ^ 1 ^ -# ^ : ^v_$ >70g80g+70pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >05+8*5+80pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^ < ^v_$ >70g80g`!|v ^>v v<<<<<<<<<< ^ 1 ^ -# ^ : v < ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >@v ^>v v<< ^ 1 ^ -# ^ : ^v_$ > >01+8*0+8*0+40pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >00+30pv ^>v v<<<<<<< ^ 1 ^ -# ^ : ^v_$ > >10g40g/50pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g40g/60pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >50g60g+50pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >02+60pv ^>v v<<<<<<< ^ 1 ^ -# ^ : ^v_$ >50g60g%50pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >00+60pv ^>v v<<<<<<< ^ 1 ^ -# ^ : v < ^v_$ >50g60g-!|v ^>v v<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >30g40g+30pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ > >02+50pv ^>v v<<<<<<< ^ 1 ^ -# ^ : ^v_$ >40g50g/40pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >00+50pv ^>v v<<<<<<< ^ 1 ^ -# ^ : v < ^v_$ >40g50g-!|v ^>v v<<<<<<<<<< ^ 1 ^ -# ^ : ^ < ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ > >30gv ^>v v<<<< ^ 1 ^ -# ^ : ^v_$ >10pv ^>v v<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ > >10gv ^>v v<<<< ^ 1 ^ -# ^ : ^v_$ >30pv ^>v v<<<< ^ 1 ^ -# ^ : ^v_$ >01+10pv ^>v v<<<<<<< ^ 1 ^ -# ^ : ^v_$ > >00+40pv ^>v v<<<<<<< ^ 1 ^ -# ^ : v < ^v_$ >20g40g-!|v ^>v v<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >10g30g*10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >02+8*0+8*0+40pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >10g40g%10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >01+40pv ^>v v<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g40g-20pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^ < ^v_$ >^v ^>v v<< ^ 1 ^ -# < ^ : ^v_$ > >^v ^>v v<< ^ 1 ^ -# ^ : v < ^v_$ > >06+8*4+^v ^>v v<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ > >20pv ^>v v<<<< ^ 1 ^ -# ^ : ^v_$ >07+30pv ^>v v<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g30g+20pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g10g+20pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g10g+20pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20gv ^>v v<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*6+8*6+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >06+10pv ^>v v<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >05+8*5+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*7+8*3+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*2+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*4+8*5+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >07+8*3+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*7+8*6+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*2+8*1+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >04+8*6+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*3+8*2+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*0+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >04+8*1+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*0+8*4+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >03+8*2+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*1+8*1+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >03+8*6+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*2+8*2+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >05+8*0+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*6+8*6+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*3+8*1+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*2+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*7+8*6+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*4+8*5+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >02+8*2+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >05+8*6+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >02+8*1+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*6+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*4+8*3+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*5+8*2+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*3+8*1+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >03+8*7+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*3+8*6+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*3+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*5+8*1+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*5+8*1+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >05+8*6+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >04+8*4+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >02+8*6+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >05+8*6+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*2+8*1+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >06+8*0+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >01+8*2+8*5+10pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >03+8*1+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >05+8*4+10pv ^>v v<<<<<<<<<<< ^ 1 ^ -# < ^ : ^v_$ >^v ^>v v<< ^ 1 ^ -# ^ : ^v_$ > >01+8*4+8*3+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*5+8*7+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*6+8*2+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*6+8*2+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*4+8*5+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*4+8*3+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*6+8*4+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >04+8*1+20pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >@v ^>v v<< ^ 1 ^ -# ^ : ^v_$ >>01+8*5+8*1+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*5+8*6+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*4+8*3+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*5+8*7+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*6+8*2+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*6+8*2+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*4+8*5+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*4+8*3+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >01+8*6+8*4+20pv ^>v v<<<<<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >04+8*0+20pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >07+8*2+20pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >05+8*0+20pv ^>v v<<<<<<<<<<< ^ 1 ^ -# ^ : ^v_$ >20g,v ^>v v<<<<< ^ 1 ^ -# ^ : ^v_$ >@v ^>v v<< ``` ``` import sys def read_in(filename): GRID = [] r = 0 c = 0 row = [] with open(filename, 'r') as f: contents = f.read() for x in contents: if x == '\n': r += 1 c = 0 GRID.append(row) row = [] else: row.append(x) c += 1 # right pad with spaces maxlen = max(len(r) for r in GRID) for i in range(len(GRID)): GRID[i] += [' '] * (maxlen - len(GRID[i])) return GRID def befunge(GRID): PC = [0, 0] DIR = 0 DIR_DELTAS = [0, 1, 0, -1, 0] STACK = [] STRINGMODE = False MAXITER = 2**20 for _ in range(MAXITER): try: ch = GRID[PC[0]][PC[1]] if STRINGMODE: if ch == '"': STRINGMODE = False else: STACK.append(ord(ch)) else: match ch: case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9': STACK.append(int(ch)) case '+': a = STACK.pop() b = STACK.pop() STACK.append(a+b) case '-': a = STACK.pop() b = STACK.pop() STACK.append(b-a) case '*': a = STACK.pop() b = STACK.pop() STACK.append(a*b) case '/': a = STACK.pop() b = STACK.pop() STACK.append(b//a) case '%': a = STACK.pop() b = STACK.pop() STACK.append(b%a) case '!': a = STACK.pop() if a == 0: STACK.append(1) else: STACK.append(0) case '`': a = STACK.pop() b = STACK.pop() if b > a: STACK.append(1) else: STACK.append(0) case '>' | '^' | '<' | 'v': DIR = '>v<^'.index(ch) case '_': a = STACK.pop() if a == 0: DIR = 0 else: DIR = 2 case '|': a = STACK.pop() if a == 0: DIR = 1 else: DIR = 3 case '"': STRINGMODE = True case ':': STACK.append(STACK[-1]) case '\\': STACK[-2], STACK[-1] = STACK[-1], STACK[-2] case '$': STACK.pop() case '.': print(STACK.pop(), end=' ') case ',': print(chr(STACK.pop()), end='') case '#': PC[0] += DIR_DELTAS[DIR] PC[1] += DIR_DELTAS[DIR + 1] case 'p': y = STACK.pop() x = STACK.pop() v = STACK.pop() GRID[y][x] = chr(v) case 'g': y = STACK.pop() x = STACK.pop() STACK.append(ord(GRID[y][x])) case '~': x = sys.stdin.read(1) STACK.append(ord(x)) case '@': break case ' ': pass case _: print(f'Error: unknown character "{ch}"') break # end match PC[0] += DIR_DELTAS[DIR] PC[1] += DIR_DELTAS[DIR + 1] except Exception as e: print('oh no', e, PC) break ``` * `Hàm befunge` Thực hiện việc thực thi mã Befunge trên lưới ký tự đã được đọc từ tệp đầu vào. Lệnh match được sử dụng để xử lý các trường hợp khác nhau của ký tự trong lưới Befunge. * Các phép toán số học (+, -, *, /, %) được thực hiện trên các phần tử trên stack. * Các toán tử logic (!, `) kiểm tra và thao tác trên các giá trị trong STACK. * Các ký tự hướng (>, <, ^, v) thay đổi hướng di chuyển của con trỏ chương trình. * Các ký tự điều kiện (_, |) thay đổi hướng di chuyển dựa trên giá trị đầu tiên trong STACK. * Các ký tự khác như ", :, \, $, . vv., được sử dụng để thực hiện các hoạt động khác nhau như thao tác trên STACK, in ra giá trị hoặc ký tự, vv. * Ký tự @ kết thúc việc thực thi mã Befunge. * Ta sẽ tiến hành sửa file befunge ``` import sys def read_in(filename): GRID = [] r = 0 c = 0 row = [] with open(filename, 'r') as f: contents = f.read() for x in contents: if x == '\n': r += 1 c = 0 GRID.append(row) row = [] else: row.append(x) c += 1 # right pad with spaces maxlen = max(len(r) for r in GRID) for i in range(len(GRID)): GRID[i] += [' '] * (maxlen - len(GRID[i])) return GRID def befunge(GRID): PC = [0, 0] DIR = 0 DIR_DELTAS = [0, 1, 0, -1, 0] STACK = [] STRINGMODE = False MAXITER = 2**20 for _ in range(MAXITER): try: ch = GRID[PC[0]][PC[1]] if STRINGMODE: if ch == '"': STRINGMODE = False else: STACK.append(ord(ch)) else: match ch: case '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9': print(f"STACK.append({int(ch)})") STACK.append(int(ch)) case '+': a = STACK.pop() b = STACK.pop() print(f"STACK.append({a}+{b}) = {a+b}") STACK.append(a+b) case '-': a = STACK.pop() b = STACK.pop() print(f"STACK.append({b}-{a}) = {b-a}") STACK.append(b-a) case '*': a = STACK.pop() b = STACK.pop() print(f"STACK.append({a}*{b}) = {a*b}") STACK.append(a*b) case '/': a = STACK.pop() b = STACK.pop() print(f"STACK.append({b}//{a}) = {b//a}") STACK.append(b//a) case '%': a = STACK.pop() b = STACK.pop() print(f"STACK.append({b}%{a}) = {b%a}") STACK.append(b%a) case '!': a = STACK.pop() if a == 0: print(f"if {a} == 0 :STACK.append(1)") STACK.append(1) else: print(f"if {a} != 0 :STACK.append(0)") STACK.append(0) case '`': a = STACK.pop() b = STACK.pop() if b > a: print(f"if {b} > {a} :STACK.append(1)") STACK.append(1) else: print(f"if {b} <= {a} :STACK.append(0)") STACK.append(0) case '>' | '^' | '<' | 'v': DIR = '>v<^'.index(ch) print(f"'>v<^'.index(ch) = {DIR}") case '_': a = STACK.pop() if a == 0: print(f"if {a} == 0 : DIR = 0") DIR = 0 else: print(f"if {a} != 0 : DIR = 2") DIR = 2 case '|': a = STACK.pop() if a == 0: print(f"if {a} == 0 : DIR = 1") DIR = 1 else: print(f"if {a} != 0 : DIR = 3") DIR = 3 case '"': STRINGMODE = True print("STRINGMODE = True") case ':': keke = STACK[-1] STACK.append(keke) print(f"STACK.append({keke})") case '\\': STACK[-2], STACK[-1] = STACK[-1], STACK[-2] print(f"swap()") case '$': print(" STACK.pop()") STACK.pop() case '.': print("STACK.pop(): ",STACK.pop()) case ',': print("chr(STACK.pop()): ",chr(STACK.pop())) case '#': print(f"{PC[0]} += {DIR_DELTAS[DIR]}") print(f"{PC[1]} += {DIR_DELTAS[DIR + 1]}") PC[0] += DIR_DELTAS[DIR] PC[1] += DIR_DELTAS[DIR + 1] case 'p': y = STACK.pop() x = STACK.pop() v = STACK.pop() GRID[y][x] = chr(v) print(f"GRID[{y}][{x}] = {chr(v)}") case 'g': y = STACK.pop() x = STACK.pop() STACK.append(ord(GRID[y][x])) print(f"STACK.append(ord(GRID[{y}][{x}])) = {ord(GRID[y][x])}") case '~': x = sys.stdin.read(1) STACK.append(ord(x)) print(f"Taking input: STACK.append({ord(x)})") case '@': break case ' ': pass case _: print(f'Error: unknown character "{ch}"') break # end match PC[0] += DIR_DELTAS[DIR] PC[1] += DIR_DELTAS[DIR + 1] except Exception as e: print('oh no', e, PC) break ``` * File này mình sẽ đổi lại một tí ở phần xử lý input đầu vào và thao tác trên lưới * Khi gặp ký tự ~, chương trình sẽ in ra thông báo rằng nó đang lấy đầu vào từ bàn phím và giá trị được đưa vào STACK. * Khi con trỏ chương trình di chuyển đến một ô trống, chương trình sẽ in ra thông tin về cách con trỏ chương trình thay đổi vị trí. * Thao tác trên Lưới: Khi gặp các lệnh p và g, chương trình sẽ in ra thông tin về cách mà các giá trị trên lưới được thay đổi hoặc truy xuất. * Dựa vào file befunge ta có scrip để brute các char từ hàm check input ở befunge ``` import subprocess command = "python runner.py" input_data = "wctf{" max = 6 brute_char = "}abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_~!@#$%^&*()_+-=?><.," while input_data.find('}') == -1: for i in range(len(brute_char)): input_data +=brute_char[i] + 'A' command = "python runner.py" process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) output, error = process.communicate(input_data.encode()) count = output.decode().count("Taking input") if count > max: max = count input_data = input_data[:-1] print("real:",input_data) break else: input_data = input_data[:-2] ``` * Chờ một tí sẽ có flag ![image](https://hackmd.io/_uploads/BJtH9eOAa.png) > wctf{pr30ccup13d_w1+h_wh3+h3r_0r_n0t_1_c0uld} ## 6. Missing Resources * Ta được 1 file .exe nhưng khi run thì sẽ bị báo lỗi thiếu dll có lẽ đúng như tên bài là chall này sẽ bị thiếu vài file module và dll sau khi đọc thread disc thì mình lấy được các dll cần thiết để run chương trình ![image](https://hackmd.io/_uploads/SyGF_-uRT.png) * Một vấn đề nữa khi run thì chương trình sẽ bị out mà k trả cho ta bất kì output nào, có lẽ phải cần quăng IDA để disas ![image](https://hackmd.io/_uploads/rkuEcW_Ca.png) * Hàm check có lẽ là nguyên nhân gây outprocress ![image](https://hackmd.io/_uploads/rkXLqWuRp.png) * Error là một con trỏ không thay đổi trỏ đến một vị trí trong bộ nhớ có kiểu dữ liệu là char. Hàm gọi SDL_GetError() để lấy thông điệp lỗi từ SDL. Sau đó, hàm sử dụng SDL_LogError() để ghi thông điệp lỗi ra log. Có vẻ như %s sẽ được thay thế bằng thông điệp lỗi được lấy từ SDL_GetError(). Cuối cùng, hàm gọi exit(1) để kết thúc chương trình với mã lỗi 1. * Để patch ta chỉ cần sửa lại rax == 1 vì hàm check `test rax, rax` nếu bằng nhau bằng 0 sẽ out process ![image](https://hackmd.io/_uploads/HyKyiW_R6.png) * Chạy xuống dưới sẽ gặp một loạt các lệnh để xor từng giá trị tăng dần của v11 với các const * Ta chỉ cần chạy hết lệnh và check giá trị rcx sẽ lấy được flag ![image](https://hackmd.io/_uploads/Hyz7oW_Aa.png) > wctf{ch@mp1on_d11_sn1ff3r} ## 7. Game Graphic Debugging * Đề cho ta 1 file zip, unzip và lấy được file exe tiến hành run thử chương trình ![image](https://hackmd.io/_uploads/rkdoR3cCp.png) * Chương trình k hề có IO, mọi thứ ta có là một msgbox tên `The flag is somewhere inside me` và strings `ooh spinny` * Do k có IO nên thực hiện theo cách thông thường sẽ k khả thi và do là một file lớn nên việc phân tích từng phần cũng mất thời gian nên mình sẽ tận dụng những gì mình đang có * Quăng vào IDA và search tên msgbox `The flag is somewhere inside me` như chương trình đã hint ta được 1 hàm như sau ![image](https://hackmd.io/_uploads/S1LegTqRp.png) * Đoạn mã này có vẻ như là một phần của một chương trình xử lý tệp 3D Assimp. Hàm này có vẻ như dùng để cấp phát bộ nhớ. Hàm `sub_7FF7AC996890` được gọi với một đối số là một con trỏ trỏ đến một vị trí cụ thể trong vùng nhớ, cụ thể là vị trí bắt đầu của đối tượng v2 cộng thêm 4, có thể là để truy cập vào phần tử thứ 5 của đối tượng, bởi vì con trỏ này được tăng lên 4 * kích thước mỗi phần tử, ta sẽ kiểm tra thử ![image](https://hackmd.io/_uploads/H18Y-T50a.png) * Ta thấy đoạn mã thực hiện một loạt các phép toán XOR với các hằng số cụ thể trên các byte tại các vị trí liên tiếp trong vùng nhớ bắt đầu từ địa chỉ `v23`, ta sẽ debug để lấy data tại v23 thử ``` 0x77, 0x63, 0x74, 0x66, 0x7B, 0x79, 0x6F, 0x75, 0x72, 0x2D, 0x64, 0x33, 0x73, 0x63, 0x33, 0x6E, 0x74, 0x2D, 0x69, 0x6E, 0x74, 0x6F, 0x2D, 0x67, 0x61, 0x6D, 0x65, 0x64 ``` ![image](https://hackmd.io/_uploads/BkNyYTqRp.png) > wctf{your-d3sc3nt-into-gamedev-beg1ns} ## 8. Some flag is not an error * Ta có 1 file source code compile bằng C++ và 1 file bin ``` template <int x, int y> struct C { static constexpr int r = x; static constexpr int i = y; }; template <typename X, typename ...vals> struct V { using c = X; using n = V<vals...>; template <typename Y> using C = V<X,vals...,Y>; }; template <typename X> struct V<X> { using c = X; using n = void; template <typename Y> using C = V<X,Y>; }; template<> struct V<void> { using c = void; using n = void; template <typename Y> using C = V<Y>; }; using secret = V<C<438, 3190>, C<102, 2664>, C<58, 2712>, C<229, 2954>, C<219, 3452>, C<69, 2647>, C<311, 3002>, C<303, 2647>, C<284, 2988>, C<3, 3081>, C<830, 3274>, C<-170, 2991>, C<66, 2729>, C<123, 2948>, C<99, 2967>, C<55, 2881>, C<-50, 2920>, C<169, 3152>, C<204, 2551>, C<328, 2709>, C<-99, 2753>, C<184, 2620>, C<165, 2893>, C<253, 2711>, C<298, 2443>, C<195, 3000>, C<2, 2595>, C<-164, 3003>, C<555, 2977>, C<-404, 2749>, C<146, 3079>, C<283, 2578>>; template <unsigned i, unsigned a, unsigned b, unsigned c> struct G { static constexpr unsigned A = (a * 171) % 30269; static constexpr unsigned B = (b * 172) % 30307; static constexpr unsigned C = (c * 170) % 30323; static constexpr unsigned v = G<i-1, A, B, C>::v; }; template <unsigned a, unsigned b, unsigned c> struct G<0, a, b, c> { static constexpr unsigned A = (a * 171) % 30269; static constexpr unsigned B = (b * 172) % 30307; static constexpr unsigned C = (c * 170) % 30323; static constexpr unsigned v = (A + B + C) % 32; }; template <unsigned i> using GR = G<i, 12643, 29806, 187>; template <unsigned i> using GI = G<i, 3823, 25188, 24854>; template <typename T, unsigned i, unsigned j, unsigned k, typename ...vals> struct B { using M = B<T, i, j-1, k, vals..., C<GR<i*k+j>::v, GI<i*k+j>::v>>::M; }; template <typename T, unsigned j, unsigned k> struct B<T, 0, j, k> { using M = T; }; template <typename T, unsigned i, unsigned k, typename ...vals> struct B<T, i, 0, k, vals...> { using M = B<typename T::template C<V<vals...>>, i-1, k, k>::M; }; using W = B<V<void>, 32, 32, 32>::M; struct _a {static constexpr int r = 0, i = 1;}; struct _b {static constexpr int r = 1, i = 0;}; struct _c {static constexpr int r = 2, i = 0;}; struct _d {static constexpr int r = 1, i = 1;}; struct _e {static constexpr int r = 0, i = 2;}; struct _f {static constexpr int r = 3, i = 0;}; struct _g {static constexpr int r = 2, i = 1;}; struct _h {static constexpr int r = 1, i = 2;}; struct _i {static constexpr int r = 0, i = 3;}; struct _j {static constexpr int r = 4, i = 0;}; struct _k {static constexpr int r = 3, i = 1;}; struct _l {static constexpr int r = 2, i = 2;}; struct _m {static constexpr int r = 1, i = 3;}; struct _n {static constexpr int r = 0, i = 4;}; struct _o {static constexpr int r = 5, i = 0;}; struct _p {static constexpr int r = 4, i = 1;}; struct _q {static constexpr int r = 3, i = 2;}; struct _r {static constexpr int r = 2, i = 3;}; struct _s {static constexpr int r = 1, i = 4;}; struct _t {static constexpr int r = 0, i = 5;}; struct _u {static constexpr int r = 6, i = 0;}; struct _v {static constexpr int r = 5, i = 1;}; struct _w {static constexpr int r = 4, i = 2;}; struct _x {static constexpr int r = 3, i = 3;}; struct _y {static constexpr int r = 2, i = 4;}; struct _z {static constexpr int r = 1, i = 5;}; struct _0 {static constexpr int r = 0, i = 6;}; struct _1 {static constexpr int r = 7, i = 0;}; struct _2 {static constexpr int r = 6, i = 1;}; struct _3 {static constexpr int r = 5, i = 2;}; struct _4 {static constexpr int r = 4, i = 3;}; struct _5 {static constexpr int r = 3, i = 4;}; struct _6 {static constexpr int r = 2, i = 5;}; struct _7 {static constexpr int r = 1, i = 6;}; struct _8 {static constexpr int r = 0, i = 7;}; struct _9 {static constexpr int r = 8, i = 0;}; struct __ {static constexpr int r = 7, i = 1;}; template <int L, int R> struct S { static constexpr int v = L + R; }; template <int L, int R, int M> struct P { static constexpr int v = P<L&~M,R,M*2>::v+(L&M)*R; }; template <int R, int M> struct P<0, R, M> { static constexpr int v = 0; }; template <typename L, typename R> struct A { using r = C<S<L::r,R::r>::v, S<L::i,R::i>::v>; }; template <typename L, typename R> struct M { using r = C<S<P<L::r,R::r,1>::v,-P<L::i,R::i,1>::v>::v, S<P<L::r,R::i,1>::v,P<L::i,R::r,1>::v>::v>; }; template <typename L, typename R> struct D { using r = A<typename M<typename L::c,typename R::c>::r, typename D<typename L::n,typename R::n>::r>::r; }; template <> struct D<void, void> { using r = C<0, 0>; }; template <typename L, typename R> struct Z { using r = Z<typename L::n, R>::r::template C<typename D<typename L::c, R>::r>; }; template <typename R> struct Z<void, R> { using r = V<void>; }; template <typename L, typename R> struct E { static constexpr bool valid = (L::c::r == R::c::r) && (L::c::i == R::c::i) && E<typename L::n, typename R::n>::valid; }; template<> struct E<void, void> { static constexpr bool valid = true; }; template <typename ...Ts> struct wctf { static constexpr unsigned valid = E<typename Z<W, V<Ts...>>::r, secret>::valid; }; int main() { static_assert(wctf<_w, _h, _4, _t, _5, __, _t, _h, _3, __, _f, _l, _4, _g>::valid); } ``` * Để giải quyết bài này ta sẽ chia nhỏ ra từng phần và phân tích. Đầu tiên hai template struc `C` có trách nhiệm định nghĩa số phức(phần r và i) và `V` để lưu trữ các giá trị số phức dưới dạng `vecto` hoặc `linked list` ![image](https://hackmd.io/_uploads/rJ32NuhAT.png) ![image](https://hackmd.io/_uploads/HJj3fFn0a.png) * Đây là một template struct dùng để phát sinh các số ngẫu nhiên theo thuật toán PRNG của Wichmann-Hill. Thuật toán này sử dụng ba tham số a, b, và c để tính toán ra các giá trị ngẫu nhiên. Mỗi lần gọi, giá trị của a, b, và c được tính toán lại theo các công thức tương ứng. * Kết quả của mỗi lần gọi là v, và lần gọi tiếp theo sẽ sử dụng giá trị v của lần trước. Quá trình này lặp lại cho đến khi i đạt đến 0, khi đó giá trị cuối cùng của v sẽ được trả về. ## 9. Maize * Bài này sau khi được sự hỗ trợ của anh Sơn thì mình đã nắm được cơ bản, ta được 1 file elf check main ta được như sau ``` __int64 __fastcall MEMORY[0x55E00304FD36]() { int v0; // eax int v2[20]; // [rsp+10h] [rbp-80h] char s1[35]; // [rsp+60h] [rbp-30h] BYREF char v4; // [rsp+83h] [rbp-Dh] int k; // [rsp+84h] [rbp-Ch] int j; // [rsp+88h] [rbp-8h] int i; // [rsp+8Ch] [rbp-4h] sub_55E00304F175(); v2[0] = 0x30; v2[1] = 0x86; v2[2] = 5; v2[3] = 0xEC; v2[4] = 0xDC; v2[5] = 0x95; v2[6] = 0xD2; v2[7] = 0x65; v2[8] = 0x4D; v2[9] = 0xDC; v2[0xA] = 0x6F; v2[0xB] = 0x44; v2[0xC] = 0x17; v2[0xD] = 0xBA; v2[0xE] = 0x69; v2[0xF] = 0x51; v2[0x10] = 0x9C; v2[0x11] = 0x42; v2[0x12] = 0x30; v2[0x13] = 0; printf("flag: "); __isoc99_scanf("%31s", s1); if ( strncmp(s1, "wctf{", 5uLL) ) { puts("No, not that, I need a FLAG."); exit(0); } for ( i = 0; i <= 0x13; ++i ) { s1[i + 5] ^= v2[i]; } for ( j = 5; s1[j] != 0x7D && s1[j]; ++j ) { v4 = s1[j]; for ( k = 0; k <= 3; ++k ) { v0 = (v4 >> (2 * k)) & 3; if ( v0 == 3 ) { sub_55E00304F73A(3u); } else if ( v0 == 2 ) { sub_55E00304F73A(2u); } else if ( v0 ) { sub_55E00304F73A(0); } else { sub_55E00304F73A(1u); } if ( qword_55E003055070 == 0x260000005ALL ) { break; } } } if ( qword_55E003055070 == 0x260000005ALL && dword_55E003055058 == 0xDF ) { puts("Success."); } else { puts("better luck next time"); } return 0LL; } ``` * Chương trình yêu cầu người dùng nhập flag và check xem chuỗi có bắt đầu bằng "wctf{" hay không và XOR giữa các ký tự của chuỗi và các giá trị trong mảng v2. * Sau đó vòng lặp để kiểm tra các ký tự trong chuỗi sau khi đã thực hiện XOR. Trong vòng lặp này, các giá trị của các bit trong mỗi ký tự được sử dụng để gọi một hàm `55E00304F73A` để thực hiện quá trình di chuyển trong mê cung từ 0-3 tương ứng các chức năng `^v<>`. Chương trình sẽ di chuyển cho đết khi đạt đủ điều kiện ở if * Ta tiến hành debug với input là spam flag với format `wctf{...}` ![image](https://hackmd.io/_uploads/SkS38rhA6.png) * sau khi check giá trị ở `qword_55E003055070` thì ta biết được có lẽ checkpoint của ta là `2600000027h`, từ maze cho sẵn thì ta thấy được đi 2 step bên phải là khả thi mình sẽ sửa `EAX = 3` để đi về bên phải ![image](https://hackmd.io/_uploads/Sy0R8r30p.png) * Nhưng giá trị cuối cùng mình nhận được lại là `2800000027h` tức là tăng lên tận 2 step biết được khi check `dword_55E3C31B6058` là `2` * Sau khi xem wu từ anh Sơn và hint từ author thì mình biết được cách tính đường đi ngắn nhất từ BFS ![image](https://hackmd.io/_uploads/Syod0Bh06.png) * Vị trí S là nơi checkpoint ta bắt đầu, đi dần tới biên thì có hàm đệ quy kiểm tra và set lại các giá trị để nhảy sang các mặt khác của khối cube ``` unsigned __int64 __fastcall sub_55E3C31B0550(int a1, int a2) { __int64 v3; // [rsp+10h] [rbp-8h] if ( a1 == 0xFFFFFFFF ) { v3 = (unsigned int)a2 | 0x6700000000LL; } else if ( a2 == 0xFFFFFFFF ) { HIDWORD(v3) = 0x81 - a1; LODWORD(v3) = 0x1A; } else if ( a1 == 0x68 ) { v3 = (unsigned int)a2; } else if ( a2 == 0x4E ) { HIDWORD(v3) = 0x81 - a1; LODWORD(v3) = 0x33; } else if ( a1 <= 0x19 && a2 == 0x19 ) { v3 = (unsigned int)a1 | 0x1A00000000LL; } else if ( a1 <= 0x19 && a2 == 0x34 ) { HIDWORD(v3) = 0x1A; LODWORD(v3) = 0x4D - a1; } else if ( a1 == 0x19 && a2 <= 0x19 ) { HIDWORD(v3) = a2; LODWORD(v3) = 0x1A; } else if ( a1 == 0x19 && a2 > 0x33 ) { HIDWORD(v3) = 0x4D - a2; LODWORD(v3) = 0x33; } else if ( a1 == 0x34 && a2 <= 0x19 ) { HIDWORD(v3) = 0x4D - a2; LODWORD(v3) = 0x1A; } else if ( a1 == 0x34 && a2 > 0x33 ) { HIDWORD(v3) = a2; LODWORD(v3) = 0x33; } else if ( a1 > 0x33 && a1 <= 0x4D && a2 == 0x19 ) { HIDWORD(v3) = 0x33; LODWORD(v3) = 0x4D - a1; } else if ( a1 > 0x33 && a1 <= 0x4D && a2 == 0x34 ) { v3 = (unsigned int)a1 | 0x3300000000LL; } else if ( a1 > 0x4D && a2 == 0x19 ) { HIDWORD(v3) = 0x81 - a1; LODWORD(v3) = 0; } else if ( a1 > 0x4D && a2 == 0x34 ) { HIDWORD(v3) = 0x81 - a1; LODWORD(v3) = 0x4D; } else { v3 = __PAIR64__(a1, a2); } return __PAIR64__(v3, HIDWORD(v3)); } ``` * Sau khi rotate các mặt và tìm được đi đúng từ hint của author và anh Sơn thì mình có script để xor lấy giá trị flag ra ``` m1 = [[3, 0, 3, 1], [3, 0, 3, 3], [3, 1, 2, 1], [3, 3, 1, 3], [3, 0, 0, 2], [0, 0, 2, 2], [2, 0, 2, 2], [0, 0, 3, 0], [3, 0, 2, 0], [3, 0, 0, 2], [1, 2, 0, 0], [3, 0, 3, 1], [3, 0, 2, 0], [0, 3, 0, 2], [0, 2, 0, 0], [3, 1, 2, 1], [3, 3, 3, 3], [3, 1, 3, 1], [3, 0, 0, 0], [2, 1, 3, 0]] xor_key = [48, 134, 5, 236, 220, 149, 210, 101, 77, 220, 111, 68, 23, 186, 105, 81, 156, 66, 48, 0] apb = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=<>,./{}[]\|~:;?"\'' flag = "" def check_character(char, xor_value, indices): a = ord(char) ^ xor_value return all(((a >> (2 * i)) & 3) == indices[i] for i in range(len(indices))) for xor_value, indices in zip(xor_key, m1): for char in apb: if check_character(char, xor_value, indices): flag += char break print(flag) ``` > wctf{Cub3_5pUn_f746a6c536}