Looking at a few basic stack-based buffer overflows, I'm confused as to the difference the caller's ebp
plays in a basic return address overwrite vs an off-by-one ebp overwrite.
In the return address overwrite, the goal is to smash the stack enough to overwrite the return address and therefore control eip
.
In the off-by-one attack, the LSB of the caller's ebp
is overwritten. This forces ebp
to pop, and the esp
is moved to a location within the attacker-controlled buffer, which elicits control of the return address and, therefore `eip.
My confusion stems from the behaviour of ebp
. In the basic return address overwrite, it doesn't matter that we overwrite the caller's ebp
with junk bytes, but ebp
's value needs to be coherent in the off-by-one attack. How does the function epilogue work in the basic buffer overflow case?
mov esp, ebp
pop ebp
retn
In a basic return address overwrite, as you said you're able to overwrite ret
directly so only retn
matters in the instructions of function epilogue.
In an off-by-one ebp overwrite apparently you know you can't overwrite ret
, but you can pivot the stack to somewhere user controlled.
In this case attacker goes through two function epilogue at least to gain control.
off-by-one ebp overwrite
mov esp, ebp
pop ebp ; now ebp is partially overwriten
retn
pivot stack to controlled area.
mov esp, ebp ; now stack(esp) is moved to controlled area.
pop ebp ; controlled
retn ; controlled --> gain eip control