pwn学习之———通用ROP
攻防世界—pwn_100 wp
最近在ctf wiki上看到了通用ROP的技术,跟着教程把它的例题做了,感觉需要找一题比赛真题练练手。
准备工作
用die看看程序的基本信息
ELF64位的程序
用checksec看看开了啥保护
只开了NX保护
静态分析 by ida
进入函数sub_40068E,注意到V1只开辟了64h的空间
进入函数 sub_40063D,分析可知,该函数的功能类似read(0,input,200),就是输入200个byte的数据保存到栈中,存在明显的栈溢出漏洞
动态调试 by gdb
调试了很久,这部分省略了~~~
开始ROP
发现程序中没有现成的system和“/bin/sh”使用,所以我们考虑使用通用ROP解题
找到两个通用ROP的关键地址
cus_addr_end = 0x40075a |
控制程序返回到这两个地址,我们可以控制rbx,rbp,r12,r13,r14,r15,rdx,rsi,edi 寄存器的数据,即64位程序函数的传参都没问题了,并且还可以调用我们构造的[r12+rbx*8]地址处所指向的的函数。
思路:
1.利用puts函数泄露libc中函数的地址
具体实现:
利用栈溢出覆盖栈中原本的返回地址为cus_addr_end,将我们需要的寄存器参数(puts_got_addr)写入,再将返回地址覆盖为cus_addr_front,这样就可以执行puts函数泄露puts函数的地址,注意执行完cus_addr_front后还会接下去执行cus_addr_end处的pop,所以需要填充8 * 7 = 56 byte的数据,最后再将返回地址覆盖为main_addr,因为我们之后还得再利用栈溢出漏洞,还得注意将payload填充至200 byte(输入函数有要求)
!注意这里输入数据用的是send()而不是sendline,因为输入函数是read()而不是gets()
接下来接收打印在屏幕上的puts地址,再与libc中puts偏移地址相减获得libc基址——libc_base,之后就可以轻松获取execve函数的地址。
2.利用read函数将字符串写入bss段
具体实现:
类似第一步的操作,将r12寄存器的值设置为read函数got表地址——read_got_addr、将其参数设置为bss段偏移为16的地址——bss_base_16,执行read()
你可能会好奇为什么不直接用bss段的起始地址而是用bss段偏移为16的地址?
!注意这里有一个大坑,调试了好几遍才发现
main函数的开始从cs:stdin和cs:stdout里取值赋给寄存器,作为setbuf函数的参数,并且bss段首存在stdin,stdout 结构体指针,如果在bss段首写入数据将这两个结构体指针覆盖了,程序运行到call_setbuf函数会报错,然后终止,所以要避开这两个结构体指针,从bss_base_16写入execve地址——sys_addr
最后利用send()将“/bin/sh”写入bss_base_16 + 8处。
3.再次利用通用ROP执行execve
具体实现:
类似第一步的操作,将r12寄存器的值设置为bss_base_16、将其参数设置bss_base_16 + 8,执行execv(“/bin/sh”)。
exp:
from pwn import * |