=============================== Lec05: Bypassing stack canaries =============================== 1. Revisiting "crackme0x00" =========================== This is the reversed-engineered source code of the crackme0x00 challenge that we are familiar with: ------------------------------------------------------------ $ cat crackme0x00.c #include #include #include #include int main(int argc, char *argv[]) { char buf[16]; printf("IOLI Crackme Level 0x00\n"); printf("Password:"); scanf("%s", buf); if (!strcmp(buf, "250382")) printf("Password OK :)\n"); else printf("Invalid Password!\n"); return 0; } ------------------------------------------------------------ We are going to compile this source code into four different binaries with following options: $ make cc -m32 -g -O0 -fno-stack-protector -z execstack -o crackme0x00-nossp-exec crackme0x00.c checksec --file crackme0x00-nossp-exec [*] 'crackme0x00-nossp-exec' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments cc -m32 -g -O0 -fno-stack-protector -o crackme0x00-nossp-noexec crackme0x00.c checksec --file crackme0x00-nossp-noexec [*] 'crackme0x00-nossp-noexec' Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000) cc -m32 -g -O0 -fstack-protector -o crackme0x00-ssp-exec -z execstack crackme0x00.c checksec --file crackme0x00-ssp-exec [*] 'crackme0x00-ssp-exec' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments cc -m32 -g -O0 -fstack-protector -o crackme0x00-ssp-noexec crackme0x00.c checksec --file crackme0x00-ssp-noexec [*] 'crackme0x00-ssp-noexec' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000) There are a few interesting compilation options that we used: 1. -fno-stack-protector: do not use a stack protector 2. -z execstack: make its stack "executable" So we name each binary with a following convention: crackme0x00-{ssp|nossp}-{exec|noexec} 2. Let's crash the "crackme0x00" binary ======================================= crackme0x00-nossp-exec behaves exactly same as "crackme0x00". Not sparingly, it crashes with a long input: $ echo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | ./crackme0x00-nossp-exec IOLI Crackme Level 0x00 Password:Invalid Password! Segmentation fault (core dumped) What about crackme0x00-ssp-exec that is compiled with a stack protector? $ echo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | ./crackme0x00-ssp-exec IOLI Crackme Level 0x00 Password:Invalid Password! *** stack smashing detected ***: ./crackme0x00-ssp-exec terminated Aborted (core dumped) The "stack smashing" is detected so the binary simply prevents itself from exploitation; resulting in a crash instead of being hijacked. You might want to run gdb to figure out what's going on this binary: $ gdb ./crackme0x00-ssp-noexec Reading symbols from ./crackme0x00-ssp-noexec...done. (gdb) r Starting program: crackme0x00-ssp-noexec IOLI Crackme Level 0x00 Password:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Invalid Password! *** stack smashing detected ***: crackme0x00-ssp-noexec terminated Program received signal SIGABRT, Aborted. 0xf7fdb440 in __kernel_vsyscall () (gdb) bt #0 0xf7fdb440 in __kernel_vsyscall () #1 0xf7e4d687 in raise () from /lib/i386-linux-gnu/libc.so.6 #2 0xf7e50ab3 in abort () from /lib/i386-linux-gnu/libc.so.6 #3 0xf7e87fd3 in ?? () from /lib/i386-linux-gnu/libc.so.6 #4 0xf7f1ab8b in __fortify_fail () from /lib/i386-linux-gnu/libc.so.6 #5 0xf7f1ab1a in __stack_chk_fail () from /lib/i386-linux-gnu/libc.so.6 #6 0x080485ae in main (argc=97, argv=0xffffd734) at crackme0x00.c:19 3. Let's analyze! ================= To figure out, how two binaries are different. We (so kind!) provide you a script, "./diff.sh" that can easily compare two binaries. $ ./diff.sh crackme0x00-nossp-noexec crackme0x00-ssp-noexec --- /dev/fd/63 2017-09-22 10:52:38.378422450 -0400 +++ /dev/fd/62 2017-09-22 10:52:38.378422450 -0400 @@ -6,7 +6,13 @@ push ebx push ecx or DWORD PTR [esp],0x0 - add ebx,0x1ab4 + add ebx,0x1a64 + mov eax,ecx + mov eax,DWORD PTR [eax+0x4] + mov DWORD PTR [ebp-0x2c],eax + mov eax,gs:0x14 + mov DWORD PTR [ebp-0xc],eax + xor eax,eax sub esp,0xc push eax add esp,0x10 @@ -29,6 +35,8 @@ push eax add esp,0x10 mov eax,0x0 + mov edx,DWORD PTR [ebp-0xc] + xor edx,DWORD PTR gs:0x14 pop ecx pop ebx pop ebp Two notable differences are at the function prologue and epilogue. There is an extra value (%gs:0x14) placed right after the frame pointer on the stack: + mov eax,gs:0x14 + mov DWORD PTR [ebp-0xc],eax + xor eax,eax And it validates if the inserted value is same right before returning to its caller: 8048634: 8b 55 f4 mov edx,DWORD PTR [ebp-0xc] 8048637: 65 33 15 14 00 00 00 xor edx,DWORD PTR gs:0x14 804863e: 74 05 je 8048645 8048640: e8 7b 00 00 00 call 80486c0 <__stack_chk_fail_local> __stack_chk_fail() is the function you observed in the gdb's backtrace. 4. Stack Canary =============== This extra value is called, "canary" (a bird, umm why?). More precisely, what are these values? $ gdb ./crackme0x00-ssp-exec (gdb) br *0x08048533 (gdb) r => 0x08048533 <+22>: mov %eax,0x2c(%esp) (gdb) info r eax eax 0x8a2db00 144890624 (gdb) r => 0x08048533 <+22>: mov %eax,0x2c(%esp) (gdb) info r eax eax 0x41d63500 1104557312 Did you notice the canary value keeps changing? This is great because attackers should truly guess (i.e., bypass) the canary value before exploitation. 5. Bypassing Stack Canary ========================= However, what if the stack canary implementation is not "perfect", meaning that an attacker might be able to guess (i.e., %gs:0x14)? Let's check out this binary: $ objdump -d ./target-ssp ... Instead of this: mov %gs:0x14,%eax mov %eax,0x2c(%esp) xor %eax,%eax What about this? This implementation uses a "known" value (i.e., 0xdeadbeef) as a stack canary. movl $0xdeadbeef,0x2c(%esp) So the stack should be like: |<-- 0x14 ------------>|+--- ebp top v [ [ ][canary][fp][ra][ ....] |<---- 0x30 ------------------->| [Task] How could we exploit this program? like last week's tutorial? and get the flag?