基本概念

  • 关于重新定向符详细解释可参考该篇文章:Linux重定向

  • 简单来说,重新定向符的作用为:

    • 分为覆盖写入以及追加写入两种写入方式,分别用两种符号表示:<,<<

    • 尖的头头代表输出/输入的对象,判断输出输入流,另一侧,符号的”嘴部”后的数据,
      我们可以判断该数据/指令是用于输出指令还是用于对一个对象进行输入的准备材料。
      通过这样的判断,我们可以来区分该重定向是输入还是输出。

    • 具体实例可以看我放的博客传送门。

  • 关于错误重定向:
    典型代码有:

    1
    2
    2>&1
    2>>&1

    从上到下分别是覆写和追加标准错误输出到标准输出,这里都是在对输出流进行操作。

以web42为例

  • 如下图:

  • 该题中有源码:

    1
    >/dev/null 2>&1

    这段代码的含义就是将指令写入/dev/null路径之下,并且将标准错误输出流重定向到标准输出流。

相关原理

“空设备符”

在 Linux 中,/dev/null 是一个特殊的设备文件,常被称为 “空设备” 。具有以下特性:

  • 写入丢弃:任何写入到 /dev/null 的数据都会被立即丢弃,不会进行存储,就如同数据进入了黑洞。比如执行命令 echo "test" > /dev/null ,“test” 这个字符串不会被保存到任何地方 。
  • 读取为空:尝试从 /dev/null 读取数据时,会立即返回文件结束符(EOF) ,意味着无法从中读取到实际数据。

意义何在?

  • 首先我们需要明确:
    比如我们使用这样的命令:ls /some/dictionary/ > /dev/null,虽然该命令输出的结果被丢弃在空设备符中,但是这个操作是确切被落实了的,只是它的结果没有被显示在前端罢了。
  • 其作用:
    • 屏蔽无关输出:当你只关心命令是否成功执行,而不关心其正常输出内容时,可以将输出重定向到 /dev/null 来屏蔽大量无关的信息,使终端输出更加简洁,便于查看其他重要信息。例如,在一些脚本中,可能会执行多个命令,有些命令的输出只是中间过程信息,对最终结果没有影响,将这些输出重定向到 /dev/null 可以避免它们干扰视线。
    • 防止输出影响后续操作:如果后续的命令或操作依赖于当前命令的返回状态而不是输出内容,那么将当前命令的输出重定向到 /dev/null 可以确保不会因为输出内容而产生意外的影响。比如,在一个自动化脚本中,需要先执行一个可能会产生大量输出的命令来初始化一些环境设置,然后再执行其他关键命令,为了防止初始化命令的输出干扰后续命令的执行或解析,就可以将其输出重定向到 /dev/null
    • 安全和隐私考虑:在某些情况下,命令的输出可能包含敏感信息,将其重定向到 /dev/null 可以避免这些信息在终端上显示出来,从而提高安全性和保护隐私。

关于重定向于标准输出流

  • 这里代码写作:>/dev/null 2>&1,在>/dev/null这段代码中明确把还未写上的输出流指令重新定向到输入流,即/dev/null,所以可以明确标准输出就是被定义到了/dev/null,那么在后面的/dev/null就是该题中默认的输出流。
  • 所以该题代码目的是将标准错误输出的数据流重新定向到标准输出流,即/dev/null,”空设备符”。

绕过思路

  • 在源代码中有以下语句:

    1
    system($c." >/dev/null 2>&1")
  • 该语句将GET请求的值与重新定向语句进行拼接,上文已经解析了该段重新定向命令的作用,即将重新定向的命令输入到空设备符中,所以,我们需要注意,不可以直接传入指令值,需要用一个多余的指令将后方的重新定向指令给绕过。

  • 我们传入payload:?/c=ls;ls,第一个ls被成功执行,而第二个ls则用于执行后方重新定向指令,绕过后方重新定向指令。

  • 疑问:为什么不可以采用/?c="ls;ls"的payload。
    url中传入/?c=ls;ls,传入程序中可以被php自动处理解析为字符串。
    PHP 自动处理参数值为字符串$_GET['c'] 的值已经是字符串类型,无需额外引号包裹

    1
    $c = $_GET['c']; // 若 URL 为 ?c=ls;ls,则 $c = "ls;ls"

    在php代码中使用双引号,即如原码:

    1
    system($c . " >/dev/null 2>&1");

    这里使用双引号只是为了界定字符串边界,若$c='ls;ls',则传入system函数进行拼接后为"ls;ls >/dev/null 2>&1",那么我们就可以执行第一个ls。并且避免重新定向指令的执行。

  • 我们可以获得回显:

  • 继续传入payload:/?c=cat flag.php;ls

获得flag。