Microcorruption 0x07 - Whitehorse
Prev: 0x06 Reykjavik
Next: 0x08 Montevideo
According to this manual, this program no longer has the ability to unlock the door, and checking the code confirms that there is no longer an "unlock_door" function. Instead, this program sends the password to the security module within a "conditional_unlock_door" function, and the module itself will unlock the door if the password is correct.
If we check the "login" function, we see the familiar call to "getsn" with "0x30" passed in as the length, meaning we can still enter 48 characters when a max of 16 is expected. Taking what we've learned from the previous levels, let's run the program and enter 48 A's as the password and... yep, we receive the "insn address unaligned" error and a crash. We can now see that "4141" was popped into ret, and sp is pointing into our password string on the stack.
So, we can control program flow with pc by overflowing the intended password buffer, and although we can't exactly jump to an "unlock_door" function anymore, this gives us a lot of options nonetheless. First, let's check the lock manual so we can understand what we're looking for.
Page 10 of the lock manual explains that if we call INT with 0x7F on the stack, we unlock the door. If we check previous levels, we see that this is what our favorite "unlock_door" function did:
So, one way or another, we need to call INT with 0x7F on the stack. There are two ways to accomplish this here:
First, the simplest option: let's overflow the saved return address for main with a return to an instruction that calls "INT." After, we can have "7f" in the string to make it appear as if that value was pushed onto the stack, despite it being place there by us. Note that we must return to an instruction that calls "INT" for this to work, as opposed to to returning to the start of "INT" itself - we can do the latter, but we would need to add our own 2-byte buffer to make up for the lack of return address being pushed onto the stack that results from calling "INT."
If we break at the "ret" instruction at the end of the "login" function, we can confirm that this level still expects the password to be 16 bytes maximum:
We can solve the level by returning to a call to "INT" (address 4460) using this string: "4141414141414141414141414141414160447f" (16 bytes of "A," ret to call to INT, and 7f)
Or, we can return directly into the start of "INT" (address 4532) using this string: "41414141414141414141414141414141324541417f" (16 bytes of "A," ret to INT directly, 2 bytes of "A," and 7f)
However, we still have another option available to us: arbitrary code execution! This is where the fun happens. If we overflow the return address of "main" with the start of our string, the program will execute whatever assembly code we entered there.
From our research into the lock manual and practice with the previous techniques, we already know what code we want to execute: we first want to push 7f onto the stack, and then we want to call the INT function. Enter these instructions (with proper syntax) into the assembler the site provides and you'll have your shellcode:
Next, we need the address of our password string on the stack, which by entering a dummy string we can see is 3838.
Finally, we can combine these elements to get our code: the instructions we generated, plus however many A's are needed to fill the remainder of the 16-byte password buffer (8), plus the address of our password (3838) which will overflow the return address of main.
Enter "30127f00b012324541414141414141413838" to complete the level!
Prev: 0x06 Reykjavik
Next: 0x08 Montevideo
Next: 0x08 Montevideo
According to this manual, this program no longer has the ability to unlock the door, and checking the code confirms that there is no longer an "unlock_door" function. Instead, this program sends the password to the security module within a "conditional_unlock_door" function, and the module itself will unlock the door if the password is correct.
If we check the "login" function, we see the familiar call to "getsn" with "0x30" passed in as the length, meaning we can still enter 48 characters when a max of 16 is expected. Taking what we've learned from the previous levels, let's run the program and enter 48 A's as the password and... yep, we receive the "insn address unaligned" error and a crash. We can now see that "4141" was popped into ret, and sp is pointing into our password string on the stack.
So, we can control program flow with pc by overflowing the intended password buffer, and although we can't exactly jump to an "unlock_door" function anymore, this gives us a lot of options nonetheless. First, let's check the lock manual so we can understand what we're looking for.
Page 10 of the lock manual explains that if we call INT with 0x7F on the stack, we unlock the door. If we check previous levels, we see that this is what our favorite "unlock_door" function did:
So, one way or another, we need to call INT with 0x7F on the stack. There are two ways to accomplish this here:
First, the simplest option: let's overflow the saved return address for main with a return to an instruction that calls "INT." After, we can have "7f" in the string to make it appear as if that value was pushed onto the stack, despite it being place there by us. Note that we must return to an instruction that calls "INT" for this to work, as opposed to to returning to the start of "INT" itself - we can do the latter, but we would need to add our own 2-byte buffer to make up for the lack of return address being pushed onto the stack that results from calling "INT."
If we break at the "ret" instruction at the end of the "login" function, we can confirm that this level still expects the password to be 16 bytes maximum:
We can solve the level by returning to a call to "INT" (address 4460) using this string: "4141414141414141414141414141414160447f" (16 bytes of "A," ret to call to INT, and 7f)
Or, we can return directly into the start of "INT" (address 4532) using this string: "41414141414141414141414141414141324541417f" (16 bytes of "A," ret to INT directly, 2 bytes of "A," and 7f)
However, we still have another option available to us: arbitrary code execution! This is where the fun happens. If we overflow the return address of "main" with the start of our string, the program will execute whatever assembly code we entered there.
From our research into the lock manual and practice with the previous techniques, we already know what code we want to execute: we first want to push 7f onto the stack, and then we want to call the INT function. Enter these instructions (with proper syntax) into the assembler the site provides and you'll have your shellcode:
Next, we need the address of our password string on the stack, which by entering a dummy string we can see is 3838.
Finally, we can combine these elements to get our code: the instructions we generated, plus however many A's are needed to fill the remainder of the 16-byte password buffer (8), plus the address of our password (3838) which will overflow the return address of main.
Enter "30127f00b012324541414141414141413838" to complete the level!
Prev: 0x06 Reykjavik
Next: 0x08 Montevideo