summaryrefslogtreecommitdiff
path: root/source/ctfs/layerone/2017
diff options
context:
space:
mode:
Diffstat (limited to 'source/ctfs/layerone/2017')
-rw-r--r--source/ctfs/layerone/2017/writeup_sheb-teth.md27
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>