Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ROP: fix ROP(ELF(exe)).leave is None in some ELF #2506

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from

Conversation

RocketMaDev
Copy link

@RocketMaDev RocketMaDev commented Dec 13, 2024

Conclusion

When libc has gadgets like pop rsp; pop rbp; ret; and leave; ret;, ROP(ELF(libc)).leave is None. Added a specific order "leav" to filter out the former case.

Analysis

# pwnlib/rop.py#L1410 __load()
                    sp_move += 9999999999
                    regs += frame_regs
# the 'leave' has a very high move and owns regs ['?sp', '?bp']

# pwnlib/rop.py#L1421 __load()
        leave = self.search(regs=frame_regs, order='regs')
        if leave and leave.regs != frame_regs:
            leave = None
        self.leave = leave
# when initializing ROP object, `leave` attribute is searched via regs ['?sp', '?bp']

# pwnlib/rop.py#L1476 search(move, regs, order)
            'regs': lambda g: (len(g.regs), g.move, g.address),
# with `order == 'regs'`, gadgets are sorted first by number of regs involved,
# then by their move

# pwnlib/rop.py#L1481 search(move, regs, order)
            result = min(matches, key=key)
# when returning gadget, always choose the closest match

However, when gadgets to filter contain both Gadget(0x26211, ['pop rsp', 'pop rbp', 'ret'], ['rsp', 'rbp'], 0x18) and Gadget(0x2556a, ['leave', 'ret'], ['rbp', 'rsp'], 0x2540be407),
the former one is chosen as it has the same len(g.regs) == 2 as the latter one and has a smaller move. When return back to __load, because leave.regs == ['rsp', 'rbp'] and leave.regs != ['rbp', rsp'], so self.leave is None.

The libc I test is 2.39-0ubuntu8_amd64

POC:

from pwn import *
context.arch = 'amd64'
gadgets = ROP(ELF('libc.so.6'))
print(gadgets.leave)

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.
@RocketMaDev RocketMaDev changed the title ROP: fix ROP(ELF(exe)).leave is None in some ELF ROP: fix ROP(ELF(exe)).leave is None in some ELF Dec 13, 2024
@Arusekk
Copy link
Member

Arusekk commented Dec 18, 2024

Thank you for the fix! I think a more correct solution would be to add move 999999 and migrated flag to pop rsp as well. Can you check if it works?

@RocketMaDev
Copy link
Author

Oh, you're right. Since pop rsp; pop rbp; ret; takes 0x18 bytes, so it will choose leave instead without my solution.

But what if the user wish to match pop rsp; pop rbp; ret?

@peace-maker peace-maker added the rop Return Oriented Programming manipulation routines label Dec 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rop Return Oriented Programming manipulation routines
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants