============ Lec09: Misc1 ============ In this tutorial, we are going to learn about how to write an one-shot exploit by re-triggering a vulnerability. 1. Leak code pointer ==================== void start() { printf("IOLI Crackme Level 0x00\n"); printf("Password:"); char buf[32]; memset(buf, 0, sizeof(buf)); read(0, buf, 256); if (!strcmp(buf, "250382")) printf("Password OK :)\n"); else printf("Invalid Password!\n"); } int main(int argc, char *argv[]) { setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stdin, NULL, _IONBF, 0); start(); return 0; } ------------------------------------------------------------ $ make cc -m32 -g -O0 -fno-stack-protector -o crackme0x00 crackme0x00.c -ldl /vagrant/bin/checksec.sh --file crackme0x00 RELRO STACK CANARY NX PIE RPATH RUNPATH FILE Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH crackme0x00 Code is almost same with lab07's tutorial, but there is no explicit code pointer leak. Since NX is enabled, you cannot use shellcode. You can use return-to-libc technique, but because of ASLR, you cannot know an exact address. You might use brute-force attack, but it is infeasible in 64bit and even in 32bit, it is hard to debug and takes much time. And if you do pentesting, too many trials can be captured by monitoring. Therefore, making a reliable(one-shot) exploit is important. - GOT (Global Offset Table) GOT is a table used by dynamic linker and each dynamic-linked function(*f*) has its own GOT table entry. At first, it is pointing to dl_runtime_resolve(), a function for converting a symbol to an address. After *f* is called, the address is changed into *f*'s address. i.e., if *f* is libc function, then if you leak the address, then you can get LIBC_BASE(*f*'s address - *f*'s offset). NOTE: GOT entry is changed into an address once a target function is called. So if you want to leak an address of some function, the function should be the one which had been called before. __libc_start_main() is a good target to leak because it is called before main(). NOTE: Runtime symbol resolution is complicated. If you want to understand how it works, please try to solve return-to-dl challenge. Your payload to leak __libc_start_main()'s address should look like this: [buf ] [.....] [ra ] -> printf() [dummy] [arg1 ] -> __libc_start_main() GOT When printf() is invoked, __libc_start_main()'s address will be leaked. But after you get the address, you need to control the program again with newly crafted payload based on leaked address. 2. Back to main() ================= Since there is a vulnerability, you can reuse it. If a program does not use arguments, then you can go back to main and re-trigger the vulnerability. Your first payload should be changed like this to go back to main(): [buf ] [.....] [ra ] -> printf [main] [arg1 ] -> __libc_start_main GOT Then, your program will be back to main() and you can re-trigger the vulnerability. In second execution, since you know the libc base address, you can do return-to-libc as before. Your second payload should look like this: [buf ] [.....] [ra ] -> system [dummy] [arg1 ] -> "/bin/sh" Then shell will be spawned.