Странное поведение 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пользователь с этим паролем для тестов.

TL;DR

Я сломал zsh, передав этот пароль в sudo на stdin

(Очень) длинная версия

Вот в чем ошибка: я использую sudoс targetpwфлагом, что означает, что пароль, запрашиваемый sudo, является паролем целевого пользователя, а не моим. Я использую passменеджер паролей (сайт) для всего, включая пароль root всех моих компьютеров/серверов. По очевидным причинам безопасности этот пароль генерируется случайным образом с помощью pass из 128 символов (потому что почему бы и нет). Как ленивый человек (уверен, вы тоже), я хочу использовать следующий псевдоним:

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)

Но вот что происходит при тестировании команды alias:

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

Здесь %цвет знака инвертирован: мой терминал — белый текст на черном фоне, черный %на белом фоне, как бы выделен белым цветом, и печатается после расшифровки пароля по проходу. Между печатью [sudo] password for root:и печатью этого %GPG запрашивает мою парольную фразу закрытого ключа. Я уже замечал перевернутый знак процента в какой-то момент, и, судя по тому, где и когда я его увидел, я полагаю, что это должно быть напечатано EOF(осторожно, я совсем в этом не уверен, см. в конце поста способ напечатать это %). Обратите внимание, что после этого странного знака я не root, но echo $?возвращаю 0...

Поэтому я сделал hexdump вывода, 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

0aсоответствует . Вы можете заметить, что здесь нет знака, а символ новой строки, необходимый для sudo (см. ), находится здесь NL.man ascii%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

Что здесь происходит? Как показано, даже замена экземпляра zsh на другой ( exec zsh) не исправляет ситуацию. Что здесь на самом деле сломано? pass, zsh или sudo ?

Итак, продолжим, проведем еще тесты, сделаем еще один hexdump в новом окне терминала (потому что я, очевидно, не знаю, как вернуть 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, но оболочка теперь находится в сломанном состоянии (я определенно не знаю, как правильно назвать это "сломанное состояние"). Выполнение команды pass root_password | sudo --stdin --shellпосле этой вызывает то же самое сообщение об ошибке, что и раньше. Следует отметить, что все одинаково на tty и на терминаторе, даже этот злой %символ.

Вот вся информация, которая, как мне кажется, может мне понадобиться. Я использую Arch Linux, и все упомянутые пакеты обновлены на сегодняшний день (2017-04-16 или 15, в зависимости от того, где вы находитесь на планете, сейчас 4 утра во Франции), что означает ( pacman -Qвывод):

  • ядро: 4.10.9-1
  • зш: 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который вызываетpass root_password
  • Установите SUDO_ASKPASSпеременную окружения в my .zprofileна путь к этому скрипту
  • Добавил строку alias sudo='sudo -A'в свой.zshrc

И вуаля! sudo commandТеперь при вызове, как и требовалось, запрашивается мой ключ GnuPG для расшифровки пароля root, и с его помощью sudo --shellя правильно получаю root-оболочку.

решение1

pass root_password | sudo --stdin --shellуспешно. passраспечатал пароль, sudoпрочитал его и запустил оболочку; вывод passзавершен, что означает, что поток, используемый для вывода passи ввода оболочкой, был закрыт; оболочка успешно завершила работу по EOF.

Последующие казни в течениеsudoокно тайм-аутане требовала аутентификации, поэтому оболочка сама получила пароль и попыталась выполнить его как команду, выдав эту ошибку.


Ваш псевдоним, вероятно, не сможет работать, если вам нужна оболочка или для запуска какой-либо команды, считывающей данные со stdin (но он работает для команд, которые этого не делают). Вы можете установить псевдоним, который будет работать, pass root_password | sudo --stdin trueесли захотите, а затем положиться на таймаут для запуска команд без пароля.

Вы также можете создать своего рода помощник askpass, который будет давать желаемое поведение. Он будет предоставлять пароль черезpass whenспросилдля этого sudo.

Связанный контент