
Walkthrough
Diberikan sebuah binary .ko (kernel module) bernama ghost.ko. Karena ini adalah kernel module, kita bisa expect ada init function dan exit function.
Langsung aja buka di IDA/Ghidra.
1. Analysis: The Init Function
Di fungsi ghost_init, kita melihat ada beberapa setup penting.

Module ini mencoba membuat entry di /proc/ghost4301. Hal yang menarik ada di logic pengecekannya:
ghost_len = i; uint64_t rax_1 = proc_create("ghost4301", 0x124, 0, &ghost_fops); ghost_entry = rax_1;
if (!rax_1) _printk(0x400210, "ghost4301"); else { _printk(0x4002b1, "ghost4301_telemetry\nz[i] = rotl8((flag[i]^key[i%7] + poly(i)), (3*i)%8)\npoly(i) = i^3 + " "7*i^2 + 13*i + 37 (mod 256)\n"); _printk(0x4002b1, "key[7] = {0x13,0x37,0x42,0xA5,0x5C,0x9E,0x11}\n"); _printk(0x400238, "ghost4301"); }Jika proc_create BERHASIL (rax_1 != 0), dia malah nge-print “telemetry” yang berisi rumus enkripsi dan key-nya. Ini behaviour yang agak unik, biasanya error message yang diprint kalau gagal.
Dari printk tersebut, kita dapat informasi vital:
- Encryption Formula:
z[i] = rotl8((flag[i]^key[i%7] + poly(i)), (3*i)%8) - Key:
{0x13, 0x37, 0x42, 0xA5, 0x5C, 0x9E, 0x11} - Polynomial:
poly(i) = i^3 + 7*i^2 + 13*i + 37 (mod 256)
2. Extracting the Ciphertext
Kalau kita lihat di awal fungsi ghost_init, ada loop yang nge-print “telemetry frames”:
i += scnprintf(i + &ghost_buf, 0x200 - i, "frame[%d] = 0x%016llx\n", rcx_1, *((r14_1 << 3) + &ghost_blob));Loop ini membaca data dari ghost_blob dan menampilkannya sebagai 64-bit integers (0x%016llx).
Kita bisa ekstrak data ini langsung dari file binary .ko menggunakan readelf atau objdump. Kita cari section .rodata atau symbol ghost_blob.

Kita temukan blok data 40 bytes yang mencurigakan di string dump atau rodata:
70 9A 2C C1 3B 5E F4 77F2 41 12 64 BA 4C 5D B190 6D CA 2D 3F AD 1F 669E D4 25 42 F6 E1 94 17AA AA AA AA AA DB 4D 31Ini kemungkinan besar adalah ciphertext z kita.
3. The Solver Strategy
Dari rumus enkripsi:
z[i] = rotl8((flag[i] ^ key[i%7] + poly(i)), (3*i)%8)
Kita harus melakukan operasi kebalikannya (decryption) urut dari luar ke dalam:
- Rotate Right: Kebalikan dari
rotl8adalahrotr8. - Subtract Poly: Kebalikan dari
+ poly(i)adalah- poly(i). - XOR Key: Kebalikan dari
^ keyadalah^ key(XOR adalah inversenya sendiri).
Jadi rumus dekripsinya:
flag[i] = rotr8(z[i], (3*i)%8) - poly(i) ^ key[i%7]
Attempt 1: Naive Solve
Awalnya gw coba solve langsung byte-per-byte sesuai urutan dump:
z_raw = bytes.fromhex("""70 9A 2C C1 3B 5E F4 77F2 41 12 64 BA 4C 5D B190 6D CA 2D 3F AD 1F 669E D4 25 42 F6 E1 94 17AA AA AA AA AA DB 4D 31""")# ... decryption loop ...Tapi hasilnya gagal. Outputnya cuma sampah binary yang gak membentuk flag.`
Attempt 2: Addressing Endianness
Gw sadar ada yang aneh. Di code ghost_init, data dibaca sebagai unsigned long long (64-bit) dan diprint dengan format %016llx.
*((r14_1 << 3) + &ghost_blob)Di arsitektur x86 (Little Endian), ketika kita baca 8 byte “A B C D E F G H” sebagai satu integer 64-bit, nilainya jadi 0xHGFEDCBA.
Jadi, data raw yang kita copy (kalau kita asumsi itu dari print output atau cara storage kernel) perlu kita reverse per 8 byte untuk mendapatkan urutan byte asli yang dimaksud oleh enkripsi byte-per-byte.
Code fix-nya:
z = b''.join( z_raw[i:i+8][::-1] for i in range(0, len(z_raw), 8))4. Final Solver Script
Berikut script solver lengkapnya:
z_raw = bytes.fromhex("""70 9A 2C C1 3B 5E F4 77F2 41 12 64 BA 4C 5D B190 6D CA 2D 3F AD 1F 669E D4 25 42 F6 E1 94 17AA AA AA AA AA DB 4D 31""")
# Reverse every 8 bytes chunk to fix endiannessz = b''.join( z_raw[i:i+8][::-1] for i in range(0, len(z_raw), 8))
key = [0x13,0x37,0x42,0xA5,0x5C,0x9E,0x11]
def poly(i): return (i**3 + 7*i**2 + 13*i + 37) % 256
def rotr8(x, r): r &= 7 return ((x >> r) | (x << (8-r))) & 0xFF
flag = []for i, c in enumerate(z): # 1. Reverse rotation x = rotr8(c, (3*i) % 8)
# 2. Reverse addition (subtract) x = (x - poly(i)) & 0xFF
# 3. Reverse XOR x ^= key[i % 7]
flag.append(x)
print(bytes(flag))