title: Hack.lu 2k13 RoboAuth - reverse150 author: depierre published: 2013-10-24 categories: CTF, Security, Reverse Engineering keywords: hack.lu, ctf, write-up, reverse, 150, challenge, security, ida, int3, sigtrap, hackgyver Hack.lu's CTF ============= Bonjour les gens ! The last two days, we have seen [Hack.lu's CTF](http://2013.hack.lu/index.php/Main_Page) taking place online. It was a lot of fun, their IRC channel was really fun, so was their challenges :) Last results? [__106 over 413__](https://ctf.fluxfingers.net/scoreboard) applying teams. __Well done [HackGyver](http://www.hackgyver.org/)__ \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](/static/images/hacklu/roboauth_challenge.png) 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 :::bash ~/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? :) :::bash ~/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: * __r0b0__ and __RUlef__ which could be the first and second password (or not) * __You passed level1!__ which we can later find in the debugger * __strcmp__ and __strncmp__ which on what we could break and see the parameters 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](/static/images/hacklu/roboauth_ref_strcmp.png) 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](/static/images/hacklu/roboauth_password1_eax.png) Let's try __r0b0RUlez!__ as the first password then! ![RoboAuth first password is good](/static/images/hacklu/roboauth_password1_ok.png) 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](/static/images/hacklu/roboauth_strcmp_continue.png) The red line is a highlight to what bothered me. Fortunately [jvoisin](https://www.dustri.org/b/) 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](/static/images/hacklu/roboauth_bp_exception.png) ![RoboAuth IDA is smart](/static/images/hacklu/roboauth_ida_is_smart.png) 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: * SetUnhandledExceptionFilter * UnhandledExceptionFilter Let's continue... ![RoboAuth check function call](/static/images/hacklu/roboauth_check_function_call.png) After a while, we stumble upon an interesting part, where: * It asks the user an input * Call a function (well, already labelized by myself, sorry for the spoil :/) * Then a branch where * on one hand, it puts something and exit * on the other hand, directly exit :p We can presume that this function is indeed responsible for the check of the second password. Let's inspect it! ![RoboAuth check function code](/static/images/hacklu/roboauth_check_function_code.png) 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](/static/images/hacklu/roboauth_check_exit.png) The first letter of the ciphered string will be placed into EAX register: * If it is 0x2 (the NUL-byte-like terminating character), we exit with the code 0 * Otherwise we continue ![RoboAuth check function xor block](/static/images/hacklu/roboauth_check_xor.png) 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: * They are different, we exit with the code 1 * Otherwise, the two counters are incremented and we continue 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: :::bash ~/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](/static/images/hacklu/roboauth_password2_ok.png) Oh, yeah! Those sweet sweet words! Therefore the flag: __r0b0RUlez_w3lld0ne \o/__ ![RoboAuth challenge validation](/static/images/hacklu/roboauth_success.png) __Might be interested:__ [jvoisin prefers using SIGSEV signals instead of SIGTRAP ones](http://depier.re/crackme_0x1d01ebcc/) :p __Bonus:__ [Robot Pirates (music)](http://youtu.be/-XLgpReEkLc) :)