In this challenge, the goal is to reverse a NetBSD kernel module and get the flag. We are provided with a zip file which contains the kernel module, NetBSD disc img, and couple of scripts to setup qemu and gdb for the challenge. It would have been easier to solve the challenge dynamically by setting up qemu and gdb but due to no experience with NetBSD reversing i proceeded to analyze the kernel module statically with IDA.

Taking a look at the strings doesn’t reveal much, but there are interesting functions in the kernel module. The two functions which looked interesting were get_flag_ready and chall1_read.

So I decided to start the analyses from chall1_read. The disassembly of chall1_read is pretty interesting.

.text:0800037D                 cmp     byte ptr [eax], 67h
.text:08000380                 jz      short loc_80003C0
...........
.text:080003C0 loc_80003C0:                            
.text:080003C0                 cmp     byte ptr [eax+1], 69h
.text:080003C4                 jnz     short loc_8000382
.text:080003C6                 cmp     byte ptr [eax+2], 76h
.text:080003CA                 jnz     short loc_8000382
.text:080003CC                 cmp     byte ptr [eax+3], 65h
.text:080003D0                 jnz     short loc_8000382
.text:080003D2                 cmp     byte ptr [eax+4], 5Fh
.text:080003D6                 jnz     short loc_8000382
.text:080003D8                 cmp     byte ptr [eax+5], 74h
.text:080003DC                 jnz     short loc_8000382
.text:080003DE                 cmp     byte ptr [eax+6], 68h
.text:080003E2                 jnz     short loc_8000382
.text:080003E4                 cmp     byte ptr [eax+7], 69h
.text:080003E8                 jnz     short loc_8000382
.text:080003EA                 cmp     byte ptr [eax+8], 73h
.text:080003EE                 jnz     short loc_8000382
.text:080003F0                 cmp     byte ptr [eax+9], 5Fh
.text:080003F4                 jnz     short loc_8000382
.text:080003F6                 cmp     byte ptr [eax+0Ah], 74h
.text:080003FA                 jnz     short loc_8000382
.text:080003FC                 cmp     byte ptr [eax+0Bh], 6Fh
.text:08000400                 jnz     short loc_8000382
.text:08000402                 cmp     byte ptr [eax+0Ch], 5Fh
.text:08000406                 jnz     loc_8000382
.text:0800040C                 cmp     byte ptr [eax+0Dh], 67h
.text:08000410                 jnz     loc_8000382
.text:08000416                 cmp     byte ptr [eax+0Eh], 65h
.text:0800041A                 jnz     loc_8000382
.text:08000420                 cmp     byte ptr [eax+0Fh], 74h
.text:08000424                 jnz     loc_8000382
.text:0800042A                 cmp     byte ptr [eax+10h], 5Fh
.text:0800042E                 jnz     loc_8000382
.text:08000434                 cmp     byte ptr [eax+11h], 66h
.text:08000438                 jnz     loc_8000382
.text:0800043E                 cmp     byte ptr [eax+12h], 6Ch
.text:08000442                 jnz     loc_8000382
.text:08000448                 cmp     byte ptr [eax+13h], 61h
.text:0800044C                 jnz     loc_8000382
.text:08000452                 cmp     byte ptr [eax+14h], 67h
.text:08000456                 jnz     loc_8000382
.text:0800045C                 call    get_flag_ready

So the input here is getting compared with “give_this_to_get_flag” and then after that get_flag_ready is being called. Now taking a look inside get_flag_ready function reveals that 40 bytes are placed on stack (local array) at the starting of the function. There are 2 functions which are called after this namely, md5hash and sha1hash. I think the crucial part of this challenge was determining what were md5hash and sha1hash functions doing.

Code Snippet from md5hash:

.text:0800016F                 push    ebp
.text:08000170                 mov     ebp, esp
.text:08000172                 push    esi
.text:08000173                 push    ebx
.text:08000174                 sub     esp, 78h
.text:08000177                 lea     ebx, [ebp+var_60]
.text:0800017A                 mov     [esp], ebx
.text:0800017D                 call    MD5Init
.text:08000182                 mov     esi, s
.text:08000188                 mov     [esp], esi      ; s
.text:0800018B                 call    strlen
.text:08000190                 mov     [esp+8], eax
.text:08000194                 mov     [esp+4], esi
.text:08000198                 mov     [esp], ebx
.text:0800019B                 call    MD5Update
.text:080001A0                 mov     [esp+4], ebx
.text:080001A4                 lea     esi, [ebp+var_70]
.text:080001A7                 mov     [esp], esi
.text:080001AA                 call    MD5Final
.text:080001AF                 mov     ebx, offset byte_80006B4

After some analysis, it was clear that this function is calculating md5 hash of a string but now the question was which string. As we can see above strlen there is an operand “s”, that is an operand name generated by IDA and it references the input string which was being compared to give_this_to_get_flag. This can be confirmed by checking the chall1_read function. So now its clear that md5hash function calculates the md5 hash of “give_this_to_get_flag” which is 29c5d56f77a0d0369c55101c53005050. 

Code Snippet from sha1hash:

.text:0800021D                 mov     ecx, eax
.text:0800021F                 add     cl, al
.text:08000221                 sbb     edx, offset unk_80006B7
.text:08000227                 mov     [esp+8], edx
.text:0800022B                 mov     dword ptr [esp+4], offset byte_80006B4
.text:08000233                 mov     [esp], ebx
.text:08000236                 call    SHA1Update
.text:0800023B                 mov     [esp+4], ebx
.text:0800023F                 lea     esi, [ebp+var_78]
.text:08000242                 mov     [esp], esi
.text:08000245                 call    SHA1Final

Now if we look carefully inside sha1hash function, we can see that it accesses the previously saved md5 hash and computes the sha1 hash of the md5 string. The sha1hash is 001b6a634bee73d9fe2d88bb4435fb1ee3ad7918.

After computing both md5 and sha1 (of md5) we are back in get_flag_ready_function. We can see from above image that the binary xor’s the earlier copied 40 bytes with the sha1 string we got from the sha1hash function. Here is a simple script to get the flag:

flag = [86, 92, 80, 5, 77, 15, 83, 71, 118, 87, 33, 58, 94, 6, 59, 13, 17, 22, 2, 9, 11, 103, 27, 82, 65, 107, 64, 93, 86, 23, 93, 1, 58, 4, 19, 29, 104, 80, 6, 69]
sha1hash = "001b6a634bee73d9fe2d88bb4435fb1ee3ad7918"

for i in range(0, 40):
    flag[i] ^= ord(sha1hash[i])

print ''.join(map(chr, flag))

This gives us the flag: flag{netB5D_i5_4ws0m3_y0u_sh0uld_7ry_i7}