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.

相關內容