I though that I published this writeup right after the end of the CTF, but apparently I forgot. The task wasn't hard at all, and the only point of this blogpost is to show how to use radare2 to get things done™.
$ r2 -A kernel
DWARF: version 4 is yet not supported
DWARF: version 4 is yet not supported
DWARF: version 4 is yet not supported
DWARF: version 4 is yet not supported
DWARF: version 4 is yet not supported
[0x001005f0]> iI~class
class ELF32
[0x001005f0]>
So, it seems that the file is a x86 ELF binary, likely some kernel-related thing. The challenge said:
The third Tick gives you the answer ;) http://41.231.53.40/kernel
[0x001005f0]> is~tick
addr=0x001060c0 off=0x000060c0 ord=082 fwd=NONE sz=4 bind=GLOBAL type=OBJECT name=tick
addr=0x00100958 off=0x00000958 ord=107 fwd=NONE sz=72 bind=GLOBAL type=FUNC name=timer_tick
The is command stands for Information about Symbols, while the tild is used in radare2 as a grep operator.
[0x001005f0]> pdf@timer_tick
Invalid address (timer_tick)
[0x001005f0]> pdf@sym.timer_tick
│ ; UNKNOWN XREF from 0x00100951 (unk)
│ ; DATA XREF from 0x000019a3 (fcn.000019a0)
│ ; DATA XREF from 0x001009a3 (unk)
0x00100958 83ec14 sub esp, 0x14
0x0010095b a1c0601000 mov eax, [sym.tick]
0x00100960 8d5001 lea edx, [eax+0x1]
0x00100963 8915c0601000 mov [sym.tick], edx
0x00100969 50 push eax
0x0010096a 6803221000 push str._d_Tick.._n ; str._d_Tick.._n
0x0010096f e8d5050000 call sym.printf
0x00100974 0fb60dc0601. movzx ecx, byte [sym.tick]
0x0010097b 83c410 add esp, 0x10
0x0010097e b800000000 mov eax, 0x0
│ ; JMP XREF from 0x0010099a (unk)
0x00100983 89ca mov edx, ecx
0x00100985 329060301000 xor dl, [eax+sym.flag]
0x0010098b 83c201 add edx, 0x1
0x0010098e 889060301000 mov [eax+sym.flag], dl
0x00100994 83c001 add eax, 0x1
0x00100997 83f828 cmp eax, 0x28
0x0010099a 75e7 jne loc.00100983
0x0010099c 83c40c add esp, 0xc
0x0010099f c3 ret
[0x001005f0]>
This seems to be a decryption function, something like:
for i in range(0x28):
A[i] = (A[i] ^ tick) + 1
Let's take a look at the sym.flag global variable with the pxa command (Print heXdump Annotated).
[0x001005f0]> pxa @sym.flag
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
/sym.flag
0x00103060 4974 6f66 726a 7862 3260 2e2e 632e 322e Itofrjxb2`..c.2.
0x00103070 3630 3331 5d67 3662 3167 6730 5e29 6231 6031]g6b1gg0^)b1
/sym.color
0x00103080 3163 625e 5e2d 5d7a 0000 0000 0f00 0000 1cb^^-]z........
/sym.video/section_end..data
0x00103090 0080 0b00 ff08 0000 0400 0000 0000 0401 ................
0x001030a0 d900 0000 01d4 0100 0064 0000 000c 0010 .........d......
0x001030b0 0029 0500 0000 0000 0002 6e02 0000 0206 .)........n.....
0x001030c0 3000 0000 0304 0780 0300 0004 0405 696e 0.............in
0x001030d0 7400 0281 0200 0002 0849 0000 0003 0207 t........I......
0x001030e0 4900 0000 0302 0592 0100 0002 9900 0000 I...............
0x001030f0 020a 6200 0000 0301 0836 0000 0003 0106 ..b......6......
0x00103100 3f00 0000 02b7 0000 0003 217b 0000 0005 ?.........!{....
0x00103110 0481 0000 0006 8c00 0000 078c 0000 0000 ................
0x00103120 0504 9200 0000 0800 0000 0009 1700 0000 ................
0x00103130 0803 32e0 0000 000a c901 0000 0334 3e00 ..2..........4>.
0x00103140 0000 000a 2100 0000 0335 3e00 0000 020a ....!....5>.....
0x00103150 9000 0000 0336 5700 0000 040a cd00 0000 .....6W.........
Unfortunately, I can't show you the rights colours, but the hexdump is highlighting variables.
Protip: You can directly dump sym.flag to Python with three commands:
- s sym.flag to Seek to sym.flag
- pcp 0x28 to Print Code Python
- s- to undo Seek
[0x001005f0]> s sym.flag;pcp 0x28;s-
import struct
buf = struct.pack ("40B",
0x49,0x74,0x6f,0x66,0x72,0x6a,0x78,0x62,0x32,0x60,0x2e,
0x2e,0x63,0x2e,0x32,0x2e,0x36,0x30,0x33,0x31,0x5d,0x67,
0x36,0x62,0x31,0x67,0x67,0x30,0x5e,0x29,0x62,0x31,0x31,
0x63,0x62,0x5e,0x5e,0x2d,0x5d,0x7a)
[0x001005f0]>
The hint was that the third Tick gives the answer. Since sym.flag is a global variable, the decryption routine has to be repeated 4 times (since there is 3 ticks) to give us the flag.
Here is the final keygen:
flag = [
0x49,0x74,0x6f,0x66,0x72,0x6a,0x78,0x62,0x32,0x60,0x2e,
0x2e,0x63,0x2e,0x32,0x2e,0x36,0x30,0x33,0x31,0x5d,0x67,
0x36,0x62,0x31,0x67,0x67,0x30,0x5e,0x29,0x62,0x31,0x31,
0x63,0x62,0x5e,0x5e,0x2d,0x5d,0x7a]
for i in range(1, 4):
for x in range(0x28):
flag[x] = (flag[x]^i)+1
print ''.join((chr(i) for i in flag))
And here you go:
$ python keygen.py
Pwnium{e5c11b1519328df9e8ff3a0e88beaa4d}