通过带有本地脚本的ssu向另一个用户


1

我有以下片段:

ssh root@"${DO_DROPLET_IP}" -i "${SSH_PRIVKEY_PATH}" su www -c sh -c -- \
  "./server-user-setup.sh" "${BB_USER}" "${APP_USER}"

受到以下(有效)代码段的启发:

ssh root@"${DO_DROPLET_IP}" -i "${SSH_PRIVKEY_PATH}" sh -s -- < \
  "./server-root-setup.sh" "${BB_USER}" "${APP_USER}"

这两个文件都是本地文件

但是,我找不到引用或转义top命令以使其成功运行的方法。以上抛出${value of BB_USER}": ./server-user-setup.sh: Permission denied。我尝试了许多变体:


有人可以向我解释如何正确引用/转义此命令吗?


更新:越来越接近关闭-这会触发脚本,但不会传递参数!

ssh root@"${DO_DROPLET_IP}" -i "${SSH_PRIVKEY_PATH}" sh -s -- su www -c < \
  "./server-user-setup.sh" "${BB_USER}" "${APP_USER}"

退货

server-user-setup.sh: Wrong number of arguments. # Part of the script - no args passed
su www -c ${value of BB_USER} ${value of APP_USER}    

道歉!当地人
尼克·布尔

大声笑是的,抱歉。一天的疲倦使我无法正确输入内容。我以root身份摇摆,所以没有sudo
Nick Bull

1
我不确定这是否是您想要的(因此仅作评论);我不确定它是否会起作用;并记住从互联网运行随机代码是危险的(尤其是作为root用户)。这种疯狂:ssh root@"${DO_DROPLET_IP}" -i "${SSH_PRIVKEY_PATH}" 'su www -c "exec sh -s -- '"'${BB_USER}' '${APP_USER}'"'"' <"./server-user-setup.sh"
卡米尔Maciorowski

@KamilMaciorowski那是...哇。我对没有得到更好的感觉。将其发布为他人的答案!我会尽快接受您的请求:)
尼克·布尔

@KamilMaciorowski它正常工作!我将其保留为某人在问题中的编辑,对于正确的解释,该答案将得到奖励:)
Nick Bull

Answers:


1

tl; dr

ssh root@"${DO_DROPLET_IP}" -i "${SSH_PRIVKEY_PATH}" \
  'su www -c "exec sh -s -- '"'${BB_USER}' '${APP_USER}'"'"' <"./server-user-setup.sh"

可以通过BB_USERAPP_USER变量进行代码注入。


长答案

让我们分析发生了什么。这很复杂。为了清楚起见,我们假设:

DO_DROPLET_IP=ip
SSH_PRIVKEY_PATH=key
BB_USER=b-user
APP_USER=a-user

原始(有效)代码段

ssh root@"${DO_DROPLET_IP}" -i "${SSH_PRIVKEY_PATH}" sh -s -- < \
    "./server-root-setup.sh" "${BB_USER}" "${APP_USER}"

所有变量都在本地扩展。重定向是本地的,并且不要在中间。使用我们假定的变量值,这是等效的命令:

<"./server-root-setup.sh" ssh root@"ip" -i "key" sh -s -- "b-user" "a-user"

ssh得到这些参数:root@ip-ikeysh-s--b-usera-user。由于root@ip看起来像user@hostname,因此以下未识别为选项(如-i)或选项参数(如key的选项参数-i)的第一个参数被认为是启动操作数数组以从中构建远程命令。所说的参数是sh,这是命令ssh尝试在远程运行:

sh -s -- b-user a-user

这将启动sh,期望从其stdin获得命令(由于-s)。--是POSIX约定,它告诉工具其后的所有内容都不是一种选择(请参阅指南10)。这样即使我们${BB_USER}扩展到-f大约,sh也不会认为它是一种选择。下列操作数被设置为外壳(的位置参数$1$2)。Stdin由提供ssh,本地文件./server-root-setup.sh通过其中流送。

如果文件和相关的两个变量在远程端都可用,则可以在此处使用以下命令获得(几乎)相同的结果:

sh "./server-root-setup.sh" ${BB_USER} ${APP_USER}

或者,如果脚本中有适当的shebang:

./server-root-setup.sh ${BB_USER} ${APP_USER}

因此,该代码段确实是在远程端运行本地脚本的一种方式。但请注意,我故意未引用${BB_USER}${APP_USER}。在原始片段中,它们在本地引用,但这没关系,因为ssh构建并通过将命令作为字符串进行解析以在远程解析。如果两个变量中的任何一个包含内部空间,则遥控器sh将获得比您期望的更多的参数。前导或尾随空格将丢失。字符,如$"'\会引起麻烦,除非该变量的值被故意设计成解析(但当时他们无法在当地使用,以产生同样的结果,这就是为什么我写了“几乎”)。

如果例如${APP_USER}扩展到会发生更糟的事情a-user& rogue_command。在远端,您会得到

sh -s -- b-user a-user& rogue_command

请注意,当您在本地运行事物(带引号或不带引号)时,此类代码注入不会发生

./server-root-setup.sh ${BB_USER} ${APP_USER}

因为像&;这样的分隔符在变量扩展之前而不是之后被识别。但是,如果在本地扩展变量,然后在远端再次解析生成的字符串,则情况完全不同。就像是远程eval

我想${BB_USER}${APP_USER}是用户名,相当安全的弦,所以整个事情的作品,尽管可能一般问题。但是,如果您没有完全控制这些变量,则该方法并不安全。


您的(失败)尝试

ssh root@"${DO_DROPLET_IP}" -i "${SSH_PRIVKEY_PATH}" su www -c sh -c -- \
  "./server-user-setup.sh" "${BB_USER}" "${APP_USER}"

这次没有重定向。再次在本地扩展变量,等效命令如下所示:

ssh root@"ip" -i "key" su www -c sh -c -- "./server-user-setup.sh" "b-user" "a-user"

ssh 尝试在远程运行此:

su www -c sh -c -- ./server-user-setup.sh b-user a-user

请注意,所有参数现在都是的参数su。目前,这sh只是选项的一个选项参数-c,类似地--另一个 -c选项的一个选项参数(因此这里并不表示选项的结束)。我的测试表明,从多个-c选项到su仅最后一个选项生效。这意味着-c sh被丢弃,su使用任何壳中(远程)被指定/etc/passwdwww用户。这可能是,也可能不是sh。假设是some-shell。接下来是(作为www用户)运行的内容:

some-shell -c --   # but wait, it's not all

剩余的操作数为su,因此

some-shell -c -- ./server-user-setup.sh b-user a-user

如果some-shell是像sh或的通用外壳bash,则其行为类似。这里-c不带选项参数(请参阅规范),--而是指示选项的结尾。在这种情况下,不能将以下参数用作选项,因此我们可以省略--

some-shell -c ./server-user-setup.sh b-user a-user

就像

sh -c [-abCefhimnuvx] [-o option]... [+abCefhimnuvx] [+o option]... command_string [command_name [argument...]]

或(丢弃我们不使用的所有内容)

some-shell -c command_string command_name argument

所以./server-user-setup.shcommand_stringb-userCOMMAND_NAMEa-user论证

-c
command_string操作数读取命令。0command_name操作数的值中设置特殊参数[…]的值,并从其余参数操作数中依次设置位置参数($1$2等等)。[…]

实际上,远程some-shell尝试读取(源)./server-user-setup.sh,字符串a-user可用$1。外壳认为自己的名称是特殊参数0(现在带有value b-user)。该名称用于此类错误消息中:

b-user: ./server-user-setup.sh: Permission denied

显然./server-user-setup.sh在远端,但是www用户看不到它。


我的方法(仍然不安全):

ssh root@"${DO_DROPLET_IP}" -i "${SSH_PRIVKEY_PATH}" \
  'su www -c "exec sh -s -- '"'${BB_USER}' '${APP_USER}'"'"' <"./server-user-setup.sh"
# 1                         12                          23 3

最后一行(评论)仅用于枚举上一行中的引号。为了理解该命令的工作原理,我们需要“解密”这个引用狂潮。重要的事情是:

  • 里面的所有内容13单引号都应按字面意义使用。
  • ${BB_USER}并且${APP_USER}用双引号括起来2; “内部”单引号属于加引号的字符串,它们不会阻止变量扩展。
  • 无论在右引号之前紧跟引号(例如,上面的引号12),被引用的字符串都将被连接起来。

知道了这一点,我们就可以使用扩展变量来告诉命令,如下所示:

<"./server-user-setup.sh" ssh root@"ip" -i "key" 'su www -c "exec sh -s -- Xb-userX Xa-userX"'

其中X 表示(仅适用于我们,此处和现在)单引号字符,该单引号字符属于以sulast 开头和结尾的字符串"。抱歉,我无法以更清晰的方式表达这一点。希望当我们看到结果时,它的神秘性会降低ssh

root@ip-ikeysu www -c "exec sh -s -- 'b-user' 'a-user'"

最后一个参数包含双引号和单引号。这恰好是字符串ssh将在远程运行。注意,我小心地将整个字符串作为一个参数传递给ssh,因此该工具不会根据多个参数和空格来构建字符串。这样,我可以完全控制字符串。通常,这很重要,例如,如果${BB_USER}扩展到带有尾随空格的内容(在这种情况下,原始代码段将失败)。

在远程端运行的命令:

su www -c "exec sh -s -- 'b-user' 'a-user'"

的行为su已经讨论过。请注意,整个引用的字符串是的选项参数-c。如果没有引号,则-s可以选择su。这是作为www用户运行的:

some-shell -c "exec sh -s -- 'b-user' 'a-user'"

请注意,su调用中的引号还会导致不存在command_name和不存在argument(比较some-shell -c command_string command_name argument上面的某处)。然后some-shell运行:

exec sh -s -- 'b-user' 'a-user'

这可以在没有的情况下运行exec,但是由于我们不再需要some-shell了,exec可能是一个好主意。它使shreplace 成为替换子some-shell,而不是some-shellsh作为其子代,仅sh保留,好像在我们运行some-shell

sh -s -- 'b-user' 'a-user'

现在,这与原始代码段的运行非常相似(sh -s -- b-user a-user)。多亏了额外的引号,空格或其他一些麻烦的字符才出现${BB_USER}${APP_USER}可能不会破坏任何内容。但是'还是"会的。代码注入仍然是可能的。考虑${APP_USER}扩展到"& rogue_command;"。在远程运行的第一件事是:

su www -c "exec sh -s -- 'b-user' '"& rogue_command;"'"

没关系,some-shell将引发错误,rogue_command无论如何都会运行。确保您的变量扩展为安全字符串。

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.