Try   HackMD

[zer0pts CTF 2020] Hobbit

tags: zer0pts CTF reversing

Overview

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.

Analysis

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.

Solution

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)