Microcorruption 0x0F - Bangalore
Prev: 0x0E Vladivostok
Next: 0x20 Lagos
This level is the first to use what is referred to as DEP: Data Execution Prevention. We can see how it works in the "set_up_protection" function that "main" calls before "login" (and thus before we can provide any input).
First, "mark_page_executable" is called after r15 is cleared. Then, a loop is setup: 0x1 is moved into r11, then to r15, "mark_page_writable" is called, r11 is incremented, and everything repeats while r11 is less than 0x44. Then, the same occurs with "mark_page_executable," looping from values 0x44 through 0x99, before calling a "turn_on_dep" which sets up an interrupt.
Given that the first byte passed into "mark_page_executable" is 0x44, and that the code section of our program begins at address 4400, we can probably assume that this bytes refer to the most significant bytes in our program's memory addresses. So, DEP defines which sections of memory are writable, and which sections of memory are executable, with no overlap: if we can write to an address, we cannot execute its instructions, and vice-versa.
After the DEP is activated, we step into "login" which prints two strings requesting the password from us, retrieves 0x30 bytes of input from us, and... for some reason, neglects to call "conditional_unlock_door" (which we can see isn't a complete function anyway) and instead tells us the password is incorrect regardless of our input.
Interesting - the program is not even complete, meaning we'll absolutely need to generate our own shellcode to unlock the door directly as there are no desirable functions to jump to.
If we check the stack for our string, we find it at 3fee.
Checking the "set_up_protection" function once more, we can see that this address is marked as writable and not executable. So, even if we overwrite what appears to be a return address on the stack after 0x10 bytes of our password (the "4044"), we cannot return to our password buffer and execute instructions. We'll need to find some way to mark our shellcode as executable first.
Let's try to better understand how the "mark_page_executable" function works. We can see that two values are pushed to the stack before sp is changed and the interrupt is called with 0x9100 in sr.
From reviewing "set_up_protection" earlier, we know r15 is passed into "mark_page_executable" as the most significant byte of the memory addresses to mark as executable. If we break on the "call 0x10" instruction within "mark_page_executable" the second time it is run (when it uses 0x44 instead of 0x00, which may be confusing for us to analyze), we can examine the stack to confirm the layout.
sp points at two null bytes, followed by four more, then our "0x44" byte. If can overwrite a return address with something in the middle of "mark_page_executable" with the stack structured in just the right way, but with "0x3f" 6 bytes ahead of sp instead of "0x44," we may be able to accomplish our objective of setting the "3fXX" address as executable.
Let's assemble a test payload:
If we enter "41414141414141414141414141414141ba443f00" we take our return into "mark_page_executable" as planned. If we also pause at the "call 0x10" instruction and examine the stack, we find that "3f00" is indeed 6 bytes from sp as the function expects.
In theory, this should have worked, so let's amend our payload so that we return into our string and see if the program tries to execute our As, or if it throws an error stating that it cannot be executed. If we break at the "ret" instruction at the end of "mark_page_executable" and examine our stack again, we see that sp is pointing two bytes after the end of our string.
Let's assemble our new test payload:
Given that the "conditional_unlock_door" function is incomplete on this level, let's reference a prior level which utilizes "unlock_door" to remind ourselves of how it works. I used "Jakarta."
Based on the instructions, it looks like "7f" is passed into "INT," which then swaps the bytes, pops it into the sr register, and performs a "bis" instruction (bitwise OR) on it before calling 0x10.
In short: we need "ff00" in the sr register (the value of 0x7f00 OR 0x8000), and then we need to call 0x10. Here's the final payload:
Next: 0x20 Lagos
This level is the first to use what is referred to as DEP: Data Execution Prevention. We can see how it works in the "set_up_protection" function that "main" calls before "login" (and thus before we can provide any input).
First, "mark_page_executable" is called after r15 is cleared. Then, a loop is setup: 0x1 is moved into r11, then to r15, "mark_page_writable" is called, r11 is incremented, and everything repeats while r11 is less than 0x44. Then, the same occurs with "mark_page_executable," looping from values 0x44 through 0x99, before calling a "turn_on_dep" which sets up an interrupt.
Given that the first byte passed into "mark_page_executable" is 0x44, and that the code section of our program begins at address 4400, we can probably assume that this bytes refer to the most significant bytes in our program's memory addresses. So, DEP defines which sections of memory are writable, and which sections of memory are executable, with no overlap: if we can write to an address, we cannot execute its instructions, and vice-versa.
After the DEP is activated, we step into "login" which prints two strings requesting the password from us, retrieves 0x30 bytes of input from us, and... for some reason, neglects to call "conditional_unlock_door" (which we can see isn't a complete function anyway) and instead tells us the password is incorrect regardless of our input.
Interesting - the program is not even complete, meaning we'll absolutely need to generate our own shellcode to unlock the door directly as there are no desirable functions to jump to.
If we check the stack for our string, we find it at 3fee.
Checking the "set_up_protection" function once more, we can see that this address is marked as writable and not executable. So, even if we overwrite what appears to be a return address on the stack after 0x10 bytes of our password (the "4044"), we cannot return to our password buffer and execute instructions. We'll need to find some way to mark our shellcode as executable first.
Let's try to better understand how the "mark_page_executable" function works. We can see that two values are pushed to the stack before sp is changed and the interrupt is called with 0x9100 in sr.
From reviewing "set_up_protection" earlier, we know r15 is passed into "mark_page_executable" as the most significant byte of the memory addresses to mark as executable. If we break on the "call 0x10" instruction within "mark_page_executable" the second time it is run (when it uses 0x44 instead of 0x00, which may be confusing for us to analyze), we can examine the stack to confirm the layout.
sp points at two null bytes, followed by four more, then our "0x44" byte. If can overwrite a return address with something in the middle of "mark_page_executable" with the stack structured in just the right way, but with "0x3f" 6 bytes ahead of sp instead of "0x44," we may be able to accomplish our objective of setting the "3fXX" address as executable.
Let's assemble a test payload:
- 16 bytes of "A" to fill the buffer
- "ba44" to return to the portion of "mark_page_executable" following the "push" instructions
- "3f00" to provide "3f" as the memory section to mark executable
If we enter "41414141414141414141414141414141ba443f00" we take our return into "mark_page_executable" as planned. If we also pause at the "call 0x10" instruction and examine the stack, we find that "3f00" is indeed 6 bytes from sp as the function expects.
In theory, this should have worked, so let's amend our payload so that we return into our string and see if the program tries to execute our As, or if it throws an error stating that it cannot be executed. If we break at the "ret" instruction at the end of "mark_page_executable" and examine our stack again, we see that sp is pointing two bytes after the end of our string.
Let's assemble our new test payload:
- 16 bytes of "A" to fill the buffer
- "ba44" to return to "mark_page_executable"
- "3f00" to provide "3f" as the memory section to mark executable
- 2 bytes of "A" to fill space before the return address
- "ee3f" to return to the start of our string
The final payload is "41414141414141414141414141414141ba443f004141ee3f." If we test this, it works! We return to our "A" bytes and we begin executing a bunch of "mov.b sp, sp" instructions. Perfect. Now all we need is to create some shellcode.
Given that the "conditional_unlock_door" function is incomplete on this level, let's reference a prior level which utilizes "unlock_door" to remind ourselves of how it works. I used "Jakarta."
Based on the instructions, it looks like "7f" is passed into "INT," which then swaps the bytes, pops it into the sr register, and performs a "bis" instruction (bitwise OR) on it before calling 0x10.
In short: we need "ff00" in the sr register (the value of 0x7f00 OR 0x8000), and then we need to call 0x10. Here's the final payload:
- "301200ff" - push 0xff00
- "3241" - pop sr
- "b0121000" - call 0x10
- 6 bytes of "A" to fill the buffer
- "ba44" to return to "mark_page_executable"
- "3f00" to provide "3f" as the memory section to mark executable
- 2 bytes of "A" to fill space before the return address
- "ee3f" to return to the start of our string
Enter "301200ff3241b0121000414141414141ba443f004141ee3f" and we're in!