
Đầu tiên kiểm tra sơ bộ bằng Detect It Easy.

target laf file PE32, thông tin chi tiết trong ảnh. Tiến hành phân tích file bằng IDA ta thấy, sau khi nhận vào Input, chương trình khởi tạo vùng nhớ lên RAM bằng VirtualAlloc sử dụng memcpy để copy một số byte từ .data lên vùng nhớ mới tạo, protect bằng VirtualProtect, AddVectorHandler để giải mã byte trong vùng nhớ thành shell code , sau đó sử dụng để check buffer nhập vào:

Debug chương trình ta thấy nó lặp lại quy trình này 2 lần, lúc này dự tính là sẽ đặt breakpoint khi chương trình call VirtualProtect có tham số là offset lpAddress của vùng nhớ ảo chứa shell code khi đó nhấn đúp vào lpAdress ta sẽ lấy được shellcode bằng cách dump phần byte được nạp vào ra:
Như dự tính, ta xử lý phần bytecode thứ nhất có địa chỉ là 0x6E0000 và size là 0x140 như các tham số trong stack:

Dump đoạn bytecode đó ra, sau đó xem xét xem handle sẽ làm gì với vùng bytecode của ta:

Chương trình sẽ kiểm tra quyền truy cập của vùng nhớ mới tạo, sau đó xor từng byte của vùng nhớ thứ nhất với 0x13 như hình. dựa vào đó ta viết một đoạn script để giải mã đoạn byte code đã dump ra:
listByteDump = [0x46, 0x98, 0xFF, 0x92, 0xFF, 0x57, 0x12, 0x13, 0x13, 0xD4, 0x56, 0xFB, 0x06, 0x27, 0x55, 0x31, 0x20, 0xD3, 0xD4, 0x56, 0xFF, 0x11, 0xCA, 0x9F, 0xDD, 0xD4, 0x56, 0xE3, 0xA9, 0x3F, 0x58, 0x63, 0xD4, 0x56, 0xE7, 0x49, 0x62, 0x7C, 0x55, 0xD4, 0x56, 0xEB, 0x1C, 0x5B, 0x3F, 0x70, 0xD5, 0x56, 0xEF, 0x78, 0xD4, 0x56, 0xAF, 0x72, 0xC2, 0xE7, 0x16, 0xD4, 0x56, 0xD3, 0x65, 0xCD, 0x8D, 0xA8, 0xD4, 0x56, 0xD7, 0x1C, 0xD9, 0x23, 0x1C, 0xD4, 0x56, 0xDB, 0x06, 0x4D, 0xDE, 0x76, 0xD4, 0x56, 0xDF, 0xC7, 0x6A, 0xB4, 0x0B, 0xD4, 0x56, 0xC3, 0x47, 0x64, 0xFD, 0x37, 0xD4, 0x56, 0xC7, 0x46, 0x7D, 0xE7, 0xD7, 0xD4, 0x56, 0xCB, 0x8B, 0x32, 0x88, 0x3B, 0xD4, 0x56, 0xCF, 0x63, 0x71, 0x72, 0xB6, 0xD4, 0x56, 0xF3, 0x59, 0xC4, 0x11, 0x9B, 0x75, 0xD4, 0x56, 0xF7, 0xEE, 0x71, 0x75, 0x83, 0x9B, 0x97, 0x16, 0xAF, 0xED, 0xEC, 0xEC, 0x53, 0x2E, 0x13, 0x12, 0x13, 0x13, 0x6F, 0xE2, 0x45, 0x20, 0xE5, 0x40, 0x1C, 0x0C, 0x53, 0x13, 0x75, 0x1C, 0x0C, 0x97, 0x13, 0x13, 0x13, 0x13, 0x13, 0x99, 0x8F, 0x26, 0xAF, 0xED, 0xEC, 0xEC, 0xAB, 0x94, 0x72, 0x0B, 0x95, 0xE4, 0xF5, 0x98, 0xD5, 0x98, 0xDD, 0x38, 0xD1, 0xC2, 0xFB, 0x10, 0xD1, 0xD2, 0xFB, 0x17, 0x78, 0xD3, 0x06, 0x38, 0xDB, 0x1C, 0xA5, 0x57, 0x1E, 0xFB, 0x11, 0xD0, 0x1C, 0xA5, 0xDB, 0x1C, 0xA5, 0x97, 0x1E, 0xAF, 0xED, 0xEC, 0xEC, 0x9B, 0x97, 0x26, 0xAF, 0xED, 0xEC, 0xEC, 0x55, 0x9B, 0x8F, 0x1E, 0xAF, 0xED, 0xEC, 0xEC, 0x92, 0xED, 0x13, 0x12, 0x13, 0x13, 0x6F, 0xA4, 0x98, 0x5E, 0x1B, 0x9E, 0x66, 0xAF, 0x20, 0xC1, 0x38, 0xE2, 0x48, 0x98, 0xD1, 0x36, 0xEC, 0x13, 0x13, 0x93, 0x6A, 0x14, 0x5B, 0x1E, 0x13, 0xEC, 0xEC, 0xEC, 0x53, 0x1C, 0xA5, 0x97, 0x16, 0xAF, 0xED, 0xEC, 0xEC, 0x23, 0x12, 0x1C, 0xA5, 0x12, 0x29, 0x17, 0x1D, 0x66, 0x02, 0x51, 0x52, 0x90, 0xE9, 0x39, 0x6F, 0xC4, 0xAB, 0x12, 0x13, 0x13, 0x13, 0x4D, 0x98, 0xF6, 0x4E, 0xD0, 0x20, 0xD3, 0x4D, 0x98, 0xF6, 0x4E, 0xD0, 0xDF, 0xDF, 0xAB, 0x33, 0x23, 0x53, 0x13, 0xD0, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF]
listNewByte = [i ^ 0x13 for i in listByteDump]
file = open("dump1.bin","wb")
sentence = bytearray(listNewByte)
file.write(sentence)
file.close()
Sau đó phân tích file dump bằng IDA sử dụng chức năng makecode của IDA để xem các câu lệnh, nếu có bản Pro thì có thể ấn P để IDA nhận diện các hàm sau đó ta có thể xem được giả mã C++:
int __cdecl sub_0(_BYTE *buffer)
{
int index; // eax
int i; // esi
char v3; // bl
int v4; // ecx
_BYTE *v5; // ecx
int v6; // edx
char v8[256]; // [esp+0h] [ebp-144h]
int v9[10]; // [esp+100h] [ebp-44h] BYREF
__int16 v10; // [esp+128h] [ebp-1Ch]
int v11[5]; // [esp+12Ch] [ebp-18h]
char v12; // [esp+140h] [ebp-4h]
v11[0] = 0x22463415;
index = 0;
v11[1] = 0xCE8CD902;
v11[2] = 0x704B2CBA;
v11[3] = 0x466F715A;
v11[4] = 0x632C480F;
v12 = 107;
v9[0] = 0x5F4D161;
v9[1] = -1147216266;
v9[2] = 254855695;
v9[3] = 1707957781;
v9[4] = 413628884;
v9[5] = 619607892;
v9[6] = -990613931;
v9[7] = 681255320;
v9[8] = -1520344464;
v9[9] = -2013079734;
v10 = 0x62FD;
do
{
v8[index] = index;
++index;
}
while ( index < 256 );
for ( i = 0; i < 256; ++i )
{
v3 = v8[i];
v4 = (unsigned __int8)(v3 + *((_BYTE *)v11 + i % 21u));
v8[i] = v8[v4];
v8[v4] = v3;
}
v5 = buffer;
v6 = 0;
while ( 1 )
{
*v5 ^= v8[v6 % 256];
if ( *v5 != v5[(char *)v9 - buffer] )
break;
++v6;
++v5;
if ( v6 >= 42 )
return 1;
}
return 0;
}
Đọc giả mã ta thấy v11 là key sẽ được handle, xor với input và so sánh với 42 BYTE trong v9 để có thể kiểm tra Ta viết một script theo mã giả sử dụng những byte trong v11 và v9 để tái tạo input:
v11 = [0x15, 0x34, 0x46, 0x22, 0x02, 0xD9, 0x8C, 0xCE, 0xBA, 0x2C, 0x4B,\
0x70, 0x5A, 0x71, 0x6F, 0x46, 0x0F, 0x48, 0x2C, 0x63, 0x6B]
v8 = [i for i in range(257)]
v9 = [0x61, 0xD1, 0xF4, 0x05, 0x76, 0xDE, 0x9E, 0xBB, 0x0F, 0xCA,\
0x30, 0x0F, 0x15, 0x5E, 0xCD, 0x65, 0xD4, 0x79, 0xA7, 0x18,\
0x54, 0x77, 0xEE, 0x24, 0x55, 0x6E, 0xF4, 0xC4, 0x98, 0x21,\
0x9B, 0x28, 0x70, 0x62, 0x61, 0xA5, 0x4A, 0xD7, 0x02, 0x88,\
0xFD, 0x62]
for i in range(257):
v3 = v8[i]
v4 = (v3 + v11[i % 21])%256
v8[i] = v8[v4]
v8[v4] = v3
password = "".join(chr(v8[i]^v9[i]) for i in range(42))
print(password)
Kết quả:

Nhập input vào chương trình để lấy flag:

Đù, sai ạ, lúc đầu cứ nghĩ bị lừa nên làm tương tự để xem shellcode còn lại nhưng nó chỉ là shellcode kiểm tra chiều dài, khi đó thử thay cái chữ G vô duyên kia thành e:

Flag: mtactf{4uT0mAt1C_R1fl3_AK-47}

