zsh 与 sudo --stdin 和带有随机字符的密码的奇怪行为

zsh 与 sudo --stdin 和带有随机字符的密码的奇怪行为

这篇文章的开头并不像您所期望或每天看到的那样,这是我的 root 密码::4\g&8n:6_F[`9Touc8Ls+L'8)>6!3,nNmUzR&Ub~w7NTd'^Lb0]`0`."u>tP\>XAspMLTt!@}=F6CP)NsSMYY7*xm'A!7`!n'tmAaGWoBhS|u4{k$*v/o|'%)mbXMw
很难记住,对吗?好吧,这不是问题。在你对我大喊大叫之前,是的,我改变了它。我现在有一个dummy_root具有该密码的用户进行测试。

长话短说

我破坏了 zsh 将此密码传递给标准输入上的 sudo

(非常)长的版本

所以这是错误的:我使用sudowithtargetpw标志,这意味着 sudo 询问的密码是目标用户的密码,而不是我的密码。我使用pass密码管理器(地点)的一切,包括我所有计​​算机/服务器的 root 密码。出于明显的安全原因,该密码是由 128 个字符的 pass 随机生成的(因为为什么不呢)。作为一个懒人(我相信你也是),我想使用以下别名:

alias sudo='pass mydomain.tld/hostname/users/root/password | sudo --stdin'

对于那些不熟悉 pass 的人来说,它基本上使用 GnuPG 对目录树中的密码进行加密,然后调用pass directory/subdirectory/password解密密码并将其打印到 stdout (当然,目录是可选的,您可以将所有内容无序地存储在根目录中,但不鼓励这样做)。别名的目的是避免每天执行数十次:

$ pass --clip root_password #Copy root password to clipboard 
$ sudo command
[sudo] password for root: paste
#command output (if the password is right of course)

但是测试别名命令,会发生什么:

$ pass root_password | sudo --stdin --shell
[sudo] password for root: %
$

在这里,%标志是颜色反转的:我的终端是黑底白字,%白底黑字,有点白色突出显示,并在密码解密后打印。在打印[sudo] password for root:和打印 this之间%,GPG 要求我提供私钥密码。我已经在某些情况下注意到了倒置的百分号,并且从我看到它的位置和时间,我相信它是打印EOF(这里小心,我根本不确定这一点,请参阅帖子末尾的方法打印这个%)。请注意,在这个奇怪的标志之后,我不是 root,而是echo $?返回0...

所以我对输出进行了十六进制转储,pass root_password看看是否可以获得有关该角色的更多信息。结果如下:

$ pass root_password
:4\?g&8n:6_F[`9Touc8Ls+L'8)>6!3,nNmUzR&Ub~w7NTd'^Lb0]`0`."u>tP\>XAspMLTt!@}=F6CP)NsSMYY7*xm'A!7`!n'tmAaGWoBhS|u4{k$*v/o|'%)mbXMw
$ pass root_password | hexdump -C
00000000  3a 34 5c 3f 67 26 38 6e  3a 36 5f 46 5b 60 39 54  |:4\?g&8n:6_F[`9T|
00000010  6f 75 63 38 4c 73 2b 4c  27 38 29 3e 36 21 33 2c  |ouc8Ls+L'8)>6!3,|
00000020  6e 4e 6d 55 7a 52 26 55  62 7e 77 37 4e 54 64 27  |nNmUzR&Ub~w7NTd'|
00000030  5e 4c 62 30 5d 60 30 60  2e 22 75 3e 74 50 5c 3e  |^Lb0]`0`."u>tP\>|
00000040  58 41 73 70 4d 4c 54 74  21 40 7d 3d 46 36 43 50  |XAspMLTt!@}=F6CP|
00000050  29 4e 73 53 4d 59 59 37  2a 78 6d 27 41 21 37 60  |)NsSMYY7*xm'A!7`|
00000060  21 6e 27 74 6d 41 61 47  57 6f 42 68 53 7c 75 34  |!n'tmAaGWoBhS|u4|
00000070  7b 6b 24 2a 76 2f 6f 7c  27 25 29 6d 62 58 4d 77  |{k$*v/o|'%)mbXMw|
00000080  0a                                                |.|
00000081

0aNL根据man ascii.您可以注意到此处%没有该符号,而是 sudo 所需的换行符(请参阅man sudo)在这里。

但是等一下,现在有趣的事情开始了:当我在新启动的终结器窗口或 tty 上运行命令两次时,会发生以下情况:

$ pass root_password | sudo --stdin --shell
[sudo] password for root: %
$ pass root_password | sudo --stdin --shell
zsh: parse error near `)'
$

从那时起,zsh 似乎就被破坏了。看起来它想执行第一个命令的输出,但只是sudo第二个命令。以下是这个“损坏”外壳的一些示例:

$ echo testpw | sudo --stdin --shell
zsh: command not found: testpw
$ echo testpw | cat
testpw
$ echo testpw | wc --bytes
11
$ echo anothertestpw | sudo --stdin --shell
zsh: command not found: anothertestpw
$ pass root_password | sudo --stdin --shell
zsh: parse error near `)'
$ exec zsh
$ echo testpw | sudo --stdin --shell
zsh: command not found:testpw

这里发生了什么?如图所示,evec 用另一个 ( ) 替换 zsh 实例exec zsh并不能解决此问题。这里真正损坏的是什么?通过、zsh 或 sudo ?

所以让我们继续,让我们测试更多,让我们在新的终端窗口中进行另一个十六进制转储(因为显然我不知道如何将 zsh 恢复到“正常”状态):

$ pass root_password | sudo --stdin --shell 2>&1 | hexdump -C
00000000  5b 73 75 64 6f 5d 20 70  61 73 73 77 6f 72 64 20  |[sudo] password |
00000010  66 6f 72 20 72 6f 6f 74  3a 20                    |for root: |
0000001a
$

这个百分号到哪里去了?不知道。它只是不在这里,而且我仍然不是 root,但 shell 现在处于损坏状态(我绝对不知道如何正确命名这个“损坏状态”)。在这之后运行该pass root_password | sudo --stdin --shell命令会引发与之前相同的错误消息。值得注意的是,tty 和终结者上的一切都是一样的,即使是这个邪恶的%角色。

这是我能想到的可能需要帮助我的所有信息。我使用的是 Arch Linux,所有提到的软件包都是截至今天的最新版本(2017 年 4 月 16 日或 15 日,具体取决于您在地球上的哪个位置,目前法国凌晨 4 点),这意味着(pacman -Q输出):

  • 内核:4.10.9-1
  • zsh:5.3.1-2
  • 须藤:1.8.19.p2-1
  • 通过:1.7.1-1
  • gnupg:2.1.20-1
  • 终结者:1.91-5

这里有一个关联对于我的 .zshrc,它也可能有帮助。

正如所承诺的,这里有一些步骤来获取这个恶意%标志pass。只需创建一个多行密码,然后按Ctrl+D完成密码而不添加尾随新行即可打印该字符。我注意到,在文本行末尾点击Ctrl+是不够的,您必须执行两次。D但是,如果您向该多行密码添加尾随行,则单击它一次将结束密码输入而不显示此内容。

$ pass insert --multiline test_evil_percent
Enter contents of test_evil_percent and press Ctrl+D when finished:
fooReturn
barCtrl+DCtrl+D%
就在那里!

但如果有尾行,它就不会出现,当我们只按Ctrl+D一次时,我们就会得到提示。

$ pass insert --multiline test_evil_percent
Enter contents of test_evil_percent and press Ctrl+D when finished:
fooReturn
barReturn
Ctrl+D
$

所以最根本的问题是:如何让我的别名发挥作用?对于那些想知道的人,我只是在我的自定义脚本目录中编写了一行脚本,调用pass root_password并将环境变量设置SUDO_ASKPASS为我的文件中该脚本的传递.zshrc另一个问题可能是:这里的 zsh 和 sudo 到底是怎么回事?


编辑2017-04-16:解决方案

谢谢迈克尔·霍默我为我的问题选择了这个解决方案:

  • askpass-sudo编写一个名为which 调用的一行脚本pass root_password
  • SUDO_ASKPASS将my中的环境变量设置.zprofile为该脚本的路径
  • 将该行添加alias sudo='sudo -A'到我的.zshrc

瞧!现在调用会sudo command根据需要询问我的 GnuPG 密钥密码来解密 root 密码,这样sudo --shell我就可以正确获得 root shell。

答案1

pass root_password | sudo --stdin --shell成功了。pass打印密码并sudo读取它并启动 shell;结束的输出,意味着shellpass用于输出和输入的流已关闭; passshell 在 EOF 时成功退出。

随后的执行sudo超时窗口不需要身份验证,因此 shell 收到密码本身并尝试将其作为命令执行,从而给出该错误。


如果您想要一个 shell 或运行一些从 stdin 读取的命令,则您的别名可能无法工作(但对于不需要的命令则可以)。如果需要,您可以设置一个运行的别名pass root_password | sudo --stdin true,然后依靠超时来运行没有密码的命令。

您还可以构建某种askpass 帮助程序来提供您想要的行为。它将通过pass以下方式提供密码为它由sudo.

相关内容