pwnable.kr-unexploitable(SROP)
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
一个栈溢出漏洞。
rop
利用syscall(59)代替执行execve
得到rax=59(利用read返回值)
- bss中写入跳入bss的rop
- 构造利用read读取59字节的rop
- 执行syscall
填充main栈
1 | bss_base = 0x0000000000601028 + 0x200 |
rop流程:
第一次read:将read调用rop和跳转到bss段执行所需的rop写入栈
第二次read:将调用read的rop和调用syscall的rop写入bss
第三次read:将”/bin/sh”写入bash_addr,将syscall地址设置为返回地址,并将payload填充至59字节,使得read调用返回值为59触发execve。
srop
原理
当内核向某个进程发起(deliver)一个signal,该进程会被暂时挂起(suspend),进入内核(1),然后内核为该进程保存相应的上下文,跳转到之前注册好的signal handler中处理相应signal(2),当signal handler返回之后(3),内核为该进程恢复之前保存的上下文,最后恢复进程的执行(4)
在这四步过程中,第三步是关键,即如何使得用户态的signal handler执行完成之后能够顺利返回内核态。在类UNIX的各种不同的系统中,这个过程有些许的区别,但是大致过程是一样的。这里以Linux为例:
在第二步的时候,内核会帮用户进程将其上下文保存在该进程的栈上,然后在栈顶填上一个地址rt_sigreturn
,这个地址指向一段代码,在这段代码中会调用sigreturn
系统调用。因此,当signal handler执行完之后,栈指针(stack pointer)就指向rt_sigreturn
,所以,signal handler函数的最后一条ret
指令会使得执行流跳转到这段sigreturn代码,被动地进行sigreturn
系统调用。下图显示了栈上保存的用户进程上下文、signal相关信息,以及rt_sigreturn
:
利用
之前通过ROP的方式实现利用,但是需要构造的gadgets很多,流程也十分繁琐,而利用SROP利用流程就变得简洁许多。
同样利用read将调用read的rop和跳转到bss段执行所需的rop写入栈,利用第二次调用read将sig_frame和”/bin/sh”写入bss,并且构造第三次调用read的rop,第三次调用read是为了将rax设置为15,使syscall触发__NR_rt_sigreturn,sigreturn被设置为syscall,rdi为”/bin/sh”地址,rax为59,成功执行execve(“/bin/sh”)。
1 | def call_function(call_addr, arg1, arg2, arg3): |