diff options
Diffstat (limited to 'source/ctfs/layerone/2017')
-rw-r--r-- | source/ctfs/layerone/2017/writeup_sheb-teth.md | 27 |
1 files changed, 15 insertions, 12 deletions
diff --git a/source/ctfs/layerone/2017/writeup_sheb-teth.md b/source/ctfs/layerone/2017/writeup_sheb-teth.md index fb98a76..6deb931 100644 --- a/source/ctfs/layerone/2017/writeup_sheb-teth.md +++ b/source/ctfs/layerone/2017/writeup_sheb-teth.md @@ -1,4 +1,3 @@ -<div class="content"> # Sheb-Teth (LayerOne 2017 CTF) [We get a binary!](Sheb-Teth.bin_pristine) I don't remember any more hints to it than just that. There was flavor text, but I've lost it. @@ -125,7 +124,7 @@ This is where things get more fun. The rest of the challenges involve some measu So, clearly, something is interesting about `/proc/self/status`. I'm not familiar with it, so it's time to take a look: - Name: vim + Name: vim State: R (running) Tgid: 25100 Ngid: 0 @@ -213,10 +212,9 @@ Much like in `xorscura_compare()`, this will exit early if any bytes of the flag nop'ing out conditional jumps at `0x8049cf6`, `0x8049d1c`, `0x8049d38`, and `0x8049d54` should do the trick, though we'll need to "fix" `xor eax, eax` at `0x8049d70` just as before, where `inc eax; inc eax` will do. <div class="codebox"> -#eval radare2 -q -e io.cache=true -c 'wx 666666666690 @ 0x8049cf6' -c 'wx 6690 @ 0x8049d1c' -c 'wc 6690 @ 0x8049d38' -c 'pd 51 @ 0x8049cd9' ./Sheb-Teth.bin | aha --no-header --stylesheet +#eval radare2 -q -e io.cache=true -c 'wx 666666666690 @ 0x8049cf6' -c 'wx 6690 @ 0x8049d1c' -c 'wc 6690 @ 0x8049d38' -c 'pd 51 @ 0x8049cd9' ./Sheb-Teth.bin_pristine | aha --no-header --stylesheet </div> - Now with breakpoints at `0x8049cf4`, `0x8049d1a`, `0x8049d36`, and `0x8049d52` and `display/c $eax` we'll get a flag one byte at a time when run and debugged! +300 @@ -313,11 +311,14 @@ Yadda yadda yadda, xor against next byte, spoiler alert: `3244241f515054e8aee9ff #### Aaaand patch! Before: -![disassembly_of_xorscura_compare_prng](xorscura_compare_prng_before_patch.png)\ +<div class="codebox"> +#eval radare2 -c 'pd 51 @ 0x8049cd9' ./Sheb-Teth.bin_pristine | aha --no-header --stylesheet +</div> After: -![hex_of_changed_xorscura_compare_prng](hex_of_changed_xorscura_compare_prng.png)\ -![disassembly_of_patched_xorscura_compare_prng](xorscura_compare_prng_after_patch.png)\ +<div class="codebox"> +#eval radare2 -q -e io.cache=true -c 'wx 666666666690 @ 0x8049cf6' -c 'wx 6690 @ 0x8049d1c' -c 'wc 6690 @ 0x8049d38' -c 'wx 3244241c515054e80beaffff58585966666690 @ 0x8049ce9' -c 'wx 3244241d515054e8e6e9ffff58585990 @ 0x8049d0e' -c 'wx 3244241e515054e8cae9ffff58585990 @ 0x8049d2a' -c 'wx 3244241f515054e8aee9ffff58585990 @ 0x8049d46' -c 'pd 51 @ 0x8049cd9' ./Sheb-Teth.bin_pristine | aha --no-header --stylesheet +</div> Wonderful - they are in fact correctly lined up calls to `puts()`, everything seems to line up... Now we should be able to run the program and, regardless of input, it'll print out flags. @@ -339,12 +340,13 @@ Except that this check, it turns out, uses the comparison in `xorscura_compare` #eval radare2 -q -A -c 'pdf @ sym.xorscura_compare' ./Sheb-Teth.bin | aha --no-header --stylesheet </div> - There's a `xor` in a loop and a `xor` not in a loop, which is curious. It turns out the first byte of the user input vs decoded flag is tested before even entering the loop! We can patch out the `jmp` at `0x8049e00` and let execution fall through into the loop with `eax` starting at 0, which will result in the first flag byte being computed twice. This lets us only patch the loop body, which is a bit easier. `6690 @ 0x8049e00` does the trick here: -![disassembly_of_xorscura_compare_patched1](pdf_sym.xorscura_compare_first_patch.png)\ +<div class="codebox"> +#eval radare2 -q -A -e io.cache=true -c 'wx 6690 @ 0x8049e000' -c 'pdf @ sym.xorscura_compare' ./Sheb-Teth.bin_pristine | aha --no-header --stylesheet +</div> Looking back to the loop body, we have `edx` being `xor`'d, so that's probably where flag bytes pass through. We can get rid of `movzx ebp, byte [edi + eax]`, `movsx edx, dl`, `cmp ebp, edx`, and `jne 0x8049e30` to make space for our patch, giving 4 + 3 + 3 + 2 + 2 = 14 bytes. @@ -352,13 +354,15 @@ Looking back to the loop body, we have `edx` being `xor`'d, so that's probably w Since we won't be needing the user-input string, which *was* in `edi`, we can use `edi` for the flag string instead, letting the ABI keep it preserved for us. -Patching `58` at 0x8049de1` to `78` changes the destination of `mov ebx, dword [eax + 8]` to `edi`, `nop`'ing `mov edi, dword [eax + 4]` at `0x08049df0` keeps `edi` safe, and patching `03` at `0x8049e13` to `07` changes `movzx edx, byte [ebx + eax]` to `movzx edx, byte [edi + eax]`. Now we don't have to worry about preserving `ebx` through `puts()` calls. +Patching `58` at `0x8049de1` to `78` changes the destination of `mov ebx, dword [eax + 8]` to `edi`, `nop`'ing `mov edi, dword [eax + 4]` at `0x08049df0` keeps `edi` safe, and patching `03` at `0x8049e13` to `07` changes `movzx edx, byte [ebx + eax]` to `movzx edx, byte [edi + eax]`. Now we don't have to worry about preserving `ebx` through `puts()` calls. This means 16 bytes of patch code, in a 14 byte hole. Thankfully, we can get two more bytes by replacing `add eax, 1` at `0x8049e22` with `inc eax`, which is just 40`. Now it's 16 bytes of code into a 16-byte hole, fitting perfectly: 3214065151505254e8e1e8ffff5a5a5859 @ 0x8049e14 -![disassembly_of_xorscura_compare_patched2](pdf_sym.xorscura_compare_second_patch.png)\ +<div class="codebox"> +#eval radare2 -q -A -e io.cache=true -c 'wx 6690 @ 0x8049e000' -c 'wx 3214065151505254e8e1e8ffff5a5a5859 @ 0x8049e14' -c 'pdf @ sym.xorscura_compare' ./Sheb-Teth.bin_pristine | aha --no-header --stylesheet +</div> and now we've patched it up to print yet another flag when run! @@ -407,4 +411,3 @@ EH_FRAME off 0x000028d4 vaddr 0x0804a8d4 paddr 0x0804a8d4 align 2**2 The bolded entry here tells us that the region at file offset 0 maps to the virtual address 0x8048000, with the file and in-memory regions being the following 0x2f68 bytes. If you see a virtual address below `0x8048000 + 0x2f68`, this is the mapping that applies! In radare2, you could instead `?p <virtual_addr>` to convert to a file offset, and not worry about these specifics. -</div> |