python-ptrace icon indicating copy to clipboard operation
python-ptrace copied to clipboard

Ptrace Segmentation Fault signal handler can't handle RETurns to invalid addresses

Open AlLongley opened this issue 3 years ago • 1 comments

Trying to _analyze() or display() a ProcessSignal of type SIGSEGV raised by a "RET" condition currently raises an Exception, as the current instruction handler has no detection for the RET opcode.

This segfault signal can be raised when an invalid address is at the top of the stack, then a RET is executed looking to pop the value into RIP. This is a valuable use-case for detecting buffer overflows / attempted RET2Libc attacks in binaries without stack protection canaries

AlLongley avatar Jan 15 '23 12:01 AlLongley

I propose an addition to the following function:

https://github.com/vstinner/python-ptrace/blob/a715d0f9bef4060022bfb6d25e25e148b2bd5f54/ptrace/debugger/ptrace_signal.py#L85-L95

With the following opcode check:

# RET instruction (e.g. "RET" with an invalid return address at the stack pointer)
match = re.search(r"^(?:RET?)?", asm)
if match:
	sp = self.process.getStackPointer()
	return_addr = self.process.readWord(sp)
	
	self.reason = InvalidRead(address=return_addr, size=CPU_WORD_SIZE, process=self.process)
	return

My main issue halting PR is that my RegEx skills are garbage, and I don't quite understand the re.match groups referenced in the previous opcode checks.

I understand they match for variations of the regular opcodes, and extract out the operands for calculating memory accessed by specific offsets, though I don't believe RET has any variations or operands.

Is my sample code above a valid check? And is simply readWord() from the StackPointer a safe operation between 32 and 64 bit archs?

AlLongley avatar Jan 15 '23 12:01 AlLongley