# How to debug with coredump ## Generating the coredump ### Check system-wide configuration for coredump ```bash $ cat /proc/sys/fs/suid_dumpable 1 <-- (debug mode) - all processes dump core when possible. $ cat /proc/sys/kernel/core_pattern core.%u.%P.%t <-- core.{uid}.{pid}.{timestamp} ``` If you see anything different, you should inform TAs or instructor. ### Check User configuration. 1. You need to set the core size to be *unlimited* (run it on each login). ```bash $ ulimit -c unlimited $ ulimit -a -t: cpu time (seconds) unlimited -f: file size (blocks) unlimited -d: data seg size (kbytes) unlimited -s: stack size (kbytes) 8192 -c: core file size (blocks) unlimited <-- confirm core file size is set to be unlimited. ``` Or you can append `ulimit` command to your RC (Run Command) script. ```bash $ echo "ulimit -c unlimited >> $HOME/.bashrc ``` 2. Ensure that you have writable permission to the directory ```bash $ ls -ld . drwxrwxr-x 2 kxj190011 kxj190011 4096 Feb 8 10:35 . ``` 3. Run a program to generate a crash and CHECK THE MESSAGE `(core dumped)`. ```bash unit2/bof-level5 $ python -c "print(b'A' * 300)" | ./bof-level5 Values in two local variables are: ... Analyze the program! [1] 6074 done python -c "print(b'A' * 300)" | 6075 segmentation fault (core dumped) ./bof-level5 unit2/bof-level5 $ ls -l core* -rw------- 1 kxj190011 unit2-bof-level5-solved 356352 Feb 9 09:56 core.1007.6075.1644422185 ``` Great! Now you just have generated coredump! ## Debug with coredump ### Generating core and analyze with gdb 1. Run gdb with generated coredump ```bash $ gdb --core=core.xxx.xxx.xxxxxx # or you can run the following command to load the latest core $ gdb --core=$(ls -tr core* |tail -1) ``` This command will open a gdb session at the crash point. The coredump file contains a system's status at the crash point, which includes memory, register, type of signal on crash (mostly SIGSEGV), etc. You cannot run and step through the program (e.g., using r, ni, si) because the execution was terminated. However, you can still examine the contents of memory when it is killed by the kernel. ```gdb $ gdb --core=$(ls -tr core* |tail -1) ... [New LWP 8692] Core was generated by `./bof-level5'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x08048576 in ?? () LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ────────────────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────────────── EAX 0x84 EBX 0x0 ECX 0xffffd298 ◂— 0x41412762 ("b'AA") EDX 0x84 EDI 0xf7fb3000 ◂— 0x1b2db0 ESI 0xf7fb3000 ◂— 0x1b2db0 EBP 0x41414141 ('AAAA') ESP 0xffffd320 ◂— 0x1 EIP 0x8048576 ◂— leave ─────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────── ► 0x8048576 leave 0x8048577 retl 0x8048578 leal 4(%esp), %ecx 0x804857c andl $0xfffffff0, %esp 0x804857f pushl -4(%ecx) 0x8048582 pushl %ebp 0x8048583 movl %esp, %ebp 0x8048585 pushl %ecx 0x8048586 subl $4, %esp ──────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────── 00:0000│ esp 0xffffd320 ◂— 0x1 01:0004│ 0xffffd324 —▸ 0xffffd3e4 —▸ 0xffffd54f ◂— './bof-level5' 02:0008│ 0xffffd328 —▸ 0xffffd338 ◂— 0x0 03:000c│ 0xffffd32c —▸ 0x804858e ◂— movl $0, %eax 04:0010│ 0xffffd330 —▸ 0xf7fb33dc —▸ 0xf7fb41e0 ◂— 0x0 05:0014│ 0xffffd334 —▸ 0xffffd350 ◂— 0x1 06:0018│ 0xffffd338 ◂— 0x0 07:001c│ 0xffffd33c —▸ 0xf7e18647 ◂— addl $0x10, %esp 08:0020│ 0xffffd340 —▸ 0xf7fb3000 ◂— 0x1b2db0 ... ↓ 0a:0028│ 0xffffd348 ◂— 0x0 0b:002c│ 0xffffd34c —▸ 0xf7e18647 ◂— addl $0x10, %esp 0c:0030│ 0xffffd350 ◂— 0x1 0d:0034│ 0xffffd354 —▸ 0xffffd3e4 —▸ 0xffffd54f ◂— './bof-level5' 0e:0038│ 0xffffd358 —▸ 0xffffd3ec —▸ 0xffffd55c ◂— 'TERM=xterm-256color' 0f:003c│ 0xffffd35c ◂— 0x0 ────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────── ► f 0 8048576 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg> ``` This output means that the coredump is generated by './bof-level5', and the program crashed at 0x8048635 with the signal SIGSEGV. 2. Checking register values. Just type "i r" (info regs) ```gdb pwndbg> i r eax 0x84 132 ecx 0xffffd298 -11624 edx 0x84 132 ebx 0x0 0 esp 0xffffd320 0xffffd320 ebp 0x41414141 0x41414141 esi 0xf7fb3000 -134533120 edi 0xf7fb3000 -134533120 eip 0x8048576 0x8048576 eflags 0x10286 [ PF SF IF RF ] cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x63 99 ``` You can see that the program uses the stack address around 0xffffd320, and the frame pointer ($ebp) has been changed to 0x41414141. And to check the cause of the crash, let's check what is the current instruction. ```gdb pwndbg> x/i $pc => 0x8048576: leave ``` x/i means that "examine instruction", and the $pc contains the location of the current instruction (it is $eip, actually). Because the value of %ebp is 0x41414141, which is an invalid address, SIGSEGV was raised on running leave instruction because it will change %esp = 0x41414141 then run "pop %ebp" (dereferencing 0x41414141). 3. Examining the stack contents You already know that this challenge is about changing the frame pointer ($ebp) to control the execution (return address) at the end. **Why check the stack contents in core?** The important part of launching such attack is to know where the start of my input buffer is on the stack. Some of you are suffering the problem that your attack works on the samples binary but does not work with challenges binary; this is because the stack layout could be different on each program execution, so the starting address of your buffer could be different in challenge binary than the sample binary. To get the exact address in the challenge binary, you can get the 'corefile' of the challenge binary by running it and generating the crash because corefile contains the exact content at the crash point. In other words, if you get the address of your buffer in the corefile, the same address was being used on its execution! Let's check how to get that in the corefile. You can start with the current stack ($esp), however, there are two function calls (run() -> receive_input()). While your input resides in the local stack of receive_input(), your current execution is on the run() (frame-pointer attack returns twice, and crash by invalid $ebp requires two returns to generate SIGSEGV). We can presume that stack has moved up little bit, because there is the code for leave; ret;, which rolls back the local stack of receive_input(). So let's examine the stack values from somewhere below the stack ($esp) ```gdb pwndbg> x/100x $esp - 0x200 0xffffd120: 0x00000000 0x00000000 0x00000048 0x00000400 0xffffd130: 0xf7fe1fc9 0x00000000 0xf7ffdad0 0xffffd1b8 0xffffd140: 0xffffd200 0xf7fe2b4b 0x080481dc 0xffffd1b8 0xffffd150: 0xf7ffda74 0x00000001 0xf7fd34a0 0x00000001 0xffffd160: 0x00000000 0x00000001 0xf7ffd918 0xf7fb3780 0xffffd170: 0xf7fb3000 0x00000400 0x00000400 0xf7e71085 0xffffd180: 0xf7fb3000 0xf7fb3000 0x00000400 0x00000000 0xffffd190: 0xf7ffd000 0xf7ffd918 0xffffd1b0 0x0804828a 0xffffd1a0: 0x00000000 0xffffd244 0xf7fb3d60 0xf7e5d951 0xffffd1b0: 0xffffffff 0x0804b008 0xf7e07b08 0xf7fd31b0 0xffffd1c0: 0xf7fe1fc9 0x00000000 0xf7ffdad0 0xffffd248 0xffffd1d0: 0x00000016 0x00000000 0x080481fc 0x00000011 0xffffd1e0: 0x00002190 0x00000001 0x000003e9 0x00000005 0xffffd1f0: 0xf7fe2a70 0x080481dc 0x00000001 0xf7ffd918 0xffffd200: 0x0804a00c 0xf7fe78a2 0xf7ffdad0 0xf7fd34a0 0xffffd210: 0x00000001 0x00000001 0x00000000 0xf7e690c1 0xffffd220: 0x00000001 0x0804b008 0x0000001e 0x00000000 0xffffd230: 0xf7ffd000 0x0804825c 0xf7e5d8d9 0xf7fb3d60 0xffffd240: 0x0000000a 0xf7e07b08 0xffffd308 0xf7e683f4 0xffffd250: 0xf7fe77eb 0x00000000 0xf7fb3000 0xf7fb3000 0xffffd260: 0xffffd318 0xf7fee010 0xffffd318 0x00000084 0xffffd270: 0xffffd298 0xf7ed5c43 0x00000000 0x08048534 0xffffd280: 0x00000000 0xffffd298 0x00000084 0xf7e6a48b 0xffffd290: 0xf7fb3d60 0x0804b008 0x41412762 0x41414141 0xffffd2a0: 0x41414141 0x41414141 0x41414141 0x41414141 ``` x/100x $esp - 0x200, this command will display upto 100 4-byte integer values from the address %esp-0x200 (the location 0x200 atop the esp; you can use + to see downward). While we were injecting the input of "A"*200, which will be lots of 0x41414141, I cannot see that on the stack. Let's move upwards (pressing the enter again). ```gdb pwndbg> (just pressing the 'enter' key) 0xffffd2b0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd2c0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd2d0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd2e0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd2f0: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd300: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd310: 0x41414141 0x41414141 0x41414141 0x08048575 0xffffd320: 0x00000001 0xffffd3e4 0xffffd338 0x0804858e 0xffffd330: 0xf7fb33dc 0xffffd350 0x00000000 0xf7e18647 0xffffd340: 0xf7fb3000 0xf7fb3000 0x00000000 0xf7e18647 0xffffd350: 0x00000001 0xffffd3e4 0xffffd3ec 0x00000000 0xffffd360: 0x00000000 0x00000000 0xf7fb3000 0xf7ffdc04 0xffffd370: 0xf7ffd000 0x00000000 0xf7fb3000 0xf7fb3000 0xffffd380: 0x00000000 0xfd695fe7 0xc1c3f1f7 0x00000000 0xffffd390: 0x00000000 0x00000000 0x00000001 0x080483d0 0xffffd3a0: 0x00000000 0xf7fee010 0xf7fe8880 0xf7ffd000 0xffffd3b0: 0x00000001 0x080483d0 0x00000000 0x080483f1 0xffffd3c0: 0x08048578 0x00000001 0xffffd3e4 0x080485a0 0xffffd3d0: 0x08048600 0xf7fe8880 0xffffd3dc 0xf7ffd918 0xffffd3e0: 0x00000001 0xffffd54f 0x00000000 0xffffd55c 0xffffd3f0: 0xffffd570 0xffffd6f0 0xffffd701 0xffffd71a 0xffffd400: 0xffffd72c 0xffffd74b 0xffffd760 0xffffd773 0xffffd410: 0xffffd782 0xffffd794 0xffffd79c 0xffffd7c1 0xffffd420: 0xffffd7e9 0xffffd808 0xffffd813 0xffffd81b 0xffffd430: 0xffffd83b 0xffffddc3 0xffffdde1 0xffffde0c ``` Ah, now we can see several 0x41414141s. It seems the buffer starts at 0xffffd29a (for the samples -- bof-level5) binary. ### Generating core from different location Then, what if I run the program in the different directory? Let's check with the core. ```bash $ cp $HOME/unit2/bof-level5/bof-level5 /tmp/ $ cd /tmp/ /tmp $ python -c "print(b'A' * 200)" | ./bof-level5 Now the program contains a buffer overflow vulnerability, but the vulnerability does not allow us to overwrite the return address... Can you exploit this program? [1] 30715 done python -c "print(b'A' * 200)" | 30716 segmentation fault (core dumped) ./bof-level5 ``` Core was generated by `/tmp/bof-level5'. Program terminated with signal SIGSEGV, Segmentation fault. ```gdb $ gdb --core=$(ls -tr core* |tail -1) pwndbg> x/100x $esp - 0x200 0xffffd170: 0x00000000 0x00000000 0x00000048 0x00000400 0xffffd180: 0xf7fe1fc9 0x00000000 0xf7ffdad0 0xffffd208 0xffffd190: 0xffffd250 0xf7fe2b4b 0x080481dc 0xffffd208 0xffffd1a0: 0xf7ffda74 0x00000001 0xf7fd34a0 0x00000001 0xffffd1b0: 0x00000000 0x00000001 0xf7ffd918 0xf7fb3780 0xffffd1c0: 0xf7fb3000 0x00000400 0x00000400 0xf7e71085 0xffffd1d0: 0xf7fb3000 0xf7fb3000 0x00000400 0x00000000 0xffffd1e0: 0xf7ffd000 0xf7ffd918 0xffffd200 0x0804828a 0xffffd1f0: 0x00000000 0xffffd294 0xf7fb3d60 0xf7e5d951 0xffffd200: 0xffffffff 0x0804b008 0xf7e07b08 0xf7fd31b0 0xffffd210: 0xf7fe1fc9 0x00000000 0xf7ffdad0 0xffffd298 0xffffd220: 0x00000016 0x00000000 0x080481fc 0x00000011 0xffffd230: 0x00002190 0x00000001 0x000003e9 0x00000005 0xffffd240: 0xf7fe2a70 0x080481dc 0x00000001 0xf7ffd918 0xffffd250: 0x0804a00c 0xf7fe78a2 0xf7ffdad0 0xf7fd34a0 0xffffd260: 0x00000001 0x00000001 0x00000000 0xf7e690c1 0xffffd270: 0x00000001 0x0804b008 0x0000001e 0x00000000 0xffffd280: 0xf7ffd000 0x0804825c 0xf7e5d8d9 0xf7fb3d60 0xffffd290: 0x0000000a 0xf7e07b08 0xffffd358 0xf7e683f4 0xffffd2a0: 0xf7fe77eb 0x00000000 0xf7fb3000 0xf7fb3000 0xffffd2b0: 0xffffd368 0xf7fee010 0xffffd368 0x00000084 0xffffd2c0: 0xffffd2e8 0xf7ed5c43 0x00000000 0x08048534 0xffffd2d0: 0x00000000 0xffffd2e8 0x00000084 0xf7e6a48b 0xffffd2e0: 0xf7fb3d60 0x0804b008 0x41412762 0x41414141 0xffffd2f0: 0x41414141 0x41414141 0x41414141 0x41414141 pwndbg> search 'AAAA' [stack] 0xffffd2ea 0x41414141 ('AAAA') ``` Ah, now you can see that the address starts at 0xffffd2ea (containing 0x41414141). Previously, it was 0xffffd29a but now it is 0xffffd2ea (changed!). In a subsequent running of the challenge program, this address value will be the same if your running command is the same (i.e., runs `$HOME/unit2/bof-level5/bof-level5`, not in the different path or something). A shortcut to have a correct address value is that, when you are using [pwntools] (writing a python script), the program execution within [pwntools] will also generate a corefile to your directory. So please take the following steps: 1. You will run the program with pwntool script. 2. Supply a crash input that you can identify. This time we will try `cyclic(200)` to get identifiable input. Run the following command to crash `bof-level5`. ```bash /tmp $ python -c "from pwn import *;print(cyclic(200))" | ./bof-level5 ``` 3. Check the generated 'core' with gdb to confirm the exact stack address. ```gdb pwndbg> search aaaa [stack] 0xffffd2ea 0x61616161 ('aaaa') ``` 4. Use that address to formulate your exploit!. --- [pwntools]:http://docs.pwntools.com/en/latest/