参考文献:

考察点:stdout(标准输出)已经被关闭的情况下,通过把标准输出重定向到标准输入对应的终端/管道方向,保证能看到输出。

1. 题目描述

1
关闭了输出流,一定是最安全的吗?

2. 解题过程

<1> 收集信息

info

<2> ida 查看主逻辑

伪代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int __fastcall main(int argc, const char **argv, const char **envp)
{
char buf[40]; // [rsp+10h] [rbp-30h] BYREF
unsigned __int64 v5; // [rsp+38h] [rbp-8h]

v5 = __readfsqword(0x28u);
setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
puts(s);
puts(asc_BF0);
puts(asc_C70);
puts(asc_D00);
puts(asc_D90);
puts(asc_E18);
puts(asc_EB0);
puts(" * ************************************* ");
puts(aClassifyCtfsho);
puts(" * Type : Linux_Security_Mechanisms ");
puts(" * Site : https://ctf.show/ ");
puts(" * Hint : Turn off output, how to get flag? ");
puts(" * ************************************* ");
if ( fork() )
{
wait(0LL);
sleep(3u);
printf("flag is not here!");
}
else
{
puts("give you a shell! now you need to get flag!");
fclose(_bss_start);
read(0, buf, 0x20uLL);
system(buf);
}
return 0;
}

关键逻辑

  1. if (fork()):这里使用 fork() 函数创建一个子进程。父进程中,fork() 返回子进程的进程 ID,所以进入 if 语句块;子进程中,fork() 返回 0,所以进入 else 语句块。

在父进程中:

  1. wait(0LL):父进程通过 wait() 函数等待子进程结束,以确保子进程执行完毕。
  2. sleep(3u):父进程睡眠 3 秒钟。
  3. printf("flag is not here!"):输出提示信息,表明 flag 不在此处。

在子进程中:

  1. puts("give you a shell! now you need to get flag!"):输出提示信息,表示给予用户一个 shell,让其获取 flag。
  2. fclose(_bss_start):关闭文件输出流。
  3. read(0, buf, 0x20uLL):从标准输入中读取用户输入的命令,并存储在 buf 中。
  4. system(buf):执行用户输入的命令。

<3> 困境

即使输入 cat /ctfshow_flag,由于 stdout(标准输出)已经被 fclose 关闭了,flag 内容会从文件读入内存,但无法吐到屏幕上。

<4> 前置知识

1. 什么是文件描述符(FD)

在 Linux 看来,一切皆文件。程序每打开一个流,都会分配一个数字编号:

1
2
3
0 (stdin):标准输入,相当于程序的“耳朵”(听你输入)。
1 (stdout):标准输出,相当于程序的“嘴巴”(打印给你看)。
2 (stderr):标准错误,相当于程序的“求救声”(打印错误信息)。

2. exec() 函数

exec 函数族的本质:

  • 它不是创建新进程,而是用新程序替换当前进程镜像
  • 调用成功后,原来的代码不会继续往下执行
  • 进程号通常不变,继承原有文件描述符(除非被设置为 close-on-exec

<5> 破局:重定向

方法

使用 exec 函数来执行 sh 命令,并使用 1>&0 来进行输出重定向。这个命令将标准输出重定向到标准输入,实际上就是将命令的输出发送到后续命令的输入。

具体来说,1>&0 中的 1 表示标准输出,0 表示标准输入。通过将标准输出重定向到标准输入,可以实现将命令的输出作为后续命令的输入。这样可以在执行 sh 命令后,进入一个交互式的 Shell 环境,可以在该环境中执行命令并与用户进行交互。

也可以直接 exec cat /ctf* 1>&0,将 cat /ctf* 命令的输出发送到标准输入,实际上就是将命令的输出再次输出到屏幕上。

<6> payload

1
exec cat /ctf* 1>&0

或者:

1
exec /bin/sh 1>&0
宇宙级免责声明
重要声明:本文仅供合法授权下的安全研究与教育目的!
  1. 合法授权:本文所述技术仅适用于已获得明确书面授权的目标或自己的靶场内系统。未经授权的渗透测试、漏洞扫描或暴力破解行为均属违法,可能导致法律后果(包括但不限于刑事指控、民事诉讼及巨额赔偿)。
  2. 道德约束:黑客精神的核心是建设而非破坏。请确保你的行为符合道德规范,仅用于提升系统安全性,而非恶意入侵、数据窃取或服务干扰。
  3. 风险自担:使用本文所述工具和技术时,你需自行承担所有风险。作者及发布平台不对任何滥用、误用或由此引发的法律问题负责。
  4. 合规性:确保你的测试符合当地及国际法律法规(如《计算机欺诈与滥用法案》(CFAA)、《通用数据保护条例》(GDPR)等)。必要时,咨询法律顾问。
  5. 最小影响原则:测试过程中应避免对目标系统造成破坏或服务中断。建议在非生产环境或沙箱环境中进行演练。
  6. 数据保护:不得访问、存储或泄露任何未授权的用户数据。如意外获取敏感信息,应立即报告相关方并删除。
  7. 免责范围:作者、平台及关联方明确拒绝承担因读者行为导致的任何直接、间接、附带或惩罚性损害责任。
安全研究的正确姿势:
  • 先授权,再测试
  • 只针对自己拥有或有权测试的系统
  • 发现漏洞后,及时报告并协助修复
  • 尊重隐私,不越界
警告:技术无善恶,人心有黑白。请明智选择你的道路。
声明参考链接:https://blog.csdn.net/2402_84408069/article/details/157263936