# PTITCTF (Revese Rust Challenges) ## Phân tích tổng quát: - Đầu tiên mình sẽ xác định loại file này là gì. ![image](https://hackmd.io/_uploads/SyDaJuBkex.png) - Nó chính là file `PE(Windown)` được compiler bằng ngôn ngữ Rust. - Tiếp theo mình sẽ tiến hành phân tích tĩnh trên IDA. ```rust= int __fastcall main(int argc, const char **argv, const char **envp) { return sub_140002480(sub_1400030E0, argc, argv, 0LL); } ``` - Nhìn tổng quan thì hàm main() chả có gì ngoài việc return về thằng ```sub_140002480(sub_1400030E0, argc, argv, 0LL)```. - Ta hiểu qua rằng nó sẽ run kèm theo đối số. - VD: `./chall.exe <flag>` - Mình sẽ xem thằng `sub_140002480()` này làm gì. ```rust= // Hidden C++ exception states: #wind=1 #try_helpers=1 __int64 __fastcall sub_140002480(__int64 a1, __int64 a2, __int64 a3, char a4) { _QWORD v5[4]; // [rsp+38h] [rbp-30h] BYREF char v6; // [rsp+5Fh] [rbp-9h] v5[1] = a1; v5[2] = a2; v5[3] = a3; v6 = a4; v5[0] = a1; return sub_140007C60(v5, &unk_1400207B0); } ``` ```rust= // Hidden C++ exception states: #wind=1 #try_helpers=1 __int64 __fastcall sub_140007C60(__int64 a1, __int64 a2) { __int64 v4; // rax __int64 v6; // rdx __int64 v7; // rsi __int64 v8; // rdx __int64 v9; // rdx ULONG StackSizeInBytes[2]; // [rsp+20h] [rbp-60h] BYREF __int64 v11; // [rsp+50h] [rbp-30h] BYREF __int64 v12; // [rsp+60h] [rbp-20h] __int64 v13; // [rsp+68h] [rbp-18h] __int64 v14; // [rsp+78h] [rbp-8h] __int64 v15; // [rsp+80h] [rbp+0h] BYREF __int64 v16; // [rsp+90h] [rbp+10h] BYREF __int64 v17; // [rsp+98h] [rbp+18h] BYREF v14 = -2LL; AddVectoredExceptionHandler(0, Handler); StackSizeInBytes[0] = 20480; SetThreadStackGuarantee(StackSizeInBytes); GetCurrentThread(); off_14002C028(); try { *(_QWORD *)StackSizeInBytes = 0LL; v4 = sub_140008210(StackSizeInBytes); } catch ( __int64 v17 ) { sub_14001E550(v17); goto LABEL_9; } catch ( ... ) { sub_14001E550(0LL); goto LABEL_9; } try { sub_140008020(v4); v7 = (*(int (__fastcall **)(__int64))(a2 + 40))(a1); } catch ( __int64 v16 ) { v12 = sub_14001E550(v16); v13 = v8; goto LABEL_5; } catch ( ... ) { v12 = sub_14001E550(0LL); v13 = v9; LABEL_5: if ( *(_QWORD *)v13 ) (*(void (__fastcall **)(__int64))v13)(v12); v6 = *(_QWORD *)(v13 + 8); v7 = 101LL; if ( v6 ) sub_140005FE0(v12, v6, *(_QWORD *)(v13 + 16)); } try { if ( dword_14002C144 != 4 ) { LOBYTE(v11) = 1; *(_QWORD *)StackSizeInBytes = &v11; sub_14001E890(StackSizeInBytes); } } catch ( __int64 v15 ) { sub_14001E550(v15); goto LABEL_9; } catch ( ... ) { sub_14001E550(0LL); LABEL_9: sub_140006730(); } return v7; } ``` - Càng vào sâu thì chả có manh mỗi gì nên mình sẽ quay lại thằng ```sub_1400030E0()```. ```rust= // Hidden C++ exception states: #wind=2 __int64 sub_1400030E0() { __int64 v0; // rax __int64 v1; // rdx __int64 v2; // rdx __int64 v3; // rax __int64 v4; // rdx __int64 v5; // rax __int64 v6; // rdx int *v7; // rdx int v9; // [rsp+2Ch] [rbp-54h] unsigned __int64 v10; // [rsp+98h] [rbp+18h] __int64 v11; // [rsp+A8h] [rbp+28h] _QWORD v12[2]; // [rsp+D0h] [rbp+50h] BYREF _BYTE v13[48]; // [rsp+E0h] [rbp+60h] BYREF _OWORD v14[2]; // [rsp+110h] [rbp+90h] BYREF _BYTE v15[24]; // [rsp+138h] [rbp+B8h] BYREF _BYTE v16[32]; // [rsp+150h] [rbp+D0h] BYREF _BYTE v17[48]; // [rsp+170h] [rbp+F0h] BYREF _OWORD v18[2]; // [rsp+1A0h] [rbp+120h] BYREF _BYTE v19[48]; // [rsp+1C8h] [rbp+148h] BYREF _BYTE v20[24]; // [rsp+1F8h] [rbp+178h] BYREF _QWORD v21[2]; // [rsp+210h] [rbp+190h] BYREF _BYTE *v22; // [rsp+220h] [rbp+1A0h] char v23; // [rsp+22Fh] [rbp+1AFh] BYREF _DWORD v24[34]; // [rsp+230h] [rbp+1B0h] _QWORD v25[3]; // [rsp+2B8h] [rbp+238h] BYREF _BYTE v26[24]; // [rsp+2D0h] [rbp+250h] BYREF _QWORD v27[3]; // [rsp+2E8h] [rbp+268h] BYREF unsigned __int64 v28; // [rsp+300h] [rbp+280h] int *v29; // [rsp+308h] [rbp+288h] _BYTE v30[48]; // [rsp+310h] [rbp+290h] BYREF _BYTE v31[48]; // [rsp+340h] [rbp+2C0h] BYREF __int64 v32; // [rsp+370h] [rbp+2F0h] unsigned __int64 v33; // [rsp+378h] [rbp+2F8h] int v34; // [rsp+384h] [rbp+304h] _BYTE *v35; // [rsp+388h] [rbp+308h] int v36; // [rsp+394h] [rbp+314h] __int128 v37; // [rsp+398h] [rbp+318h] __int64 v38; // [rsp+3A8h] [rbp+328h] __int64 (__fastcall *v39)(); // [rsp+3B0h] [rbp+330h] __int64 v40; // [rsp+3B8h] [rbp+338h] __int128 v41; // [rsp+3C0h] [rbp+340h] _QWORD *v42; // [rsp+3D0h] [rbp+350h] __int64 (__fastcall *v43)(); // [rsp+3D8h] [rbp+358h] _QWORD *v44; // [rsp+3E0h] [rbp+360h] __int64 v45; // [rsp+3E8h] [rbp+368h] v45 = -2LL; v12[0] = asc_14002092E; v12[1] = 3598LL; v42 = v12; v43 = sub_140003AA0; v44 = v12; *&v41 = v12; *(&v41 + 1) = sub_140003AA0; v14[1] = v41; v14[0] = v41; sub_140003AD0(v13, &unk_140021740, v14); sub_14000A340(v13); sub_140008970(); sub_140005F60(v15, v16); if ( sub_140005670(v15) != 2 ) { v38 = sub_140005800(v15, 0LL, &off_140021818); v39 = sub_140004FE0; v40 = v38; *&v37 = v38; *(&v37 + 1) = sub_140004FE0; v18[1] = v37; v18[0] = v37; sub_140003AD0(v17, &off_1400217F8, v18); sub_14000A450(v17); sub_14000C820(1LL); } v11 = sub_140005800(v15, 1LL, &off_140021770); v32 = v11; v10 = sub_140004FA0(); if ( v10 < 4 ) sub_14001FA30(&off_140021788); if ( v10 != 34 ) { sub_140003B20(v19, &off_1400217D8); sub_14000A450(v19); sub_14000C820(1LL); } sub_140005440(v20); v0 = sub_140004FC0(v11); v21[0] = sub_140005D40(v0, v1); v21[1] = v2; while ( 1 ) { v22 = sub_140005E60(v21); if ( !v22 ) break; v35 = v22; v23 = *v22; v36 = sub_140003710(&v23, 1LL); sub_140005680(v20); } v24[0] = -1184252295; v24[1] = -1107002784; v24[2] = -587065671; v24[3] = -1107002784; v24[4] = 1037565863; v24[5] = -1107002784; v24[6] = 1304234792; v24[7] = 366298937; v24[8] = 1255198513; v24[9] = 1842515611; v24[10] = 30677878; v24[11] = -2082672713; v24[12] = 2013832146; v24[13] = 701932520; v24[14] = -186917087; v24[15] = 1993550816; v24[16] = 701932520; v24[17] = -630205792; v24[18] = -69523947; v24[19] = 701932520; v24[20] = 1304234792; v24[21] = 1812594589; v24[22] = -2082672713; v24[23] = 1842515611; v24[24] = 2013832146; v24[25] = -1730327860; v24[26] = 453955339; v24[27] = 701932520; v24[28] = 1466425173; v24[29] = -227710402; v24[30] = -2068763730; v24[31] = 1790921346; v24[32] = -1637089325; v24[33] = -55123444; v3 = sub_1400057A0(v20); v5 = sub_140005910(v3, v4); sub_140005CB0(v26, v5, v6); sub_140001FC0(v25, v26); v27[0] = v25[0]; v27[1] = v25[1]; v27[2] = v25[2]; while ( 1 ) { v28 = sub_140001ED0(v27); v29 = v7; if ( !v7 ) break; v33 = v28; v9 = *v29; v34 = *v29; if ( v28 >= 0x22 ) sub_14001F590(v28, 34LL, &off_1400217B8); if ( v9 != v24[v28] ) { sub_140003B20(v30, &off_1400217D8); sub_14000A340(v30); sub_14000C820(1LL); } } sub_140003B20(v31, &off_1400217A8); sub_14000A340(v31); sub_140002A10(v20); return sub_140002C90(v15); } ``` - Đây rồi có vẻ đây là hàm xử lý chính vì mình có thấy chuỗi data target sau khi encode. - Nhìn sơ qua thì flag nhập vào nó sẽ xử lý bằng cách nào đó rồi return về thằng `v9` rồi nó sẽ đi so sánh với thằng data target. - Nhưng `v9` nó có liên quan đến `v7` nhưng mình không thấy nó `v7` được tham chiếu từ đâu. - Mình nghĩ rằng nó sẽ có một hàm xử lý riêng trước đó. - Thì ra nó ở đây. ```rust= if ( v10 != 34 ) { sub_140003B20(v19, &off_1400217D8); sub_14000A450(v19); sub_14000C820(1LL); } sub_140005440(v20); v0 = sub_140004FC0(v11); v21[0] = sub_140005D40(v0, v1); v21[1] = v2; while ( 1 ) { v22 = sub_140005E60(v21); if ( !v22 ) break; v35 = v22; v23 = *v22; v36 = sub_140003710(&v23, 1LL); sub_140005680(v20); } ``` - Ở đây thằng `v10` là len của flag sẽ là `34`. - Tiếp tục thằng `while` sẽ xử lý mã hóa flag, tại thằng ```v36 = sub_140003710(&v23, 1LL);```. - Mình sẽ xem thử nó làm gì. ```rust= __int64 __fastcall sub_140003710(__int64 a1, __int64 a2) { __int64 v2; // rdx unsigned __int64 v3; // rdx __int64 v4; // rdx int v5; // edx int v7; // edx unsigned __int64 v8; // [rsp+38h] [rbp-490h] _DWORD v11[256]; // [rsp+50h] [rbp-478h] BYREF _QWORD v12[2]; // [rsp+450h] [rbp-78h] BYREF __int64 v13; // [rsp+460h] [rbp-68h] unsigned __int64 v14; // [rsp+468h] [rbp-60h] unsigned int v15; // [rsp+470h] [rbp-58h] _DWORD v16[2]; // [rsp+474h] [rbp-54h] BYREF int v17; // [rsp+47Ch] [rbp-4Ch] int v18; // [rsp+480h] [rbp-48h] unsigned int v19; // [rsp+484h] [rbp-44h] _QWORD v20[2]; // [rsp+488h] [rbp-40h] BYREF char *v21; // [rsp+498h] [rbp-30h] __int64 v22; // [rsp+4A0h] [rbp-28h] __int64 v23; // [rsp+4A8h] [rbp-20h] char v24; // [rsp+4B7h] [rbp-11h] __int64 v25; // [rsp+4B8h] [rbp-10h] unsigned __int64 v26; // [rsp+4C0h] [rbp-8h] v22 = a1; v23 = a2; memset(v11, 0, sizeof(v11)); v12[0] = sub_140002550(0LL, 256LL); v12[1] = v2; while ( 1 ) { v13 = sub_140002500(v12); v14 = v3; if ( !v13 ) break; v8 = v14; v26 = v14; v15 = v14; v16[0] = sub_140002540(0LL, 8LL); v16[1] = v5; while ( 1 ) { v17 = sub_140002520(v16); v18 = v7; if ( !v17 ) break; if ( (v15 & 1) != 0 ) v15 = (v15 >> 1) ^ 0xEDB88320; else v15 >>= 1; } if ( v8 >= 0x100 ) sub_14001F590(v8, 256LL, &off_140021848); v11[v8] = v15; } v19 = -1; v20[0] = sub_140005D40(a1, a2); v20[1] = v4; while ( 1 ) { v21 = sub_140005E60(v20); if ( !v21 ) break; v24 = *v21; v25 = (v24 ^ v19); v19 = v11[v25] ^ (v19 >> 8); } return ~v19; } ``` - Thuật toán cũng khá lạ nên mình sẽ tìm hiểu. - Đây là đường link github của một người lạ nói về thuật toán này https://gist.github.com/timepp/1f678e200d9e0f2a043a9ec6b3690635 - Nó là thuật toán `CRC 32` hiểu đơn giản là nó sẽ tạo một bảng chữ cái tưng ứng với từng 8bit hex cho mỗi ký tự theo cách của nó. - Vậy thằng này khi chuyển về hex sẽ tương ứng với từng ký tự flag theo thứ tự ```rust= v24[0] = -1184252295; v24[1] = -1107002784; v24[2] = -587065671; v24[3] = -1107002784; v24[4] = 1037565863; v24[5] = -1107002784; v24[6] = 1304234792; v24[7] = 366298937; v24[8] = 1255198513; v24[9] = 1842515611; v24[10] = 30677878; v24[11] = -2082672713; v24[12] = 2013832146; v24[13] = 701932520; v24[14] = -186917087; v24[15] = 1993550816; v24[16] = 701932520; v24[17] = -630205792; v24[18] = -69523947; v24[19] = 701932520; v24[20] = 1304234792; v24[21] = 1812594589; v24[22] = -2082672713; v24[23] = 1842515611; v24[24] = 2013832146; v24[25] = -1730327860; v24[26] = 453955339; v24[27] = 701932520; v24[28] = 1466425173; v24[29] = -227710402; v24[30] = -2068763730; v24[31] = 1790921346; v24[32] = -1637089325; v24[33] = -55123444; ``` ## Solution - Và cách làm của mình sẽ viết script python tạo ra một bảng chữ các bằng 8bits hex tương ứng sau đó mapping thằng target này vào và lấy flag. > Script solve ```python= def crc32(data: bytes) -> int: table = [] for i in range(256): crc = i for _ in range(8): if crc & 1: crc = (crc >> 1) ^ 0xEDB88320 else: crc >>= 1 table.append(crc) crc = 0xFFFFFFFF for byte in data: crc = table[(byte ^ crc) & 0xFF] ^ (crc >> 8) return ~crc & 0xFFFFFFFF target = [ 0xB969BE79, 0xBE047A60, 0xDD0216B9, 0xBE047A60, 0x3DD7FFA7, 0xBE047A60, 0x4DBD0B28, 0x15D54739, 0x4AD0CF31, 0x6DD28E9B, 0x01D41B76, 0x83DCEFB7, 0x7808A3D2, 0x29D6A3E8, 0xF4DBDF21, 0x76D32BE0, 0x29D6A3E8, 0xDA6FD2A0, 0xFBDB2615, 0x29D6A3E8, 0x4DBD0B28, 0x6C09FF9D, 0x83DCEFB7, 0x6DD28E9B, 0x7808A3D2, 0x98DD4ACC, 0x1B0ECF0B, 0x29D6A3E8, 0x5767DF55, 0xF26D6A3E, 0x84B12BAE, 0x6ABF4A82, 0x9E6BFFD3, 0xFCB6E20C ] charset = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_" result = b"" for t in target: found = False for c in charset: if crc32(bytes([c])) == t: result += bytes([c]) found = True break if not found: result += b"?" print("Flag:", result.decode(errors="replace")) ``` > Flag: PTITCTF{B3g1n_0f_My_Fr13nds_Ru57?}