This challenge is similar to the last one, with a minor difference. In this challenge, we'll explore an Unnatural (Artificial) Jump and its implications for our payload. This write-up will match the one from the last challenge to show how similar they are.
Again, we always start with checksec.

No canary, no PIE. We're good for standard buffer overflows.
If we check the disassembly for main, we see this method takes input from the user:
0x080491f6 <+39>: push 0x40 0x080491f8 <+41>: lea eax,[ebp-0x30] 0x080491fb <+44>: push eax 0x080491fc <+45>
This is equivalent to:
read(0, ebp-0x30, 0x40);
At this point, we should be confident in the buffer overflow. We plan to jump to win() found inside info functions.
There's nothing new about our payload:
payload = b'A' * 0x34 payload += p32(0x08049186)
When we run this, we don't get much of a response.

We're confident in our padding and return address. Let's check on win() to see what's happening.
gef➤ disas win Dump of assembler code for function win: 0x08049186 <+0>: push ebp 0x08049187 <+1>: mov ebp,esp 0x08049189 <+3>: push ebx 0x0804918a <End of assembler dump.
We can get a better view of this in a program like Radare2:

This shows win() has some sort of conditional! We see ebp+0x8 is being compared to 0xdeadbeef. It then does je to system@plt and jne to puts@plt.
From our past experience, we confidently guess we need to pass 0xdeadbeef somehow. We now need to understand how our stack frame looks between function calls.
This diagram assumes that we call win() naturally via a call win statement.

In the natural case, we see the following happen:
0xdeadbeef is pushed onto the stack. That is written in Yellow in the image.call win is executed. This does two vital things:
win() is finished executing.call.win(), the prologue of that function pushes ebp, then moves esp to make space for that method's stack frame.Therefore, in the diagram, our parameter ends at ebp+0x8. This does not change across binaries; that is where the first parameter will be!
ebp+0xc, and so on.Now that we understand how parameters are passed naturally, let's try it.
We first attempt to format the payload the same as last challenge. That would look like:
payload = b'A' * 52 payload += p32(0x08049186) payload += p32(0xdeadbeef)
If we run this, we still see You lose!. Why? We have to understand our current stack frame.
It's important to note we arrived to win artificially, meaning we didn't arrive via a call. In this case, we overwrote a return pointer to arrive at this function. Considering the crucial steps of calling a function:
call)By us not using call to arrive at win, we miss Step 2! This means our argument is not where the interpreter expects it to be. This is why we still get You lose; our value was not loaded correctly.
To fix this, we need to make space for the return pointer. That can be any value, but I'll use 0x0 in this case. This value must go between the return address and the argument to ensure we're in the correct order.
payload = b'A' * 52 payload += p32(0x08049186) payload += p32(0x0) payload += p32(0xdeadbeef)
Running this successfully gets a flag!
