============================ Lec09: Make Reliable Exploit ============================ In this tutorial, we are going to learn 1) how to write more reliable exploit and 2) logical vulnerability. 1. Write reliable exploit ========================= Let's start this tutorial by using our old friend - crackme0x00. You can generate target binary by: $ cd crackme $ make 1.1. Leaking address --------------------- As you know, this binary contains simple buffer overflow vulnerability. However, your server has ASLR setting enabled and this will change your LIBC address everytime you run the binary. How about your compile environment? If you compile the binary in different environment (e.g., different compiler), the binary will be changed; then your exploit may no longer work. Our goal is to write more reliable exploit that doesn't depend on static offset and doesn't require brute-forcing. To achieve that, you should successfully leak the LIBC address first. Take a look at the disassembled code from start() function: ... 0x08048576 <+25>: call 0x8048400 0x0804857b <+30>: mov DWORD PTR [esp+0x8],0x20 0x08048583 <+38>: mov DWORD PTR [esp+0x4],0x0 0x0804858b <+46>: lea eax,[ebp-0x28] 0x0804858e <+49>: mov DWORD PTR [esp],eax ... How about overwriting buffer and invoke printf() function and argument with '__libc_start_main()'s got address'? After you leak the address, you will get the LIBC base address. Sounds like a plan? Your payload will be like this: [Overwrite Buf] [printf] [ret] [__libc_start_main] Once you invoke printf() function and leak the address of '__libc_start_main', you will go back to ret address that you specified. Take your time and make your exploit. We recommend you to use template.py script. Could you successfully leak the address of __libc_start_main()? If you are running the tutorial in the CS6265 remote server, you probably not see anything. Ok! Let's find out the reason. $ ldd crackme0x00 linux-gate.so.1 => (0xf775d000) libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf759b000) /lib/ld-linux.so.2 (0x5656d000) $ readelf -s /lib/i386-linux-gnu/libc.so.6 |grep __libc_start_main 2262: 00019a00 454 FUNC GLOBAL DEFAULT 12 __libc_start_main@@GLIBC_2.0 Do you see anything interesting? '0x19a00' is an offset value of the __libc_start_main() function in LIBC library and this address ends with '\00'. If the address is used for an argument of the printf() function, the function will not print value after the '\x00' so you are not able to leak the address. Instead of using __libc_start_main() for leaking, you can use setvbuf()'s address or other functions if the address is not ended with '\00'. $ readelf -s /lib/i386-linux-gnu/libc.so.6 |grep setvbuf 2008: 00065ec0 405 FUNC WEAK DEFAULT 12 setvbuf@@GLIBC_2.0 1.2. Prevent from using fixed address -------------------------------------- Now you should make your first exploit looks like this: [Overwrite Buf] [printf] [ret] [setvbuf] It means that you should know the address of each functions (e.g., printf) by reading symbol table or by doing debugging. Fortunately, pwntools provides very useful functions to handle this issue. Open and take a look at the examples from template.py script. You can search symbols, got, or string's address by using pwntools. - printf = elf.symbols['printf'] - leak_add = elf.got['setvbuf'] - binsh_offset = libc.search('/bin/sh').next() I think it is good time to make the first half of exploit for leaking the address. You should be able to print out address of setvbuf(). 1.3. Going back to the main() again ------------------------------------ Recall the first payload: [Overwrite Buf] [printf] [ret] [setvbuf] Where do you want to go back after you leak the address of setvbuf()? One good choice would be going back to the main() function. After you go back to main(), you are able to overflow the buffer and feed your exploit. To do so, your first and second payload should be: 1st: [Overwrite Buf] [printf] [ret] [setvbuf] 2nd: [Overwrite Buf] [system] [exit] [bin_sh] * pwntools ROP support You can also automate the ROP programming process. Take a look at the below sample, then you will have a good idea about how to utilize this. ------ sample ------ from pwn import * libc = ELF('/lib/i386-linux-gnu/libc.so.6') libc.address = LEAKED_LIBC_BASE_ADDRESS rop = ROP(libc) rop.system(next(libc.search('/bin/sh\x00'))) payload = "A" * 44 + str(rop) ---- end sample ---- If you are ambitious, you can fully automate the entire exploit process by using referenced symbols and ROP functionality. 2. Logical vulnerability ========================= You can play a game and enjoy pwning here. Compile the binary first. $ cd snake $ make Interestingly, the binary invokes system() function when it starts. If you have a good idea, you can spwan a shell or read the flag without exploiting the memory corruption bugs that you learned so far. $ cat snake.c|grep system exit (WEXITSTATUS(system ("stty sane"))); if (WEXITSTATUS(system ("stty cbreak -echo stop u"))) return WEXITSTATUS(system ("stty sane")); In the second section of this tutorial, your mission is to make the snake binary do unintended behavior. (e.g., cat /proc/flag)