ssh远程命令行参数如何解析


11

我已经看到了有关需要对远程ssh命令的参数进行两次转义的问题和解答。我的问题是:第二次解析究竟在何时何地完成?

如果我运行以下命令:

$ ssh otherhost pstree -a -p

我在输出中看到以下内容:

  |-sshd,3736
  |   `-sshd,1102
  |       `-sshd,1109
  |           `-pstree,1112 -a -p

远程命令(pstree)的父进程是sshd,似乎没有任何shell可以解析远程命令的命令行参数,因此似乎没有必要使用双引号或转义(但绝对是)。相反,如果我先在那儿ssh并获得一个登录shell,然后运行,pstree -a -p我会在输出中看到以下内容:

  ├─sshd,3736
     └─sshd,3733
         └─sshd,3735
             └─bash,3737
                 └─pstree,4130 -a -p

很明显,bash在这种情况下,有一个外壳可以进行命令行解析。但是,在我直接使用远程命令的情况下,似乎没有外壳,那么为什么需要双引号?

Answers:


22

总有一个远程外壳。在SSH协议中,客户端向服务器发送一个字符串以执行。SSH命令行客户端采用其命令行参数,并在参数之间留有空格。服务器采用该字符串,运行用户的登录外壳并将其传递给该字符串。

绕过远程外壳程序是不可能的。该协议没有像发送字符串数组那样的东西,该字符串数组可以在服务器上解析为argv数组。而且SSH服务器不会绕过远程Shell,因为这可能是安全限制:使用受限制的程序作为用户的Shell是提供仅允许运行某些命令的受限制帐户(例如,仅rsync帐户或一个git帐户)。

您可能看不到外壳,pstree因为外壳可能已经消失了。许多shell进行了优化,如果它们检测到它们将要“运行此外部命令,等待它完成,然后以命令的状态退出”,则外壳将运行“此外execve部命令”。这就是您的第一个示例中发生的情况。对比以下三个命令:

ssh otherhost pstree -a -p
ssh otherhost 'pstree -a -p'
ssh otherhost 'pstree -a -p; true'

前两个相同:客户端将完全相同的数据发送到服务器。第三个命令发送一个shell命令,该命令将破坏shell的exec优化。


2
哈!简直不敢相信你击败我回答了我自己的问题。我在发布问题的中途弄清楚了,并认为我应该自己问一遍并回答。
onlynone

10

我想我知道了:

$ ssh otherhost pstree -a -p -s '$$'
init,1         
  `-sshd,3736
      `-sshd,11998
          `-sshd,12000
              `-pstree,12001 -a -p -s 12001

要使用的参数pstree包括:显示命令行参数,显示pid和仅显示给定pid的父进程。这'$$'是一个特殊的shell变量,当bash评估命令行参数时,bash将用其自己的pid替换它。引用一次即可阻止它被我的本地shell解释。但是,并没有使用双引号或转义符来使它可以被远程shell解释。

如我们所见,它被替换12001为shell的pid。我们还可以从输出中看到:pstree,12001pid为12001的进程是pstree本身。所以,pstree是的壳呢?

我收集到的信息bash是正在被调用并且正在解析命令行参数,但是随后它被调用exec以将其自身替换为正在运行的命令。

似乎仅在单个远程命令的情况下才这样做:

$ ssh otherhost pstree -a -p -s '$$' \; echo hi
init,1         
  `-sshd,3736
      `-sshd,17687
          `-sshd,17690
              `-bash,17691 -c pstree -a -p -s $$ ; echo hi
                  `-pstree,17692 -a -p -s 17691
hi

在这种情况下,我要求运行两个命令:pstree后跟echo。而且我们可以在这里看到bash实际上作为的父级出现在进程树中pstree


对 !+ 1.体现了吉尔斯更正式地放在第一位,其次放在第二位的例子。也许应该给他早日的回答以功劳?
Cbhihe

0

支持其他答案所说的内容,我查找了在远程https://github.com/openssh/openssh-portable/blob/4f29309c4cb19bcb1774931db84cacc414f17d29/session.c#L1660上调用命令的代码...

1660    /*
1661     * Execute the command using the user's shell.  This uses the -c
1662     * option to execute the command.
1663     */
1664    argv[0] = (char *) shell0;
1665    argv[1] = "-c";
1666    argv[2] = (char *) command;
1667    argv[3] = NULL;
1668    execve(shell, argv, env);
1669    perror(shell);
1670    exit(1);

如您所见,它无条件地调用shell了第一个参数-c和第二个参数command。之前,该shell变量已设置为用户的登录外壳,如中所记录/etc/passwdcommand是此函数的一个参数,最终被设置为直接从字符串中读取的字符串(请参见session_exec_req同一文件)。因此,服务器根本不解释命令,但是始终在远程上调用外壳程序。

但是,SSH协议规范相关部分似乎并不要求这种行为。它只说

 byte      SSH_MSG_CHANNEL_REQUEST
 uint32    recipient channel
 string    "exec"
 boolean   want reply
 string    command

该消息将请求服务器开始执行给定命令。“命令”字符串可能包含路径。必须采取正常的预防措施以防止执行未经授权的命令。

这可能是因为并非所有操作系统都具有命令行外壳程序的概念。例如,对于经典MacOS ssh服务器来说,将“ exec”命令字符串提供给AppleScript解释器并不是一件疯狂的事情。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.