Это пост не начинается так, как вы ожидаете или видите каждый день, вот мой пароль 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
.