## nope :::info * Đầu tiên, mình kiểm tra thông tin của binary và chạy nó. Kết quả cũng không thu được gì hay ho :grin:. ::: ![](https://hackmd.io/_uploads/HJUp41b03.png) :::info * Đây là hàm `main()` mình thu được khi dùng ghidra. Chức năng của nó là thực hiện so sánh kích thước của tham số đầu vào và`local_28` có bằng nhau hay không và nếu thỏa mãn thì gọi hàm `FUN_00101251()`, lưu ý là `local_28` có kích thước là 23 byte. Còn chức năng của `FUN_00101251()` khả năng cao là để xử lý tham số đầu vào, mình đoán vậy :grin:. * Sau khi `FUN_00101251()` hoàn thành, nếu như tham số đầu vào sau khi được xử lý giống như `local_28` thì ta sẽ thu được flag. ::: ![](https://hackmd.io/_uploads/BJG6Lte03.png) :::info * Hình dưới mô tả chức năng của `FUN_00101251()`. Chức năng của nó đơn giản là thay đổi giá trị của mỗi kí tự trong tham số dựa vào phép xor kí tự đó với vị trí của nó. Phép xor được thực hiện bởi `FUN_00101224()` :grin:. ::: ![](https://hackmd.io/_uploads/HyilvFlCn.png) ![](https://hackmd.io/_uploads/B1WzvFlCn.png) :::info * Oce, vậy là tới đây đơn giản rồi. Mình chỉ cần lấy mỗi kí tự trong `local_28` đem xor cho vị trí tương ứng của nó là thu được flag. Mình đã code một script nhỏ đề tìm flag bằng những suy đoán trên. Nhưng kết quả thì ... không đúng :thinking_face:. Hmm, vậy là có gì đó không đúng trong lúc mình reverse binary rồi :thinking_face:. ::: ![](https://hackmd.io/_uploads/ryoJOKgC3.png) :::info * Mình đành xem lại mã assembly để kiểm tra lại chức năng của binary thì thấy có điều khá hay ho ở đây :nerd_face:. Trong `FUN_00101224()`, ngoài việc thực hiện phép xor giữa hai tham số thì đầu vào thì nó còn tính toán giá trị R10B và đồng thời tăng R11 lên 1 :grin:. Lưu ý một điều là giá trị R10B được lấy từ địa chỉ R11 + 0x1 :grin:. ::: ![](https://hackmd.io/_uploads/Bk2EYFgA3.png) :::info * Sau khi hàm `FUN_00101224()` hoàn thành, mình thấy rằng R10B được gán vào địa chỉ trong R11 và sau đó giảm giá trị R9 lên :dizzy_face:. * Vậy thì chung quy lại, R9, R10, và R11 có ý nghĩa gì :thinking_face: ? Mình đành debug để tìm ra câu trả lời. ::: ![](https://hackmd.io/_uploads/HJv89FgRn.png) :::info * Mình đã chạy thử chương trình thì phát hiện ra điều cực kỳ quan trọng. Cụ thể là `R11 + 0x1 = RBX`, điều này có nghĩa là tại thời điểm trước khi được hiện lệnh `NEG` thì `R10B` chứa kí tự hiện tại đang được xét. Sau đó binary tiến hành tính toán giá trị cho `R10B` bằng cách thực hiện phép xor giữa số đối của chính nó với 1 byte giá trị tại địa chỉ trong `R9`. * Sau khi thực hiện xong phép xor thứ hai thì giá trị của `R11` được tăng lên 1, lúc này thì `R11` đúng bằng `RBX` :grin:. Hơn nữa là sau đó giá trị của `R10B` lại được gán cho địa chỉ trong `R11` cho nên phép xor lần thứ 2 mới thật sự thực hiện việc thay đổi trên chuỗi, còn lần xor đầu tiên chỉ đánh lừa mình thôi :grin:. * Ở dưới là mã giả biểu diễn thao tác xử lý chuỗi mà mình vừa giải thích. Trong đó s chính là chuỗi mà ta đang xử lý và `pad` chính là giá trị của một kí tự tại địa chỉ trong `R9`. ::: ```cpp= for(int i = 0; i < 23; i++){ s[i] = (-s[i]) ^ pad[23 - i + 1]; } ``` ![](https://hackmd.io/_uploads/Sk-chFlC2.png) :::info * Vì giá trị của `R9` được giảm đi trong quá trình xử lý chuỗi nên mình phải lấy 23 byte giá trị ngược về trước của `R9` trong lần lặp đầu để thực hiện phép xor cho các lần lặp tiếp theo :grin: ::: ![](https://hackmd.io/_uploads/H131pteC3.png) :::info * Sau khi đã có được `pad` và `local_28`, mình viết script python theo mã giả mà mình vừa đề cập để tìm flag. Kết quả là mình thu được flag `gigem{fUnky_1nlin3_4sm}` ::: ```python= from pwn import* target = p64(0x6926297d71706e73) + p64(0x692e68647f2a7d67) + p64(0x3e2f343275707c) xor_val = b"\xea\xf9\xe9\xea\xee\xac\xbc\xc2\xf5\xe8\xad\xde\xab\xfa\xba\xfe\xee\xbd\xd4\xfe\xb9\xbc\xbd" def convert(original_value): complement = (~original_value) & 0xff twos_complement = (complement + 1) & 0xff return twos_complement flag = '' for i in range(len(target) - 1): flag += chr(convert(target[i] ^ xor_val[i])) print(flag) ``` ![](https://hackmd.io/_uploads/H1EPptxR3.png) ## nothing :::info * Đây là hàm `main()` mình thu được trong quá trình reverse. Nó in ra một chuỗi và tiếp tục gọi hàm `FUN_00101155()`. ::: ![](https://hackmd.io/_uploads/B1gEAFx0n.png) :::info * Chức năng của `FUN_00101155()` cũng in ra một chuỗi và đồng thời trả về giá trị `param_1 + 0x400000`. * Trong quá trình reverse, mình đã kiểm tra hết tất cả các hàm trong binary nhưng không thấy có gì đặc biệt cả, đúng như tên challenge `nothing` :dizzy_face:. * Lúc này mình để ý đến giá trị `param_1 + 0x400000`, mình quyết định debug binary để kiểm tra giá trị tại địa chỉ này :grin:. ::: ![](https://hackmd.io/_uploads/ry_iCYg02.png) :::info * Oh wao, tại địa chỉ này là một loạt các câu lệnh :thinking_face:. Lúc này mình tự thắc mắc là tại sao trong lúc reverse mình lại không phát hiện ra nó mặc dù mình đã kiểm tra rất kỹ :thinking_face:. ::: ![](https://hackmd.io/_uploads/rJRKk9xRh.png) :::info * Mình dùng ghidra để kiểm tra tại offset `0x11ab` trong binary thì thấy nó là một dãy byte rác :grinning:. Hóa ra binary này đã che dấu một loạt các câu lệnh bằng byte rác :grin:. Oce, lần sau trong quá trình reverse mình sẽ để ý các byte rác này :grin:. ::: ![](https://hackmd.io/_uploads/H1WTJ5lA2.png) :::info * Mình disassembly lại các byte này và đồng thời định nghĩa hàm cho nó thì thu được đoạn code như sau. Nhìn code khá rối nhưng chức năng của nó khá đơn giản, nó thực hiện thay đổi chuỗi bằng cách xor mỗi kí tự của chuỗi với vị trí tương ứng của nó trong chuỗi. ::: ![](https://hackmd.io/_uploads/Hy0xxqgR2.png) :::info * Đây là script python nho nhỏ có chức năng giống như đoạn code trên để tìm ra flag :grin:. Flag thu được là `gigem{key_you_found_the_program}` ::: ```python= from pwn import* s = "ghefi~nbqVsdyRh`e\177vL`}sHhku|n|sb" flag = '' for i in range(len(s)): flag+=chr(ord(s[i])^i) print(flag) ``` ![](https://hackmd.io/_uploads/ByxdgqxAn.png) ## nothing2 :::info * Tiếp theo là challenge `nothing-2`, rút kinh nghiệm ở challenge trước thì lần này mình sẽ cẩn thận hơn trong quá trình reverse :smiling_face_with_smiling_eyes_and_hand_covering_mouth:. * Đầu tiên mình cũng chạy thử binary và kiểm tra thông tin của nó :grin:. ::: ![](https://hackmd.io/_uploads/BySH-clCh.png) :::info * Đây là hàm `main()` của binary, ngoài việc in ra một chuỗi thì nó không chứa gì cả. Thì cũng đúng thôi, nếu nó chứa gì hay ho thì đã không gọi là `nothing-2` :smiling_face_with_smiling_eyes_and_hand_covering_mouth:. ::: ![](https://hackmd.io/_uploads/S1-fWql03.png) :::info * Mình kiểm tra binary thì phát hiện ra các byte rác, theo kinh nghiệm của bài trước thì đây có thể là các câu lệnh thực hiện chức năng gì đó :smiling_face_with_smiling_eyes_and_hand_covering_mouth:. ::: ![](https://hackmd.io/_uploads/H1J_Mcl02.png) :::info * Mình cũng tiến hành disassembly và định nghĩa hàm cho byte rác thì thu được phần đầu của hàm như hình dưới. * Nó thực hiện gán giá trị cho chuỗi tại địa chỉ `unaff_RBP + -0x50`. ::: ![](https://hackmd.io/_uploads/Byohf9gR3.png) :::info * Tiếp theo nó thực hiện xử lý chuỗi :grinning:. ::: ![](https://hackmd.io/_uploads/ByMlm9xAh.png) :::info * Để dễ hiểu thì mình đã tóm gọn lại đoạn code trên bằng script python ở dưới. Chức năng của nó là tìm kiếm vị trí xuất hiện đầu tiên của các kí tự của chuỗi s1 trong chuỗi s2 và sau đó dùng các vị trí đó làm index trong chuỗi alphabet để tìm ra flag. Flag mà mình thu được sau khi chạy script này là `gigem{c0nstruct0r5_run_b3f0r3_m41n}` ::: ```py= from pwn import* alphabet = "abcdefghijklmnopqrstuvwxyz1234567890{}_" s1 = '4piq9zovafg8{1hkcm7std03xle}ry6w_ujn52b' s2 = 'oao9{5in17smtisnm6bmt1bprznmrb{ye12' pos = [] for i in range(len(s2)): pos.append(s1.find(s2[i])) flag = '' for i in range(len(pos)): flag+=alphabet[pos[i]] print(flag) ``` ![](https://hackmd.io/_uploads/r1msXceCh.png) ## eyepatch :::info * Ở challenge này, nó cung cấp cho mình một server ở port 8000. Nhiệm vụ của mình là phải submit file cho nó, mình cũng chưa biết là phải nên nộp file gì :thinking_face:. ::: ![](https://hackmd.io/_uploads/S1c2E5gRn.png) :::info * Ngoài ra nó còn cho mình hai file, một binary và một file chứa kết quả mong đợi. Khi mình chạy binary thì kết quả thu được khác so với file chứa kết quả mong đợi. * Oce, tới đây thì mình chỉ cần chỉnh sửa chức năng của binary để khi chạy nó lên thì kết quả thu được giống với file chứa kết quả mong đợi là được. Khá dễ hiểu đúng không :grin:. ::: ![](https://hackmd.io/_uploads/HyY-B9g0n.png) :::info * Đây là hàm `main()` mà mình thu được sau khi thực hiện reverse binary, nó gọi 3 hàm `fib()`, `int_relu()`, và `det()` và sau đó in kết quả của 3 hàm này ra màn hình. ::: ![](https://hackmd.io/_uploads/S1DwBclA2.png) :::info * Ở hình `fib()`, mình thấy nó sai ở chỗ `iVar2 = 2` khi `param_1 = 1` bởi vì số fibonacci thứ nhất bằng 1 chứ không phải bằng 2. ::: ![](https://hackmd.io/_uploads/ry9HIcxR3.png) :::info * Tiếp theo là hàm `int_relu()`, ta phải sửa lại điều kiện `-1 < param_1` thành `0 > param_1` thì khi param_1 có giá trị 42 thì giá trị trả về của hàm là 42 :grin:. ::: ![](https://hackmd.io/_uploads/S13YLcl03.png) :::info * Tiếp theo là `det()`, hàm này dùng để tính định thức của ma trận. Để công thức tính định thức đúng thì dấu cộng phải đổi thành dấu trừ :grin:. ::: ![](https://hackmd.io/_uploads/HJzzwcl0h.png) :::info * Oce, sau khi mình dùng ghidra để patching binary thì mình chạy thử nó. Kết quả thu được giống như file chứa kết quả mong đợi. ::: ![](https://hackmd.io/_uploads/rk2sw9xR2.png) :::info * Mình up binary này cho trong web thì thu được flag là `gigem{i_hope_you_didnt_pwn_our_infra}` ::: ![](https://hackmd.io/_uploads/r19xO5xR2.png) ## flag-encryptor :::info * Challenge này cho mình một binary và một file .png :thinking_face:. ::: ![](https://hackmd.io/_uploads/Bym51_gAn.png) :::info * Mình thử mở file .png này lên thử thì không được :thinking_face:. Dường như nó đã bị mã hóa. ::: ![](https://hackmd.io/_uploads/S1Za1_e0n.png) :::info * Như thường lệ thì mình kiểm tra thông tin của binary. ::: ![](https://hackmd.io/_uploads/S1nkldl02.png) :::info * Khi sử dụng lệnh strace thì mình phát hiện ra binary có dùng hàm `mprotect()` với cả 3 cờ `PROT_READ, PROT_WRITE, và PROT_EXEC`. Theo kinh nghiệm của mình thì đây là dấu hiệu của việc thay đổi mã của binary trong lúc runtime. ::: ![](https://hackmd.io/_uploads/rkCmxOgRn.png) :::info * Hàm `main()` mình thu được trong quá trình reverse đầu tiên sẽ gọi hàm `mprotect()` để thay đổi permission tại vị trí 0x102000, vị trí này thuộc .text section. Sau đó, nó gọi `FUN_001011c5()` với tham số đầu vào là địa chỉ của `FUN_001026e1()` ::: ![](https://hackmd.io/_uploads/HyCf-_xC3.png) :::info * `FUN_001011c5()` sẽ thực hiện phép xor giữa các byte tại địa chỉ trong param_1. * Dựa vào các nhận xét trên thì mình dám chắc rằng hàm `FUN_001026e1()` đã bị mã hóa và nó sẽ được giải mã trong lúc binary thực thi. ::: ![](https://hackmd.io/_uploads/S1CVbdeA3.png) :::info * Mình càng chắc chắn nhận định trên khi kiểm tra `FUN_001025e1()`, nó dùng các lệnh rất kỳ lạ mình chưa thấy bao giờ và sau đó cũng chứa nhiều byte rác nữa :grin:. ::: ![](https://hackmd.io/_uploads/HyLVzOl02.png) :::info * Hình dưới hiển thị ra một số câu lệnh của `FUN_001025e1()` chưa được giải mã :grin:. ::: ![](https://hackmd.io/_uploads/S1AqVueCh.png) :::info * Còn lúc này thì `FUN_001025e1()` đã được giải mã rồi :grin:. ::: ![](https://hackmd.io/_uploads/B1LpVul0n.png) :::info * Code của `FUN_001025e1()` khá dài, mình có hiển thị ra ở đây :zany_face:. Việc reverse một đoạn assembly dài như thế này khá khó đối với mình, nên mình nghĩ đến việc dump đoạn code này. ::: ![](https://hackmd.io/_uploads/r1_fSOgCh.png) ![](https://hackmd.io/_uploads/BJjSr_l03.png) ![](https://hackmd.io/_uploads/HyAoB_gC2.png) :::info * Mình dùng lệnh dump memory trong GDB để thực hiện việc này. ::: ![](https://hackmd.io/_uploads/Sk4P8OgC3.png) ![](https://hackmd.io/_uploads/ryXkDuxR3.png) :::info * Để chắc chắn thì mình dùng lệnh objdump để disassembly dump file để xem mã assembly của nó :grin:. ::: ![](https://hackmd.io/_uploads/H1wnu_g0h.png) :::info * Tới đây thì mình chỉ cần xuất ra chuỗi hex từ file dump để chèn nó vào trong binary bằng ghidra :smiling_face_with_smiling_eyes_and_hand_covering_mouth:. ::: ![](https://hackmd.io/_uploads/Hk8QKux02.png) :::info * Hehe, đây là hàm thu được bởi decompiler của ghidra, nhìn nó có vẻ dễ đọc hơn đống mã assembly thuần túy. Chức năng của hàm này là đọc dữ liệu trong file `flag.png` để xử lý gì đó, mình sẽ đi phân tích từ từ để tìm ra chức năng của hàm này :thinking_face:. * Đầu tiên nó đọc dữ liệu của flag.png và lưu trữ nó tại địa chỉ của `pvStack_28` bằng hàm `fread()`, ngoài ra kích thước của `flag.png` còn được lưu tại biến `sStack_20` :grin:. Tiếp theo thì hàm `FUN_001014ff()` được gọi để làm gì đó, tham số truyền vào gồm `auStack_e8` và địa chỉ của `DAT_0010590`. ::: ![](https://hackmd.io/_uploads/H1tMn_eC3.png) :::info * Kiểm tra sơ bộ thì mình biết được `DAT_00105090` là địa chỉ lưu trữ 16 byte dữ liệu. ::: ![](https://hackmd.io/_uploads/BJvx1KgAn.png) :::info * Khi vào hàm `FUN_00104ff()` thì mình thấy rằng nó gọi một hàm khác là `FUN_00101265()`. ::: ![](https://hackmd.io/_uploads/HyyLn_gCn.png) :::info * Chức năng của `FUN_00101265()` dường như dùng để thực hiện tính toán giá trị cho các byte tại địa chỉ `param_1` bằng `param_2` và dữ liệu tại địa chỉ của `DAT_00103080`. ::: ![](https://hackmd.io/_uploads/Hy893OeRn.png) :::info * Mình thử kiểm tra `DAT_00103080` thì thấy có điều khá thú vị, dựa vào giá trị các byte đầu tiên của nó thì mình biết rằng `DAT_00103080` chính là `S-BOX` được sử dụng trong thuật toán AES :smiling_face_with_smiling_eyes_and_hand_covering_mouth:. Dựa vào phát hiện này thì mình suy luận rằng chức năng của`FUN_00101265()` được dùng để mở rộng khóa từ 16 byte khóa ban đầu được lưu tại `DAT_00105090`. ::: ![](https://hackmd.io/_uploads/S1PAn_xC2.png) :::info * Sau khi `FUN_001014ff()` hoàn thành, thì có một vòng lặp được thực thi. Ở mỗi vòng lặp thì hàm `FUN_001023b4()` được gọi và bước nhảy của mỗi lần lặp là 0x10. Theo mình suy luận thì `FUN_001023b4()` chính là hàm mã hóa AES và bước nhảy của vòng lặp là 0x10 là bởi vì AES sẽ mã hóa một block 16 byte trong một lần :grin:. * `FUN_001023b4()` tiếp tục gọi `FUN_001022b1()` với tham số truyền vào lần lượt là `param_2` và `param_1`. ::: ![](https://hackmd.io/_uploads/r11u1YeAn.png) :::info * Đây chính code của `FUN_001022b1()`, ban đầu nó sẽ thực hiện gọi hàm `FUN_00101598()` và sau đó thực hiện một vòng lặp có 10 lần lặp để thực hiện các hàm `FUN_00101639(), FUN_001016b0(), FUN_001017b8(), và FUN_00101598()`. Lưu ý là trong lần lặp cuối thì `FUN_001017b8()` không được thực hiện :grin:. * Dựa vào thông tin này thì mình càng thêm chắc chắn rằng hàm `FUN_001022b1()` thực hiện chức năng của thuật toán AES bởi vì AES có thực hiện mã hóa qua 10 round bởi 4 hàm là AddRoundKey(), ByteSub(), ShiftRow(), MixColumn() và ở round mã hóa cuối cùng thì MixColumn() cũng không được thực hiện :grin:. ::: ![](https://hackmd.io/_uploads/Byk3kFgC2.png) :::info * `FUN_00101598()` là hàm thực hiện chức năng của AddRoundKey() trong AES. Nó sẽ xor một block 16 byte với khóa tương ứng của round hiện tại :grin:. ::: ![](https://hackmd.io/_uploads/ByLJlKx0n.png) :::info * Tiếp theo là `FUN_00101639()`, nó thực hiện chức năng của ByteSub(). ByteSub() sẽ dùng ma trận `S-Box` để thực hiện thay thế block 16 byte dữ liệu đầu vào :grin:. ::: ![](https://hackmd.io/_uploads/HkZUeKe0h.png) :::info * Còn `FUN_001016b0()` có chức năng của ShiftRow(). Chức năng của nó là dịch chuyển các phần tử của mỗi hàng sang trái dựa vào chỉ số của mỗi hàng. Ví dụ như hàng 0 thì không dịch nhưng hàng 2 thì mỗi phần tử dịch qua trái 2 lần :grin:. ::: ![](https://hackmd.io/_uploads/rkCPlYgAn.png) :::info * Cuối cùng là `FUN_001017b8()`, nó tương ứng với hàm MixColumn(). Chức năng của MixColumn() là nhân ma trận dữ liệu đầu vào 4x4 với các cột trong một ma trận có sẵn để ra các cột dữ liệu mới :grin:. ::: ![](https://hackmd.io/_uploads/ByBnxKeA3.png) :::info * Oce, mình đã có 16 byte key ban đầu trước khi thực hiện mở rộng khóa và có flag.png đã bị mã hóa. Bấy nhiêu đây là đủ để giải mã flag.png :smiling_face_with_smiling_eyes_and_hand_covering_mouth:. * Dưới đây là một đoạn script python nhỏ sử dụng thuật toán AES-256 ở mode ECB để giải mã flag.png. ::: ```py= from Crypto.Cipher import AES with open('./flag.png', 'rb') as file_read: key = b'\xc1\x38\x0f\xcd\xf7\xe1\xb9\x66\x30\x33\x3f\xa9\xc9\x1b\x77\xea' cipher_text = file_read.read() cipher = AES.new(key, AES.MODE_ECB) plain_text = cipher.decrypt(cipher_text) with open('./real_flag.png', 'wb') as file_write: file_write.write(plain_text) ``` :::info * Sau khi giải mã thì mình thu được một tấm hình chứa flag là `gigem{its_encryption_all_the_way_down}` :smiley:. ::: ![](https://hackmd.io/_uploads/HypnfYlA2.png) ## first-responder :::info * Bài này cho mình một binary sshd, đây là một chương trình thực hiện dịch vụ ssh trên máy tính. Hint của đề bài nói rằng binary này chứa lỗ hỏng và hacker có thể lấy backdoor từ lỗ hỏng này, tác giả yêu cầu mình tìm ra lỗ hỏng này từ binary sshd. ::: ![](https://hackmd.io/_uploads/Bkfb8Lx0h.png) :::info * Mình thử kiểm tra độ lớn của sshd thì thấy dữ liệu của nó rất lớn, lên đến tận 1GB. Tới lúc này thì mình hơi bị hoang mang, không biết làm thế nào mình có thể tìm được lỗ hỏng trong một binary lớn đến như vậy :dizzy_face:. ::: ![](https://hackmd.io/_uploads/SJf888gAh.png) :::info * Đây là giao thức SSH được triển khai bởi sshd của máy tính mình :grin:. ::: ![](https://hackmd.io/_uploads/HJxFLUl0n.png) :::info * Challenge này cho mình tên của user `billy` có trong database của nó. Mình thử ssh đến server bằng user `billy` thì nó yêu cầu mình nhập mật khẩu, vì mình không biết mật khẩu là gì nên yêu cầu truy cập bị từ chối. * Hmm, vậy thì nhiệm vụ của mình là phải tìm ra mật khẩu của user `billy` để tạo backdoor đến server đang triển khai dịch vụ SSH. ::: ![](https://hackmd.io/_uploads/H1H_38gC3.png) :::info * Khi mình reverse binary bằng ghidra thì thấy có điều khá thú vị. Nó có định nghĩa làm `setlogin()` và `xcrypt()`. ::: ![](https://hackmd.io/_uploads/B1D7PIeAh.png) :::info * Về hàm `setlogin()` thì mình thấy không có gì đặc biệt cả. ::: ![](https://hackmd.io/_uploads/BkbBw8x0n.png) :::info * Còn về hàm xcrypt() thì có nhiều thứ để tìm hiểu hơn, nó có dùng bộ ba hàm setpwent(), getpwent(), và endpwent() để trích xuất thông tin password trong file tại đường dẫn /etc/shadow để thực hiện kiểm tra gì đó mình không hiểu lắm :thinking_face:. Ngoài ra nó cũng gọi hàm `FUN_0018b1b0()`, `FUN_00191420()` và `FUN_001913c0()`. ::: ![](https://hackmd.io/_uploads/SyapwUxCh.png) :::info * Đây là hàm `FUN_0018b1b0()`. ::: ![](https://hackmd.io/_uploads/S1J-OLgR3.png) :::info * Đây là hàm `FUN_00191420()`. ::: ![](https://hackmd.io/_uploads/BJSmdIlRn.png) :::info * Đây là hàm `FUN_001913c0()`. ::: ![](https://hackmd.io/_uploads/BkQBO8xAn.png) :::info * Sau khi kiểm tra hàm `xcrypt()` và các hàm được gọi bởi nó thì cá nhân mình không có ý tưởng gì về việc tìm ra password của user `billy` từ nó cả nên mình quyết định kiểm tra hàm gọi `xcrypt()` để khai thác thông tin. Dựa vào graph thì mình biết rằng `FUN_00112f30()` là hàm gọi `xcrypt()`. ::: ![](https://hackmd.io/_uploads/BJQFOIxCh.png) :::info * Hàm `FUN_00112f30()` sẽ trả về giá trị 0 hoặc 1 phụ thuộc vào một số điều kiện nào đó :thinking_face:. Theo kinh nghiệm của mình thì các hàm chỉ trả về giá trị 0 hoặc 1 thường là các hàm kiểm tra tính đúng đắn của một điều kiện nào đó. Trong trường hợp của sshd thì mình nghĩ ngay đến việc hàm này đang xác thực gì đó, nếu xác thực thành công thì trả về 1 còn ngược lại thì trả về 0. * Hàm này có 3 chỗ trả về 1 và 2 chỗ trả về 0. Mình sẽ kiểm tra lần lượt 3 vị trí trả về 1 để xem thử chức năng của nó là gì. Đầu tiên là vị trí gọi hàm `FUN_00112e10()`, nếu như giá trị trả về của nó là 1 thì hàm `FUN_00112f30()` sẽ trả về 1. ::: ![](https://hackmd.io/_uploads/Bkq-tUlR3.png) :::info * Oh, `FUN_00112e10()` có sử dụng hàm strcmp() để so sánh 0x30 byte giá trị tại địa chỉ `local_58` với `puVar6 + IVar1`. Mà mặt khác `puVar6 + IVar1` lại là tham số trong hàm `FUN_00112d10()` cùng với param_2, điều này làm nghĩ ngay đến việc `FUN_00112d10()` đã xử lý param_2 theo một cách nào đó để sinh ra dữ liệu được lưu trong `puVar6 + IVar1` và sau đó lấy dữ liệu này để so sánh với `local_58`. Vậy thì theo suy luận của mình thì `FUN_00112f30()` sẽ xác thực thành công nếu như 0x30 byte tại hai vị trí này bằng nhau thì nó sẽ trả về 1 , để ý tại câu lệnh`bVar8 = iVar3 == 0` :grin:. ::: ![](https://hackmd.io/_uploads/SyaSo8eCh.png) :::info * Mình thử kiểm tra hàm `FUN_00112d10()` thì thấy nó dùng khá nhiều hàm liên quan đến mật mã :grin:. Đầu tiên, nó gọi hàm `FUN_00112c50()` và `FUN_00112ca0()` với tham số lần lượt là `local_58` và `local_68`. Sau đó, nó dùng một loạt các hàm `EVP_aes_256_cbc()`, `EVP_EncryptInit_ex()`, `EVP_EncryptUpdate()`, và `EVP_EncryptFinal_ex()` để thực hiện mã hóa `param_1` bằng thuật toán `AES-256` ở mode `CBC` với `key` và `iv` lần lượt là `local_58` và `local_68`, kết quả sau quá trình mã hóa sẽ được lưu ở `param_2`. ::: ![](https://hackmd.io/_uploads/HJj6jUeCh.png) :::info * Đây là thông tin các hàm mã hóa được sử dụng trong binary. Mình đã dựa vào các tham số của nó để suy luận ra chức năng của `FUN_00112d10()` :grin:. ::: ![](https://hackmd.io/_uploads/H1Z7WpbRn.png) :::info * Đây là hàm tạo `key`, nó dùng giao thức `PBKDF2`. `PBKDF2` được sử dụng để tạo ra một password dựa vào `key` và `salt` cho trước :grin:. ::: ![](https://hackmd.io/_uploads/SJ2JhIxR3.png) :::info * Tương tự, việc tạo `iv` cũng thế :grin:. ::: ![](https://hackmd.io/_uploads/ryGGnIxA2.png) :::info * Tới đây thì mình đã đoán được chức năng của `FUN_00112f30()`. Nó sẽ thực hiện xác thực bằng cách so sánh cipher text password của user `billy` được mã hóa bằng `AES-256` ở mode `CBC` với 0x30 byte giá trị tại `local_58`, nếu như hai giá trị giống nhau thì quá trình xác thực thành công. * Ở thời điểm này, mình đã có `key`, `iv`, và cipher text của password là `local_58`. Mình chỉ cần dựa vào các giá trị này để giải mã ra password là xong. * Đoạn script ở dưới sẽ thực hiện tính toán `key`, `iv` và giải mã `password` cho mình :grin:. ::: ```python= from Crypto.Protocol.KDF import PBKDF2 from Crypto.Cipher import AES from Crypto.Hash import SHA256 from Crypto.Util.Padding import unpad # tính toán key password_string = "hMBTb3Yr70NaDyLoH9JPT2ykfSSKgvcW" salt_string = "3Wi28RC23cDM" password = password_string.encode() salt = salt_string.encode() interation = 4096 key_length = 32 key = PBKDF2(password, salt, dkLen=key_length, count=interation,hmac_hash_module=SHA256) # tính toán iv password_string = "8WtIAFFQBEXFu8ONUbiuO265" salt_string = "x58C4A0MoYAw" password = password_string.encode() salt = salt_string.encode() interation = 1024 iv_length = 16 iv = PBKDF2(password, salt, dkLen=iv_length, count=interation,hmac_hash_module=SHA256) cipher_text_hex = "76db5d8daef8336b8231d48064e8e36b5f5a51ab9c4ad4abd9592df4ba72033a4e331a59e65506f1dffbe7afe3d4de29" cipher_text = bytearray.fromhex(cipher_text_hex) cipher = AES.new(key, AES.MODE_CBC, iv) plain_text = cipher.decrypt(cipher_text) print(plain_text.decode('utf-8')) ``` :::info * Sau khi chạy đoạn script trên thì mình thu được password của billy. Tới lúc này mình chỉ cần ssh đến server để lấy shell và có được flag là `gigem{n0t_s0_hidd3n_i_gu3ss}`. * Cá nhân mình thấy challenge này muốn giải được thì phải có nhiều kinh nghiệm và nhạy bén trong quá trình reverse :grin:. ::: ![](https://hackmd.io/_uploads/HypUAwx02.png) ## absolute-cap