# Merisnotreal ## 1. Một số write up các challenge mình có tham gia gần đây ## Pragyan CTF Write up ### BitsAndPieces Đề bài: "The boss has made a new password checker program. Try to impress him by cracking it" Mở file bằng IDA: ![](https://i.imgur.com/ZZHKWab.png) Challenge này sẽ có 2 flagcheck1 và flagcheck2, mỗi funtion sẽ kiểm tra 10 ký tự đầu tiên của và 10 ký tự cuối tương ứng giá trị sau khi mã hóa với encode1 và encode2, nếu đúng sẽ ra flag Encode1 mã hóa 10 ký tự đầu tiên, đảo thứ tự các phần tự và gọi Myfunc để Xor với giá trị 32 ![](https://i.imgur.com/xR4SydV.png) hàm Xor của Myfunc: ![](https://i.imgur.com/KPdx8Dx.png) Encode2 sẽ mã hóa 10 ký tự sau, nhưng sẽ khác với encode1, encode2 sẽ xor giá trị i^th với i+1^th và giá trị sẽ chứa trong i^th: ![](https://i.imgur.com/zWIftk8.png) Cách giải bài này: Script: ``` p1 = [58, 44, 110, 218, 172, 238, 218, 46, 44, 206] flag_part1= "" for i in range(len(p1)): p1[i] = p1[i]^32 flag_part1 += chr(int(bin(p1[i])[2:][::-1].ljust(8, '0') ,2)) p2 = [65, 20, 19, 25, 51, 45, 65, 115, 113, 49][::-1] flag_part2=chr(p2[0]) for i in range(1,len(p2)): p2[i] = p2[i] ^ p2[i-1] flag_part2 += chr(p2[i]) print(flag_part1+flag_part2[::-1]) ``` + P1, P2: nằm trong flag1 và flag2 của flagcheck1, flagcheck 2 ![](https://i.imgur.com/HLVXvs1.png) "***có thể dùng api idc.getwidedword(ea) để khỏi phải dump bằng tay***" Kudo said :> p_ctf{X0r_1s_p0w3rful_r3@1} ### Affinity CTF Lite (Junior/Beginner) ### pseudo-pseudo random Mở task bằng Ghidra t sẽ thấy đoạn code sau: ``` undefined8 main(int param_1,long param_2) { char *__s; int iVar1; size_t sVar2; undefined8 uVar3; long in_FS_OFFSET; uint local_80; int local_7c; int aiStack_68 [8]; byte abStack_48 [40]; long local_20; local_20 = *(long *)(in_FS_OFFSET + 0x28); __s = *(char **)(param_2 + 8); if (1 < param_1) { sVar2 = strlen(__s); if (sVar2 == 0x19) { srand(0x1548); local_7c = 0; for (local_80 = 0; (int)local_80 < 10; local_80 = local_80 + 1) { iVar1 = rand(); if ((local_80 & 1) == 0) { aiStack_68[local_7c] = iVar1 % 0x7f; local_7c = local_7c + 1; } } local_80 = 0; while( true ) { sVar2 = strlen(__s); if (sVar2 <= (ulong)(long)(int)local_80) break; abStack_48[(int)local_80] = __s[*(int *)(&DAT_00601080 + (long)(int)local_80 * 4)]; local_80 = local_80 + 1; } local_80 = 0; while( true ) { sVar2 = strlen(__s); if (sVar2 <= (ulong)(long)(int)local_80) break; __s[(int)local_80] = (byte)aiStack_68[(int)local_80 / 5] ^ abStack_48[(int)local_80]; local_80 = local_80 + 1; } puts("Here is your encrypted message:"); local_80 = 0; while( true ) { sVar2 = strlen(__s); if (sVar2 <= (ulong)(long)(int)local_80) break; printf("%02X",(ulong)(uint)(int)__s[(int)local_80]); local_80 = local_80 + 1; } putchar(10); uVar3 = 0; goto LAB_004008da; } } puts("Please provide the correct input!"); uVar3 = 1; LAB_004008da: if (local_20 != *(long *)(in_FS_OFFSET + 0x28)) { /* WARNING: Subroutine does not return */ __stack_chk_fail(); } return uVar3; } ``` Nói tóm gọn thì chương trình sẽ má hóa input bằng một mật mã và XOR với một khóa được tạo ra ngẫu nhiên. Sau cùng thì in ra dưới dạng hexa + Đầu tiên thì chương trình sẽ kiểm tra độ dài input nhập vào có đủ 25 ký tự hay không (0x19) + Nếu đủ điều kiện, Chương trình sẽ sử dụng hàm srand với seed (0x1548) để khởi tạo giá trị ngẫu nhiên. + Sau đó, chương trình sẽ tạo ra 5 giá trị ngẫu nhiên và lưu trữ chúng vào mảng randomValues. + Chương trình sẽ lấy từng byte của dữ liệu đầu vào, XOR với giá trị ngẫu nhiên tương ứng và lưu trữ vào mảng Strings + Cuối cùng, chương trình sẽ in ra chuỗi đã được mã hóa dưới dạng các ký tự hexa. Cách giải: ``` import string import binascii random = [0x0e, 0x4a, 0x79, 0x05, 0x66] input = string.ascii_uppercase data = (0x00000000000000505241594e4b4a55494254484d475346575145444c56434f58).to_bytes(25, "little").decode("ascii") shuf = [] for c in data: shuf.append(input.index(c)) msg = binascii.unhexlify("6351486A40091E7A7A0C0B0249484D43716B305A121B273953") dec = [i for i in range(25)] for i in range(25): dec[shuf[i]] = chr(msg[i] ^ random[i // 5]) print(''.join(dec)) ``` **Flag: AFFCTF{1t5_N0t_50_r4nd0m} ## 2. Một số kỹ thuật linh tinh :> ## Anti debuging ## **Giới thiệu Anti debug:** Các kỹ thuật chống gỡ lỗi hay Anti-Debugging, là các phương pháp được các nhà lập trình hay phát triển phần mềm sử dụng để ngăn cản quá trình đảo ngược (Reverse) hay phân tích phần mềm của họ làm ra. Anti debug được sữ dụng khiến việc thay đổi cách thức hoạt động của chương trình, hay thay đổi cấu trúc của một chương trình trở nên khó khăn hơn với các hacker :V ## **Một số phương pháp Anti debug ### Debug Flags - **IsDebuggerPresent():** Như chúng ta đã biết Process Environment Block (PEB) là một cấu trúc dữ liệu trong hệ điều hành Windows Nt. Mỗi tiến trình đều có cấu trúc này và nó chứa thông tin của mỗi tiến trình. Đối với Malware có thể phát hiện được trình gỡ lỗi mà không sử dụng một hàm cụ thể nào chẳng hạn như IsDebuggerPresent() và chúng sử dụng chính chức năng này để bảo vệ chính nó khỏi kỹ thuật đảo ngược ( reverse ). Hàm này cũng là một trong những kỹ thuật chống gỡ lỗi mà hầu hết các công cụ packer hiện nay như UPX và PECompact có thể áp dụng vào chính chương trình gốc. Nói nôm na thì nó sẽ check **BeingDebugged** có trong PEB, nếu **BeingDebugged** được đặt thành giá trị ≠ 0, thì hàm trả về True ⇒ cho biết có debugger được gắn trong tiến trình, ngược lại = 0, sẽ trả về False và sẽ không có debugger nào trong tiến trình - ****NtQueryInformationProcess():**** Malwares có thể kiểm tra xem chương trình có đang được đính kèm với trình Debugger hay không, bằng cách sử dụng lớp thông tin **ProcessDebugPort (0x7) ⇒ Một giá trị khác không được trả về khi call ⇒ Qúa trình có debugger** - ****CheckRemoteDebuggerPresent():**** Là một hàm Kernel32.dll nó sẽ đặt (-1)0xffffffff trong tham số DebuggerPresent nếu một Debugger đang chạy, nó cũng sử dụng **NtQueryInformationProcess** với **ProcessDebugPort** là tham số **ProcessInformationClass**. - **ProcessDebugFlags (0x1f)** là một undocumented class có thể được chuyển đến hàm NtQueryProcessInformation. Khi NtQueryProcessInformation được gọi với lớp ProcessDebugFlags, hàm sẽ trả về giá trị nghịch đảo của EPROCESS->NoDebugInherit, có nghĩa là nếu debugger, thì hàm này sẽ trả về False nếu quá trình đang được debugger ### **TLS callback:** Thông thường chúng ta sẽ bắt đầu tiến trình gỡ lỗi từ Entry Point, tuy nhiên một số mã độc có thể chạy trước cả điểm Entry Point. Trong PE Header sẽ có một phần đặt vị trí của TLS callbacks. Các Malwares có thể sử dụng lệnh gọi lại TLS để tránh các thông báo của một trình gỡ lỗi. Và khi một Malware sử dụng xong lệnh TLS được tải vào Debugger, tiến trình của Malware đó sẽ hoàn thành trước cả khi tiến trình Debugger dừng lại tại điểm Entry Point ### **Thread Hiding:** Windows 2000 đã giới thiệu một API chống gỡ lỗi có tên là HideThreadFromDebugger để ngăn debugger nhận các sự kiện từ bất kỳ luồng nào có class được gọi trên đó, bao gồm các breakpoint và exit: ### **OutputDebugString:** Xem OutputDebugString có gây ra lỗi hay không. Lỗi chỉ xảy ra nếu không có debugger hoạt động để nhận chuỗi; do đó, chúng ta có thể kết luận rằng nếu không có lỗi (bằng cách gọi GetLastError) sau khi gọi OutputDebugString ⇒ debugger ### Manual Checks: - ****NtGlobalFlag:**** PEB có một field được mọi là NtGlobalFlag (offset 0x68), thì thông thường khi mà chương trình không có debugger thì field NtGlobalFlag khi đó sẽ chứa giá trị 0x0, ngược lại nó thường sẽ chứa giá trị 0x70 và các flag sau đây sẽ được đặt: - `FLG_HEAP_ENABLE_TAIL_CHECK` (0x10) - `FLG_HEAP_ENABLE_FREE_CHECK` (0x20) - `FLG_HEAP_VALIDATE_PARAMETERS` (0x40) ⇒ Các kiểm tra có debugger hay không bằng cách kiểm tra các flag đấy - ****Heap Flag:**** Giống với NtGlobalFlag, HeadFlag cũng là một field nằm trong PEB với địa chỉ là 0x18. Thì Heap đầu tiên sẽ chứa một header với các field được sử dụng để thông báo cho Kernel biết liệu cái Heap vừa tạo đấy có nằm trong trình Debugger hay không. 2 field bị ảnh hưởng bởi Debugger là Flags và ForceFlags, và giá trị 2 field thường được đặt là **HEAP_GROWABLE và 0** ### ****Assembly instructions**** - **INT 3:** INT3 như là một điểm dừng phần mềm (software breakpoint) trong mã lệnh. Khi không có debugger đạt đến lệnh INT3, một ngoại lệ có mã EXCEPTION_BREAKPOINT (0x80000003) sẽ được tạo ra và bộ xử lý ngoại lệ sẽ được gọi. Nếu trình gỡ lỗi xuất hiện, điều khiển sẽ không được chuyển cho bộ xử lý ngoại lệ. - **INT 2:** Giống với INT 3, một ngoại lệ EXCEPTION_BREAKPOINT cũng được tạo ra, nhưng INT 2 sẽ khác vì windows sẽ sữ dụng thanh ghi EIP làm địa chỉ ngoại lệ, và sau đó sẽ tăng giá trị của EIP lên - ****DebugBreak:**** Tạo ra một breakpoint xảy ra trong tiến trình hiện tại, khiến trình debugger được báo hiệu để xử lý - ****ICE:**** hoạt động như một lệnh gián đoạn đơn byte (single byte interrupt 1), tạo ra một ngoại lệ bước đơn EXCEPTION_SINGLE_STEP. Nó có thể được sử dụng để phát hiện xem chương trình có bị theo dõi hay không - **Instruction Counting:** Thiết lập các điểm dừng của phần cứng, khi thực thi lệnh với các điểm dừng ấy sẽ gây ra ngoại lệ EXCEPTION_SINGLE_STEP, qua đó tăng bộ đếm count lên ( EAX trong trường hợp này), và sau khi chuỗi kết thúc, chúng ta kiểm tra bộ đếm nếu nó không bằng với độ dài của chuỗi ⇒ có Debugger - ****POPF and Trap Flag:**** Như cái tên :V Có một Trap Flag trong thanh ghi Flags. Khi Trap được đặt, ngoại lệ SINGLE_STEP sẽ được nâng lên. Nếu không tìm thấy ngoại lệ ⇒ có debugger do Trap Flag đã được debugger xóa Ngoài ra các bạn có thể đọc thêm tài liệu tại đây: + Anti debug (blog của mình): https://www.notion.so/Anti-debuging-2ce101d23d944ea28f11c6b9617d6fd0?pvs=4 + Anti Debug Trick:https://anti-debug.checkpoint.com/ + Pe file: https://hackmd.io/Eh3inO3xS8GHa_pSy1PD4w # coming soon + Kĩ thuật Internal Keygen