# Debugging It's far easier to learn to operate a debugger with a dashboard of widgets. The difference between this and stock `gdb` is like Nethack vs Zork. Sure, you could get by without the visualizations, but this requires keeping the relevant information in your head and makes it harder to spot patterns. The most fundamental tool you have in your toolbox is the breakpoint. With it you put stop signs on your program and can skip the boring parts easily. Typically you put breakpoints on functions, but you can put them on absolute addresses as well. Note that if you `reset` the debugger (as opposed to rebooting it), breakpoints are preserved, this makes it easy to try a different way of executing the program. Another pattern that comes up is that you create a breakpoint to skip ahead to some point of the program, then undo it later to inspect that part of the program more closely. Stepping through code is crucial to get right. You can `continue` execution to proceed to the next breakpoint or user input action. To proceed one instruction, use `step` which is also known as `step in`. This is because when faced with a call to a function, `step` would step into it, with the next instruction being inside that function. To avoid this there's `next` which is also known as `step over`, it behaves almost the same except that if faced with a call, it will not enter the function, but step over it (so that you stay at the same code snippet). If you find yourself in a function you'd want to get out quickly, use `finish`, this will execute instructions until its end. There are short forms of these commands, `c` (`continue`), `s` (`step`), `n` (`next`), `f` (`finish`). To repeat the last command use the enter key. It's possible to repeat commands with a numerical argument and to define macros (mostly useful for more complex repetitive patterns), but I haven't made use of these yet. To understand how memory can be corrupted in ways useful to an attacker, it's important to know how a running program is represented in memory. For this, it's useful to track the memory and register views as the program is running. You'll find that in this challenge there are separate regions of memory dedicated to the stack, heap and program code. If the memory layout permits it, interesting things can happen when the attacker writes beyond the designated boundaries. For example if the stack pointer points to attacker-controlled memory, anything reading memory from the stack will be influenced, such as the return address to the last function (which could be changed to jump to a different function). Another example is overwriting adjacent stack memory to change a local variable's contents. It's even possible to put machine code (also known as shellcode because it's typically designed to spawn a shell) on the stack, then jump back to it, provided that the stack is executable. Endianness is a subtle point here. This architecture is little-endian, so the least significant byte comes first. The value 256 for example is encoded as the bytes `0x00` and `0x01`. This is important to know when trying to make sense of numbers in the disassembly and interpreting user input (like, when entering an address). # Levels ## Tutorial - Check whether user input is 8 chars long ## New Orleans - Decode password into some place in memory - Compare user input against that password ## Sydney - Compare every 16 bits of user input against four constants - The data is interpreted little-endian, so you can't just put the constants together, you'll need to swap the bytes around first ## Hanoi - I don't quite get what happened, but I've exploited a buffer overflow (stack-allocated variable next to array?) - If you enter a too long password, you overwrite the memory location adjacent to the designated buffer - There happens to be another adjacent region that's checked in the password verification routine - Enter a bogus password filling the buffer, then the required constant ## Reykjavik > We apologize for making it too easy for the password to be recovered > on prior versions. The engineers responsible have been sacked. - This one uses "military-grade encryption". No idea what exactly the encryption is - The program starts out with initializing a table from `0x00` to `0xff`, then proceeds decrypting various parts of it that haven't been disassembled yet and jumps into these - In other words, self-modifying code which doesn't show up in the debugger as usual - However you can see the currently executed instruction in the right upper widget which is much like observing the action through a peep hole - If you follow this closely, you'll find out the code does nothing more than comparing the user input against a two-byte password ## Cusco - I can overwrite a region of memory, but there's no adjacent verification flag - After going way too many times through the verification part I noticed that after failing the verification, the stack pointer points into overflowable memory and a `ret` happens - So, put an address there and you control where you return to - Use the address of the unlock part and you're in! ## Johannesburg > This is Software Revision 04. We have improved the security of the > lock by ensuring passwords that are too long will be rejected. - Fortunately there's a `strcpy` call which overwrites a too small buffer - A byte close to the `strcpy` destination is checked, this byte can be overwritten to make the length check pass (otherwise you'd run into an early exit) - The next bytes are used as return address, so put an address there - Note that any zero bytes in the user input will make `strcpy` stop early, so avoid these at all costs ## Whitehorse > This is Software Revision 01. The firmware has been updated to > connect with the new hardware security module. We have removed the > function to unlock the door from the LockIT Pro firmware. - Seems like I need to write that function myself to trigger an unlock... - I copied the unlock assembly code to the assembly page, adjusted the address of the interrupt subroutine and added a `nop` to fill the 16 bytes of allowed input - Then I added the address pointing towards the start of the password input - When executed this will make the `pc` return to the input storage and execute its contents as if it were code, thereby unlocking the door - The only wrinkle with this solution is that there's a NUL in the shellcode, this will be problematic when passed to a string copying function (as they terminate on NUL) ## Montevideo > This is Software Revision 03. We have received unconfirmed reports > of issues with the previous series of locks. We have reimplemented > much of the code according to our internal Secure Development > Process. - The main difference here is that the input buffer is copied with `strcpy`, then zeroed out with `memset` - The previous input breaks as it contains a NUL, so not all of it is copied over - Time to rewrite the shellcode - The problem is that `push #0x17` is encoded with a NUL, presumably because the literal is stored as `#0x0017` - Since this isn't a load-store architecture, we can do fancy things with addressing and use all kinds of sources and destinations - I therefore replace `push #0x17` with `push r8` and make sure `r8` is populated with `#0x17` before that - This can be done with simple arithmetics, like writing a 16-bit constant into `r8`, then using XOR to turn it into `#0x17` - `0x413e ^ 0x4141 == 0x17` - The shell code can be stripped of the stack manipulation and return as well as they aren't needed (the return is never taken and there's plenty of unused space for the stack pointer to eat into) ## Addis Ababa > - Usernames are printed back to the user for verification. - Seems to be a format string bug because of the use of printf on user input and the support for `%n` in format strings - The format string support is rather limited compared to what's shown in guides explaining this bug class, there are no width/precision fields and just four modifiers (c for 1 byte read, x for 2 byte read, n for limited 2 byte write, s for unpredictable read) - The most basic format string to write a small 16 bit number to an address is "\xBB\xAA%x%n" - The number written can be increased by inserting padding characters between the address and the junk format instruction - This can be used on code since there's no DEP to write `rrc sr` (`\x02\x00`) or `rrc #0x0` (`\x03\x00`), both of which seem equivalent to `nop` - The website considers `\x02\x00` to be `nop`, but the emulator `\x03\x00` - When used on the jump deciding whether to unlock the door or not, this bypasses the verification check - An alternative solution is to find the variable for the unlock check and overwrite it