sudo --stdin とランダムな文字を含むパスワードを使用した場合の zsh の奇妙な動作

sudo --stdin とランダムな文字を含むパスワードを使用した場合の zsh の奇妙な動作

これは、皆さんが期待したり毎日目にするような始まりではない投稿です。これが私のルート パスワードです。: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テスト用にそのパスワードを持つユーザーがいます。

要約

このパスワードをstdinのsudoに渡すとzshが壊れてしまいます

(非常に)長いバージョン

では、何が間違っているのでしょうか。私はsudoフラグを使っていますtargetpw。つまり、sudo が尋ねるパスワードは、私のパスワードではなく、ターゲット ユーザーのパスワードです。私はpassパスワード マネージャー (サイト) を使用しています。これには、すべてのコンピューター/サーバーのルート パスワードも含まれます。セキュリティ上の理由から、このパスワードは、password によって 128 文字でランダムに生成されます (生成しない理由はありません)。私は怠け者なので (あなたもそうだと思いますが)、次のエイリアスを使用します。

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

pass についてよく知らない人のために説明すると、pass は基本的に、ディレクトリ ツリー内のパスワードを GnuPG で暗号化し、 を呼び出してpass directory/subdirectory/passwordパスワードを復号化して標準出力に出力します (もちろん、ディレクトリはオプションで、すべてをルートに無秩序に保存することもできますが、これは推奨されません)。エイリアスの目的は、これを 1 日に何十回も実行しないようにすることです。

$ 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: %
$

ここで、%記号の色が反転しています。私の端末は黒の背景に白のテキストで、 は%白の背景に黒で、白でハイライト表示され、pass によるパスワード復号後に印刷されます。 の印刷[sudo] password for root:とこの の印刷の間に%、GPG は私の秘密鍵のパスフレーズを要求します。私はすでに何度か反転したパーセント記号に気付いていましたが、いつどこで見たかから、これは印刷するためのものだと信じていますEOF(注意: これについてはまったく確信が持てません。この を印刷する方法については、投稿の最後を参照してください%)。この奇妙な記号の後、私は root ではありませんが、echo $?が返されることに注意してください0...

pass root_passwordそこで、その文字に関する詳細情報を取得できるかどうかを確認するために、出力を 16 進ダンプしました。結果は次のとおりです。

$ 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は に従います。ここで、記号がここにないこと、および sudo ( を参照) に必要な改行文字がここにあることがman asciiわかります。%man sudo

しかし、ちょっと待ってください。ここからが楽しいところです。新しく起動したターミネーター ウィンドウまたは tty でコマンドを 2 回実行すると、次のようになります。

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

そして、それ以降、zsh は壊れているように見えます。最初のコマンドの出力を実行しようとしているようですが、sudo2 番目のコマンドのみを実行しています。この「壊れた」シェルの例をいくつか示します。

$ 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この問題は修正されません。ここで実際に壊れているのは何でしょうか? pass、zsh、それとも sudo でしょうか?

では、続けて、さらにテストし、新しいターミナル ウィンドウで別の 16 進ダンプを実行してみましょう (明らかに、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
$

このパーセント記号はどこに行ったのでしょうか? わかりません。ここにはないし、私はまだルートではないのですが、シェルは壊れた状態になっています (この「壊れた状態」を適切にどう呼べばいいのかまったくわかりません)。pass root_password | sudo --stdin --shellこの後にコマンドを実行すると、前と同じエラー メッセージが表示されます。tty とターミネータでは、この悪意のある文字も含めてすべてが同じであることに注意してください%

ここに、私が思いつく限り、役に立つかもしれないすべての情報があります。私は Arch Linux を使っていますが、言及されているすべてのパッケージは、今日 (2017 年 4 月 16 日または 15 日、地球上のどこにいるかによって異なりますが、現在フランスでは午前 4 時) 時点で最新のものです。つまり、(pacman -Q出力):

  • カーネル: 4.10.9-1
  • 5.3.1-2 より前のバージョン
  • sudo: 1.8.19.p2-1
  • 合格: 1.7.1-1
  • gnupg: 2.1.20-1
  • ターミネーター: 1.91-5

がここにありますリンク私の .zshrc にも、役立つかもしれません。

そして約束どおり、この悪意のある%サインを取得するための手順をいくつか示しますpass。複数行のパスワードを作成し、末尾に新しい行を追加せずにCtrl+を押してパスワードを終了すると、この文字が出力されます。ここで、テキストを含む行の末尾で+Dを押すだけでは不十分であることに気付きました。2 回実行する必要があります。ただし、この複数行のパスワードに末尾の行を追加すると、1 回押すと、この文字が表示されずにパスワード入力が終了します。CtrlD

$ pass insert --multiline test_evil_percent
Enter contents of test_evil_percent and press Ctrl+D when finished:
fooReturn
barCtrl+DCtrl+D%
そこにそれがある !

Ctrlしかし、末尾の行がある場合は表示されず、 + を1 回押すだけでプロンプトが戻りますD

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

では、根本的な疑問は、エイリアスを機能させるにはどうすればよいかということです。疑問に思っている人のために説明すると、私はカスタム スクリプト ディレクトリに 1 行のスクリプトを記述し、ファイル内のこのスクリプトのパスをpass root_password環境変数に設定しました。もう 1 つの疑問は、zsh と sudo で一体何が起こっているのか、ということです。SUDO_ASKPASS.zshrc


編集 2017-04-16: 解決策

感謝マイケル・ホーマー私は自分の問題に対してこの解決策を選択しました:

  • askpass-sudoという1行のスクリプトを書いて、pass root_password
  • このスクリプトのパスをSUDO_ASKPASS環境変数に設定します.zprofile
  • ラインalias sudo='sudo -A'を追加しました.zshrc

すると、出来上がりです! 呼び出すsudo commandと、希望どおりに、ルート パスワードを復号化するための GnuPG キー パスフレーズが要求され、sudo --shellルート シェルが適切に取得されます。

答え1

pass root_password | sudo --stdin --shell成功しました。passパスワードを印刷してsudo読み取り、シェルを起動しました。出力はpass終了しました。つまり、シェルによる出力と入力に使用されるストリームpassが閉じられました。シェルは EOF で正常に終了しました。

その後の死刑執行はsudoタイムアウトウィンドウ認証を必要としなかったため、シェルはパスワード自体を受け取り、それをコマンドとして実行しようとして、エラーが発生しました。


シェルを使用したり、stdin からコマンドを読み込んで実行したりする必要がある場合、エイリアスは機能しない可能性があります (ただし、そうでないコマンドの場合は機能します)。pass root_password | sudo --stdin true必要に応じて実行されるエイリアスを設定し、タイムアウトを利用してパスワードなしでコマンドを実行することができます。

また、あなたが望む動作を与えるaskpassヘルパーのようなものを構築することもできます。それはpass尋ねたそのためにsudo

関連情報