Wednesday, May 26, 2021

Cusco

My intros are getting thin...

Break Main

continue

Main is very simple, call login function


login function


Here I am initially interested in the test password valid function, but I'll step through both the getsn and test password valid. Breakpoints at both, continue to getsn, and step into it.

get sn looks very simple:


I'll break at the INT function, continue to it and step in.


INT looks interesting. It appears to be calling a function at 0x10 (at 4552), unnamed similar to the previous challenge. Looking at memory, there doesn't appear to be much there, just \x3041


Turns out that that's the return instruction.


Which just returns us right back into the INT function... why?

After we complete INT, we return back to getsn

Interestingly, it looks like we return to puts, and not getsn 


It looks like puts here will modify a few registers, jumps backup to the top, and will end up calling INT again, and this loops. So I'll break just after the jnz and continue through the loop.


Once out of the loops I step through the remainder, and I am put back up towards the top of login function, at 450c, which is confusing to me, because we continued past that. 

I reset the program, stepped through again, and this time the behavior is different. When I reset again, it's different a third time. I am assuming this is some developer fun that is meant to confuse me. However all of this occurs before the password prompt, so I'll remove my breakpoints for getsn, and then continue right into the password promt, and step through from there.

RESET

continue

input test password


Looking at memory immediately after prompt, I see my password stored at 0x43ee. I do not see a register pointed to it; however, sp is pointed to 43e2, just before it.


Stepping through we immediately add \x06 to sp and change sp to point two bytes before my password location.


When we ret back to login function, for some reason sp changes again, and this time, it is now pointing to my password. I'm not sure how a ret instruction changed sp here... BUT we then move sp into r15, and call the test_password_valid function.


Once in the test password valid function, the first thing I notice is that there are no cmp instructions. So I'll have to pay extra close attention as I step through.


First up is pushing r4, then mov sp into it then incrementing it. r4 now points 2 bytes before password.

No we decrement sp and then more \x00 into -4 r4, which is where sp is pointed.

We then shove fffc into r14, and add r4 to it to make it 43e8, which is where sp is currently pointed.

push r14 and r15 and \x7d then call int


As I step through this test password, I see nothing testing my password. and then I eventually exit, where it tst r15 followed by a jz. So I need r15 to equal 1 in order to unlock the door.


So I decide to reset, and pay extra attention to r15, and try and determine how it is set. It doesn't appear to be comparing against a string that I can tell. 

RESET

Walking through, it looks like the last time r15 is set, is at 4470, where we move -0x4(r4) into it. Which is zero at the time. So I reset, and pay special attention to r4.

RESET

I notice that r4 is ultimately set via this instruction.


Since it nulls out that byte, I'm currently trying to think of says that I could possibly get that byte to by \x01 after that set. 

After a few more rounds, I can't figure out how I can affect r4, it just doesn't look like it is comparing it against a password, and that it's just hard coded to null out r14 and then tst against that. And so I start looking for other options rather than trying to cmp the passwords together. 

After a little bit, I notice that the address space where the instructions are executing are after the memory location that the password is stored—and not tooooo far after. Soooooo, I'm currently thinking overflow and execute jmp 4528? Or fill the buffer with nops all the way up to 4528?

So I reset, and throw a million As into the prompt.


Looking at memory after the prompt, it certainly does accept > 16 chars; however, it does appear to truncate my input to 48 chars. 


So now I step through, hoping that it attempts to execute instruction 4141 and crashes. No luck on the attempted overflow as there is no 4141 execution attempt.


I finish stepping through, fail the check obviously, reset, and try the same thing again. 60x As into input, and try again.

Except this time I run into this...

If I'm looking at the state at the time of crash, r4 is 0000, and its trying to write -0x4(r4) to r15. Which doesn't exist.

Somehow, r4 has changed?

Reset. Try 60x As again?

This time it looks happy


After many attempts I was unable to get it to crash :(

After MANY attempts of stepping through execution, I came across this...


At the very end of execution, AFTER it was already put "This password is not correct", we jmp to 443c,
execute a bis instruction, then execute stop progexec function.

Buffer overflow back on the table!

So I reset, fill with As and check to see if it can control 443c


BOOM, crash, now, I just need to replace the nops with call 0x4446?

So new pass, 40x nops with 1x b012 5244, hopefully I get endian correct first try...


So here is memory after input:


I remove all break points until right before the ret out of login (when we execute the nops).

And then I slowly step through and watch the current instruction.

Hmmm

Crashes.


Ok, so now I'm thinking ditch the sled, and land b012 5244 RIGHT on 443c.

My password is stored at 43ee, so 18 buffer chars, and then b012 5244?

RESET

new pass


Hmmm

My stack pointer is located at 4141... so I did something wrong. My hexi math is off.


I clearly have too many As.

So after manually adjusting where I put my instruction, I just put in all hex chars to determine where it is attempting to execute. 


Looks like, at the time of crash, its attempting to execute 1211:


So I truncate my hex to that, and replace 1112 with 4141, also looks like I'll need to reverse my bits for endianness.

RESET

new pass


So if I am correct, we will crash with the application attempting to execute 4141.


Success!

Replace my 4141 with my call instruction?

RESET

New Pass



Success!

...
...

Step again,


That's not right.

So whoops. Should've known. I do not need to input instructions to execute, just a memory address to execute. So "call 4446 Unlock_door" is located at 4528, lets put that in instead....


RESET

new pass (reverse bits for endianness)
 



Hey Now! GOT IT.

Jumped right into Unlock door

Step through the rest of the way!



FIN.

Notes:
This was crazy fun. Definitely the best challenge so far. And WAY easier than Reykjavik. I really enjoy working on BO challenges, and I DEFINATELY walked around it for a while. I really do wonder who coded this lock, doesn't seem usable if it is hard coded to always fail the password prompt. I guess they just use the physical key to get in?

I thought it was BO, then I didn't, and I really shot myself in the foot by not stepping through to the end the first 4 dozen times. I would reset after it put incorrect password to the screen, never getting to the execution inside of the overflowed memory space. Lesson learned I guess.

Share:

0 comments:

Post a Comment