Skip to content

Commit

Permalink
ROP: fix ROP(ELF(exe)).leave is None in some ELF (#2506)
Browse files Browse the repository at this point in the history
* ROP: fix `self.leave is None` in some libc

When `pop rbp; pop rsp; ret;` exists in libc, it is chosen instead of
`leave`. Add a specific `order` to filter out the former one.

* add changelog for #2506

* ROP: add sp_move when `pop rsp`

* CHANGELOG: mv #2506 to `5.0.0`
  • Loading branch information
RocketMaDev authored Feb 18, 2025
1 parent 447ac94 commit 636b3b2
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ The table below shows which release corresponds to each branch, and what date th
- [#2527][2527] Allow setting debugger path via `context.gdb_binary`
- [#2530][2530] Do NOT error when passing directory arguments in `checksec` commandline tool.
- [#2529][2529] Add LoongArch64 support
- [#2506][2506] ROP: fix `ROP(ELF(exe)).leave` is `None` in some ELF
- [#2504][2504] doc: add example case for `tuple` (host, port pair) in `gdb.attach`

[2519]: https://github.com/Gallopsled/pwntools/pull/2519
Expand All @@ -94,6 +95,7 @@ The table below shows which release corresponds to each branch, and what date th
[2527]: https://github.com/Gallopsled/pwntools/pull/2527
[2530]: https://github.com/Gallopsled/pwntools/pull/2530
[2529]: https://github.com/Gallopsled/pwntools/pull/2529
[2506]: https://github.com/Gallopsled/pwntools/pull/2506
[2504]: https://github.com/Gallopsled/pwntools/pull/2504

## 4.15.0 (`beta`)
Expand Down
15 changes: 10 additions & 5 deletions pwnlib/rop/rop.py
Original file line number Diff line number Diff line change
Expand Up @@ -1391,6 +1391,8 @@ def __getattr__(self, k):
if pop.match(insn):
regs.append(pop.match(insn).group(1))
sp_move += context.bytes
if 'sp' in insn:
sp_move += 9999999
elif add.match(insn):
arg = int(add.match(insn).group(1), 16)
sp_move += arg
Expand Down Expand Up @@ -1418,7 +1420,7 @@ def __getattr__(self, k):
if not set(['rsp', 'esp']) & set(regs):
self.pivots[sp_move] = addr

leave = self.search(regs=frame_regs, order='regs')
leave = self.search(regs=frame_regs, order='leav')
if leave and leave.regs != frame_regs:
leave = None
self.leave = leave
Expand Down Expand Up @@ -1451,15 +1453,17 @@ def search(self, move = 0, regs = None, order = 'size'):
pointer is adjusted.
regs(list): Minimum list of registers which are popped off the
stack.
order(str): Either the string 'size' or 'regs'. Decides how to
order(str): Either the string 'size', 'leav' or 'regs'. Decides how to
order multiple gadgets the fulfill the requirements.
The search will try to minimize the number of bytes popped more than
requested, the number of registers touched besides the requested and
the address.
If ``order == 'size'``, then gadgets are compared lexicographically
by ``(total_moves, total_regs, addr)``, otherwise by ``(total_regs, total_moves, addr)``.
by ``(total_moves, total_regs, addr)``, if ``order == 'regs'``,
then by ``(total_regs, total_moves, addr)``. ``order == 'leav'``
is specifically for ``leave`` insn.
Returns:
A :class:`.Gadget` object
Expand All @@ -1471,8 +1475,9 @@ def search(self, move = 0, regs = None, order = 'size'):
# Search for an exact match, save the closest match
key = {
'size': lambda g: (g.move, len(g.regs), g.address),
'regs': lambda g: (len(g.regs), g.move, g.address)
}[order]
'regs': lambda g: (len(g.regs), g.move, g.address),
'leav': lambda g: ('leave' not in g.insns, len(g.regs), g.address)
}[order] # False is prior than True

try:
result = min(matches, key=key)
Expand Down

0 comments on commit 636b3b2

Please sign in to comment.