ciscn_2019_c_1 Writeup
ret2libc
前提
- 本程序防御策略
1 | checksec ./cisscn_2019_c_1 |
- 本程序逻辑是输入一个字符串,按照内置加密函数逻辑加密转化为密文。但在加密函数中存在溢出漏洞。
- 存在
strlen()
函数。使用该函数计算读入字符串的长度,并逐位加密。- 存在漏洞:该函数接收到
\0
则认为已到达字符串结尾。
- 存在漏洞:该函数接收到
- 存在
put()
函数,可作为输出。 - amd64的参数调用顺序是如下序列的自后而前,即:完成传递参数(地址)->本函数地址 -> 返回地址
... -> pop_rdi;ret -> argv -> call_addr -> ret_address
- 在全局偏移表GOT(Global Offset Table)中,存储的是该函数在内存中的地址。
- 在程序链接表PLT(Process Link Table)中存储的是该函数在GOT中的位置。
- amd64中函数调用使用的寄存器顺序是:rdi、rsi、rdx、rcx、r8、r9。
思路
- 泄露远端服务器中某函数在内存中的地址
- 计算所需gadgets在远端服务器中的地址
- 构造payload劫持控制流获得shell
确定偏移量
注意与x86不同,输入一段字符串占用了ret,由于抛出异常机制,不会返回错误地址。但此时RIP的状态时准备跳转但跳转失败。故而,RIP所在的地址即是ret返回地址。通常通过
pwndbg> x/gx rsp
查看返回地址处内存。注意使用
pattern.py
、msf_pattern
等脚本测算输入点到ret的偏移量时,由于本题输入的字符串经过了加密。需要先对其还原才能使用重新与脚本对比,从而计算出偏移。这也是这类脚本的使用原理。即生成一串片段序列唯一的长字符串,通过比对EIP中的数据在本字符串中的位置,测算出输入点与ret处的距离。
1 | 测算序列:(150) |
泄露信息以测算内存中libc地址
获得本elf中的gadgets:
ROPgadget --binary ./ciscn_2019_c_1 --only "pop|ret"
ROPgadget --binary ./ciscn_2019_c_1 --only "ret"
构造泄露信息的payload:
- 当esp指向
pop rdi;ret
时,rip指向puts_got。 - 这意味着rip中的数据是puts_got所保留的
put()
函数内存地址。它将被利用作为下一步put()
的参数。使得控制流被劫持,输出了put()
函数在远端服务器的内存中的地址。 - 最后让程序流返回到开始以继续利用
payload1 = 'A'*(88) + p64(pop_rdi_addr) + p64(put_got) + p64(put_plt) + p64(_start)
- 当esp指向
劫持控制流获得Shell
- 由于本题未给定
libc.so.6
的版本,故使用第三方模块LibcSearcher
。 - 根据函数调用过程构造payload:
payload2 = 'A'*(88) + p64(ret) + p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(sys_addr)
完整exp
1 | from pwn import * |
ciscn_2019_c_1 Writeup