zer0pts CTF
reversing
We're given a qemu environment.
~ # ls
chall.hbt hobbit.ko
~ # ./chall.hbt
FLAG: hoge
Wrong!
~ # hexdump -C chall.hbt
00000000 48 4f 42 42 49 54 01 02 b6 98 81 8f 38 72 1d 8c |HOBBIT......8r..|
00000010 30 f0 fc b1 8c f8 70 8c ba 68 03 a0 d0 72 45 e2 |0.....p..h...rE.|
00000020 30 9e 1d 2f de 3a 74 e8 40 00 00 00 00 00 ad de |0../.:t.@.......|
00000030 00 00 00 00 7e 00 00 00 00 00 ef be 00 00 00 00 |....~...........|
00000040 cf 83 06 e3 ab e6 36 51 b3 55 9b a9 5b 80 e5 bf |......6Q.U..[...|
00000050 a0 ab 44 e6 36 e3 e0 46 c3 4e 91 6d 5e f5 d2 ee |..D.6..F.N.m^...|
00000060 93 e4 60 c9 04 1a 40 b6 8d 08 31 0c de 2d 69 4e |..`...@...1..-iN|
00000070 37 ce 6e 7e ac 86 61 02 27 41 9a 96 c7 94 bc c9 |7.n~..a.'A......|
00000080 14 d3 b6 52 60 21 f2 b0 be 7e 1c f1 9e d9 dc a3 |...R`!...~......|
00000090 b1 d7 30 5d 22 cc d0 a7 85 49 62 25 b4 77 d1 5d |..0]"....Ib%.w.]|
000000a0 6a a3 4c 4b cb 8c ae 36 e8 8e f2 29 63 e9 58 af |j.LK...6...)c.X.|
000000b0 e6 5c 9e de 21 89 6a 40 d0 a2 0a 6e fb 03 3a 24 |.\..!.j@...n..:$|
000000c0 3d e2 46 f9 16 7a 8e 0e 43 a5 0d d8 56 9c a3 9d |=.F..z..C...V...|
000000d0 e8 50 a5 64 55 f8 50 96 c5 83 03 96 6c 47 17 21 |.P.dU.P.....lG.!|
000000e0 d7 08 0c 99 3e 93 a3 39 9e 97 a6 b6 ab fb 00 23 |....>..9.......#|
000000f0 61 8e 76 ad 69 b9 2c 5f 7e 2d 54 14 70 d1 |a.v.i.,_~-T.p.|
000000fe
~ #
It seems a new binary format is registered in this kernel.
Let's check hobbit.ko
.
By analysing hobbit.ko
you can find the HOBBIT format.
Member | Offset | Size | Description |
---|---|---|---|
magic | 0x00 | 0x08 | HOBBIT\x01\x02 |
key_data | 0x08 | 0x10 | 16-byte key to decrypt data section |
key_text | 0x18 | 0x10 | 16-byte key to decrypt text section |
size_data | 0x28 | 0x04 | The size of data section |
addr_data | 0x2c | 0x08 | The address of data section |
size_text | 0x34 | 0x04 | The size of text section |
addr_text | 0x38 | 0x08 | The address of text section |
enc_data | 0x40 | size_data | The encrypted data section |
enc_text | 0x40 + size_data | size_text | The encrypted text section |
If you can read the kernel driver, you'll find the encryption method is plain RC4.
If you decrypt the text section, you'll see a code like this:
_start:
;; write(1, "FLAG: ", 7);
mov rdx, 7
mov rsi, 0xdead0000
xor eax, eax
inc eax
mov rdi, rax
syscall
;; read(0, buf, 0x100)
mov rdx, 0x100
mov rsi, 0xdead00ef
xor eax, eax
xor edi, edi
syscall
;; xor(buf, code, 39);
cld
mov rcx, 39
mov rsi, 0xdead00ef
mov rdi, rsi
ENCIPHER:
lodsb
xor al, cl
stosb
loop ENCIPHER
;; memcmp(buf, E"zer0pts{...}", 39);
mov rcx, 39
mov rsi, 0xdead0019
mov rdi, 0xdead00ef
repe cmpsb
je CORRECT
WRONG:
;; write(1, "Wrong!\n", 8);
mov rdx, 8
mov rsi, 0xdead0011
xor eax, eax
inc eax
mov rdi, rax
syscall
;; exit(0);
EXIT:
mov rdi, 0
mov rax, 60
syscall
CORRECT:
;; write(1, "Correct!\n", 10);
mov rdx, 10
mov rsi, 0xdead0007
xor eax, eax
inc eax
mov rdi, rax
syscall
jmp EXIT
You can decode the flag with python or whatever.
from ptrlib import u32, u64
import os
def KSA(key):
S = [i for i in range(0x100)]
j = 0
for i in range(0x100):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
return S
def PRGA(S):
i, j = 0, 0
while True:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) % 256]
yield K
def RC4(data, key):
S = KSA(key)
gen = PRGA(S)
data = bytearray(data)
result = bytearray(c ^ n for c, n in zip(data, gen))
return result
with open("../challenge/chall.hbt", "rb") as f:
f.seek(0x8)
key_data = f.read(16)
key_text = f.read(16)
f.seek(0x28)
len_data = u32(f.read(4))
f.seek(0x34)
len_text = u32(f.read(4))
f.seek(0x40)
data = f.read(len_data)
text = f.read(len_text)
output = ''
for i, c in enumerate(RC4(data, key_data)[0x19:]):
output += chr(c ^ (39 - i))
print(output)