Microcorruption CTF Cusco Write-up

Microcorruption CTF Cusco Write-up

- 8 mins


This is a write-up of my solution to the Microcorruption CTF challenge “Cusco” (LOCKIT PRO r b.02).

This version claims to fix the conditional flag overwrite issue that we exploited in the last challenge:

This is Software Revision 02. We have improved the security of the
lock by  removing a conditional  flag that could  accidentally get
set by passwords that were too long.

This indeed appears to be the case. Let’s check out the entire program and then see if we can figure out what’s going on:

So I’ll admit, this one took me a little longer to figure out. Let’s go through the basic steps I took to see where I got stumped:


4438 <main>
4438:  b012 0045      call	#0x4500 <login>

All main() does is make a call to login(). Let’s follow login()


4500 <login>
4500:  3150 f0ff      add	#0xfff0, sp
4504:  3f40 7c44      mov	#0x447c "Enter the password to continue.", r15
4508:  b012 a645      call	#0x45a6 <puts>
450c:  3f40 9c44      mov	#0x449c "Remember: passwords are between 8 and 16 characters.", r15
4510:  b012 a645      call	#0x45a6 <puts>
4514:  3e40 3000      mov	#0x30, r14
4518:  0f41           mov	sp, r15
451a:  b012 9645      call	#0x4596 <getsn>
451e:  0f41           mov	sp, r15
4520:  b012 5244      call	#0x4452 <test_password_valid>
4524:  0f93           tst	r15
4526:  0524           jz	#0x4532 <login+0x32>
4528:  b012 4644      call	#0x4446 <unlock_door>
452c:  3f40 d144      mov	#0x44d1 "Access granted.", r15
4530:  023c           jmp	#0x4536 <login+0x36>
4532:  3f40 e144      mov	#0x44e1 "That password is not correct.", r15
4536:  b012 a645      call	#0x45a6 <puts>
453a:  3150 1000      add	#0x10, sp
453e:  3041           ret

After a few calls to puts() to print out the password prompt message, the code makes a call to test_password_valid(). Upon returning, login() tests the value of r15 (at 0x4524). If r15 is not 0, we skip the jz instruction at 0x4526 and proceed to unlock_door(). Let’s pay attention to r15 while test_password_valid() is executing…


4452 <test_password_valid>
4452:  0412           push	r4
4454:  0441           mov	sp, r4
4456:  2453           incd	r4
4458:  2183           decd	sp
445a:  c443 fcff      mov.b	#0x0, -0x4(r4)
445e:  3e40 fcff      mov	#0xfffc, r14
4462:  0e54           add	r4, r14
4464:  0e12           push	r14
4466:  0f12           push	r15
4468:  3012 7d00      push	#0x7d
446c:  b012 4245      call	#0x4542 <INT>
4470:  5f44 fcff      mov.b	-0x4(r4), r15
4474:  8f11           sxt	r15
4476:  3152           add	#0x8, sp
4478:  3441           pop	r4
447a:  3041           ret

After shifting values inside of various registers/memory, the instruction mov.b -0x4(r4), r15 eventually sets a 0 value into r15. The instruction sxt r15 (sign extend) is executed on r15 (effectively doing nothing since the value is 0x0), and soon returns back into login(), where r15 is tested against 0. It does not appear that we have any influence over the memory address that’s moved into r15 :anguished:.

After racking my brain for a while, and continuously stepping through various parts of program, I noticed something interesting…

Cusco SP PC distance

This version of the code isn’t storing our input at 0x2400 as it was originally, and instead is much closer to the program counter! Notice how close our user input (AAAAAAAAAA) is to pc. I wonder if we can overwrite parts of the program code! Let’s try a bunch of A's and see what happens…

Cusco PC code overwritten

Looks like we can! Another interesting thing to note, I tried to input about 200 or so A's, but only 48 were copied onto the stack. I suspect maybe during the copy onto the stack, __do_copy_data() is being invoked and we’re overwriting instructions as we’re in the middle of executing them? I wasn’t able to step the code to this level of precision so I’m not sure. However, this is sort of a moot point because of what happens as a result of inputting so many bytes…

Cusco PC overwritten

We can control the pc register. Sweet! Now all we need to do is to figure out the byte offset to reach pc and figure out a suitable location to jump the execution to. After some experimenting with input strings of various lengths and keeping an eye on the pc register, we can see that the 17th and 18th byte of our input will overflow into the pc register. Let’s try overwriting pc with 0x4528, the call #0x4446 <unlock_door> instruction inside login() which comes right after the jz instruction we were trying so hard to bypass…

Cusco Solved

:+1: :beers:

Flag (mouse over to reveal)




Security guy, busticati, professional button-pusher

comments powered by Disqus
rss keybase facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora quora