Hack.lu 2k13 RoboAuth - reverse150

Published on Thursday, 24 October 2013 in CTF, Security, Reverse Engineering ; tagged with hack.lu, ctf, write-up, reverse, 150, challenge, security, ida, int3, sigtrap, hackgyver ; text version

Hack.lu's CTF

Bonjour les gens !

The last two days, we have seen Hack.lu's CTF taking place online.
It was a lot of fun, their IRC channel was really fun, so was their challenges :)

Last results? 106 over 413 applying teams. Well done HackGyver \o/

Now it's time for the write-up. More precisely, the one on RoboAuth, their 150 points reverse challenge.

RoboAuth, the challenge

RoboAuth challenge

The challenge is a binary which asks for two distinct passwords. I salute the ASCII art :P
Since futex powned ndh2K13 prequals' challenge (crackme200) with a simple strings command, I will start from the beginning :p

~/VirtualBox VMs/Windows 7 Work/hacklu » file RoboAuth.exe
RoboAuth.exe: PE32 executable (console) Intel 80386 (stripped to external PDB), for MS Windows

As expected, the first step is running the file command on that bin.
It tells us that we are dealing with a PE file, for 32bits architectures and that it is also stripped to only contain public symbols.

Second step? :)

~/VirtualBox VMs/Windows 7 Work/hacklu » strings RoboAuth.exe
[^_]
r0b0
RUlef
D$$t
L[^_]
L[^_]
[^_]
:MZt
UWVS
[^_]
[^_]
@' t
|$D=N
3|$(3|$,1
UWVS

[. . .]

l$,vA
<[^_]
D$$t.
<[^_]
UWVS
[^_]
[^_]
r/9D$
_set_invalid_parameter_handler
%20s
            .
           _|_    ROBOTIC AUTHENTICATION SYSTEM
    /\/\  (. .)  /
    `||'   |#|
     ||__.-"-"-.___
     `---| . . |--.\
         | : : |  ,||,
         `..-..'  \/\/
          || ||
          || ||
         |__|__|
You passed level1!
Unknown error
_matherr(): %s in %s(%g, %g)  (retval=%g)
Argument domain error (DOMAIN)
Argument singularity (SIGN)
Overflow range error (OVERFLOW)
The result is too small to be represented (UNDERFLOW)
Total loss of significance (TLOSS)
Partial loss of significance (PLOSS)
Mingw-w64 runtime failure:
Address %p has no image-section
  VirtualQuery failed for %d bytes at address %p
  VirtualProtect failed with code 0x%x
  Unknown pseudo relocation protocol version %d.
  Unknown pseudo relocation bit size %d.
(null)
PRINTF_EXPONENT_DIGITS
Infinity
?aCoc
<2ZGU
?_set_output_format
_get_output_format
vH7B
W4vC
[%Co
O8M2
___lc_codepage_func
__lc_codepage
DeleteCriticalSection
EnterCriticalSection
ExitProcess
GetCurrentProcess
GetCurrentProcessId
GetCurrentThreadId
GetLastError
GetModuleHandleA
GetProcAddress
GetStartupInfoA
GetSystemTimeAsFileTime
GetTickCount
InitializeCriticalSection
IsDBCSLeadByteEx
LeaveCriticalSection
LoadLibraryW
MultiByteToWideChar
QueryPerformanceCounter
SetUnhandledExceptionFilter
Sleep
TerminateProcess
TlsGetValue
UnhandledExceptionFilter
VirtualProtect
VirtualQuery
WideCharToMultiByte
__dllonexit
__getmainargs
__initenv
__lconv_init
__mb_cur_max
__set_app_type
__setusermatherr
_acmdln
_amsg_exit
_cexit
_errno
_fmode
_initterm
_iob
_lock
_onexit
_unlock
abort
atoi
calloc
exit
fputc
free
getenv
localeconv
malloc
memcpy
puts
scanf
setlocale
signal
strchr
strcmp
strerror
strlen
strncmp
wcslen
KERNEL32.dll
msvcrt.dll

Second step, strings command! (futex that's for you!)
Some strings look interesting:

Well, I don't know if I already told you but reverse is not one of my best skills (sleep? yeah, sleep is definitly one of my best skills).
Therefore please excuse my strategy for the next steps :)

Into its binary heart

RoboAuth strcmp reference

I start by looking for the strcmp's references in IDA. Only one occurence, good for me :)
Let us break on the two mov before the call of strcmp. They will certainly contain the two strings that will be compared.

RoboAuth first password

Let's try r0b0RUlez! as the first password then!

RoboAuth first password is
good

Nice! Well I admit, the first password was quite easy to find but let's continue and see what happens :)

Password 2! Stop hiding like a robot ninja!

RoboAuth continuing after the
strcmp

The red line is a highlight to what bothered me. Fortunately jvoisin explained it to me :)

When you are debugging a binary and that you set some breakpoints, your debugger will in fact replace the instruction by an INT3, which is a SIGTRAP. Then, when that signal will be raise, the debugger will handle it and stops. Hence the break :) But if the guy who coded the binary already wrote some int 3 inside of his code, the debugger will not discern its own and the guy's one. If you have some lame debugger, it will even handle the breakpoints that are not its own. The trick here is that the guy will define a custom handler which will do some stuff when it will be called by the binary itself.

RoboAuth break point
exception

RoboAuth IDA is smart

I'm glad to learn how IDA is smart :)
Since a trap debugger is not called a butterfly, but a trap debugger, I will let the application handle the signal!
If you remember the strings command's results, it contained some strings like:

Let's continue...

RoboAuth check function
call

After a while, we stumble upon an interesting part, where:

We can presume that this function is indeed responsible for the check of the second password.
Let's inspect it!

RoboAuth check function
code

For the following, I wanted to statistically understand it, like a challenge in a challenge (Yo Dawg! :D).
Remembering the explanations from jvoisin about reverse stuff, I will go step by step.

First of all, the ESP stack pointer will be placed into EBP and arg_0 and arg_4 will be some kind of indexes/pointers for each strings.

RoboAuth check function exit
block

The first letter of the ciphered string will be placed into EAX register:

RoboAuth check function xor
block

The first character of each strings will be respectively placed into EDX and EAX. EAX is XORed with the key 0x2.
Finally, EAX and EDX will be compared, which means the first characters of each strings:

We clearly understand that our input must match the 0x2-XORed string in order to pass.
Here 0 means SUCCESS!

Let us use some python in order to retreive the deciphered password:

~/ctf/hacklu/roboauth » python2 -c "print ''.join([chr(ord(c) ^ 0x2) for c in 'u1nnf2lg'])"
w3lld0ne

Let's now try it!

RoboAuth password 2 ok

Oh, yeah! Those sweet sweet words!

Therefore the flag: r0b0RUlez_w3lld0ne \o/

RoboAuth challenge validation

Might be interested: jvoisin prefers using SIGSEV signals instead of SIGTRAP ones :p Bonus: Robot Pirates (music) :)


contactdepier.re License WTFPL2