在 SSH 代理服务器上保护 $SSH_ORIGINAL_COMMAND

在 SSH 代理服务器上保护 $SSH_ORIGINAL_COMMAND

我想设置一个 SSH 服务器,作为客户端和“真实”SSH 服务器之间的代理。我通过利用 . 指向一个脚本来实现这一点,ForceCommandsshd_config脚本ForceCommand在转发会话之前会执行一些额外的操作,使用类似于以下行的内容:

ssh $theRealSSHServer $SSH_ORIGINAL_COMMAND

$SSH_ORIGINAL_COMMAND是因为我需要通过 SSH 执行命令。为了保护我的代理服务器免受恶意用户的攻击,我计划在转发会话之前执行以下操作:

if echo "$SSH_ORIGINAL_COMMAND" | grep "[^a-z|A-Z|0-9|[:space:]|_|-]" ; then
    echo "Illegal command: $SSH_ORIGINAL_COMMAND"
    exit 1
fi

即我只允许一组有限的字符,这应该使得无法插入任何将在代理服务器上本地执行的命令(如; rm -rf /)。这对可能执行的命令施加了相当大的限制,但比使用字典的静态方法要好。我真的不想使用带有受信任命令的字典,这似乎是解决这个问题的推荐方法,http://at.magma-soft.at/sw/blog/posts/The_Only_Way_For_SSH_Forced_Commands/

我的方法“安全”吗?如果不安全,是否可以修复,或者从安全角度来看这种方法是不可接受的?

答案1

ssh $theRealSSHServer $SSH_ORIGINAL_COMMAND

该命令虽然有些缺陷,但不容易允许命令注入。

即使不加引号,包含 的变量; rm -rf whatever也不会执行rm。 控制运算符(如 );在变量扩展发生之前就被识别。 当变量扩展时,命令行的逻辑已经固定,其他;无法更改它;它只是一个文字;。 这同样适用于像 这样的代码片段$(rm …)在变量扩展时弹出。

未加引号的变量仍会经历变量扩展(显然)、单词拆分和文件名生成。请参阅。您可以轻松干扰诚实用户在目标服务器上执行的操作。示例:

  • 用户调用ssh proxy 'echo "I need double spaces"'。他们在响应中获得单个空格。
  • 用户调用ssh proxy 'echo some/location/*'.*被展开在代理上并将结果作为参数传递给目标服务器echo。用户将看不到他们想要的内容。
  • 他们不仅看不到他们想要的东西,还可能在目标服务器上触发不必要的操作。想象一下,扩展将得到他们some/location/; rm -rf whatever(合法的名字)。这将是解释在目标服务器上。whatever将被删除(如果允许)。注意rm可以采用多个操作数;因此,如果代理上有更多匹配项出现在 之后some/location/; rm -rf whatever,则目标服务器上的对应项将被删除(如果允许)。

恶意用户可以利用这一点。例如:

  • 用户调用ssh proxy 'echo /etc/*'。这几乎就像ls /etc/在代理上执行一样。那么/boot//lib/modules/和子目录呢?获得的信息可能有助于他们准备攻击。
  • 用户可以将选项传递给ssh您的脚本调用。例如,如果他们输入,ssh proxy -- '-o HostName=another_server -o User=impostor -p 22'那么他们将启动与另一台服务器的连接,而不管$theRealSSHServer您的脚本中是否包含以下内容(比较这个答案)。他们还有更多可以(滥用)的选项。双引号$SSH_ORIGINAL_COMMAND将使大多数此类尝试失败。真正的对策是双划线

脚本中的命令应该是

ssh -- "$theRealSSHServer" "$SSH_ORIGINAL_COMMAND"

现在您不需要清理原始命令,除非您可能想为目标服务器执行此操作。在本地(在代理上),这是安全的。


脚本的其他部分可能仍会以允许执行(部分)变量的方式使用该变量。您的工作是确保它们不会这样做。示例:

  • 该变量提供命令的第一个字。

    • (明显的)

      SSH_ORIGINAL_COMMAND="echo foo"
      $SSH_ORIGINAL_COMMAND
      
      SSH_ORIGINAL_COMMAND="date"
      "$SSH_ORIGINAL_COMMAND"
      
    • (不太明显)

      SSH_ORIGINAL_COMMAND="date"
      cmmnd=""
      $cmmnd "$SSH_ORIGINAL_COMMAND"
      

      请注意,所有变量的严格双引号将阻止这种情况。

  • 扩展的内容在代理上进行解释。(它应该被解释,但仅在目标服务器上进行)。

    • (明显的)

      SSH_ORIGINAL_COMMAND='date; echo foo'
      eval "$SSH_ORIGINAL_COMMAND"
      
    • (不太明显)

      SSH_ORIGINAL_COMMAND="foo'; date'"
      sh -c "printf '%s\n' '$SSH_ORIGINAL_COMMAND'"
      

      比较。安全的方式是sh -c 'printf "%s\n" "$1"' sh "$SSH_ORIGINAL_COMMAND"

如果您的脚本不执行这样的操作,那么双引号$SSH_ORIGINAL_COMMAND将不会在代理服务器上注入代码。


注意,用户可以在调用中指定-L-t或 等选项。选项不会影响与目标服务器的连接。至少覆盖/ 、请求 tty 或不请求 tty,具体取决于用户是否在代理上获得 tty:-ossh-t-T

if [ -t 0 ]; then t=-t; else t=-T; fi
ssh "$t" -- "$theRealSSHServer" "$SSH_ORIGINAL_COMMAND"

如果您的脚本只是转发连接(而不是做“额外的事情”),那么最好请求用户使用ProxyJump

如果您的目标是抑制目标服务器上的某些命令,那么您可能应该在那里应用一些限制,而不是在代理上应用(设置适当的权限,使用某种受限的 shell,...)。

相关内容