Este é um post que não começa como você esperaria ou veria todos os dias, aqui está minha senha de 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
Difícil de lembrar, certo? Bem, esse não é o problema. E antes que você grite comigo, sim, eu mudei. Agora tenho um dummy_root
usuário com essa senha para testes.
DR
Quebrei o zsh passando essa senha para o sudo no stdin
Versão (muito) longa
Então aqui está o que está errado: eu uso sudo
com o targetpw
sinalizador, o que significa que a senha solicitada pelo sudo é a senha do usuário alvo, não a minha. Eu uso o pass
gerenciador de senhas (site) para tudo, incluindo a senha root de todos os meus computadores/servidores. Por razões óbvias de segurança, esta senha é gerada aleatoriamente por senha com 128 caracteres (por que não). Como sou uma pessoa preguiçosa (tenho certeza que você também), quero usar o seguinte alias:
alias sudo='pass mydomain.tld/hostname/users/root/password | sudo --stdin'
Para aqueles que não estão familiarizados com pass, ele basicamente criptografa senhas em uma árvore de diretórios com GnuPG, e invocar pass directory/subdirectory/password
descriptografa a senha e a imprime em stdout (claro, diretórios são opcionais, você pode armazenar tudo em desordem na raiz, no entanto, isso não é incentivado). O objetivo do alias é evitar fazer isso dezenas de vezes por dia:
$ 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)
Mas testando o comando alias, eis o que acontece:
$ pass root_password | sudo --stdin --shell
[sudo] password for root: %
$
Aqui, o %
sinal tem a cor invertida: meu terminal é um texto branco sobre fundo preto, o %
é preto sobre fundo branco, meio destacado em branco e é impresso após a descriptografia da senha por senha. Entre a impressão [sudo] password for root:
e a impressão deste arquivo %
, o GPG solicita minha senha de chave privada. Já notei o sinal de porcentagem invertida em alguma ocasião, e de onde e quando vi, acredito que seja para imprimir EOF
(atenção aqui, não tenho certeza disso, veja no final do post um método para imprima isso %
). Reparem que depois desse sinal estranho, não sou root, mas echo $?
retorno 0
...
Então, hexdumpei a saída de pass root_password
para ver se consigo mais informações sobre esse personagem. Aqui está o resultado:
$ 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
está NL
de acordo com man ascii
. Você pode notar aqui que o %
sinal não está aqui e que o caractere de nova linha necessário ao sudo (consulte Recursos man sudo
) está aqui.
Mas espere, agora a diversão começa: quando executo o comando duas vezes em uma janela do terminador ou tty recém-iniciada, eis o que acontece:
$ pass root_password | sudo --stdin --shell
[sudo] password for root: %
$ pass root_password | sudo --stdin --shell
zsh: parse error near `)'
$
E a partir daí, o zsh parece estar quebrado. Parece que deseja executar a saída do primeiro comando, mas apenas sudo
o segundo comando. Aqui estão alguns exemplos com este shell "quebrado":
$ 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
O que está acontecendo aqui? Conforme mostrado, substituir a instância zsh por outra ( exec zsh
) não corrige isso. O que está realmente quebrado aqui? passar, zsh ou sudo?
Então vamos continuar, vamos testar mais, vamos fazer outro hexdump em uma nova janela de terminal (porque obviamente não sei como colocar o zsh de volta em seu estado "normal"):
$ 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
$
Para onde foi esse sinal de porcentagem? Nenhuma idéia. Simplesmente não está aqui e ainda não sou root, mas o shell agora está em seu estado quebrado (definitivamente não sei como nomear esse "estado quebrado" corretamente). Executar o pass root_password | sudo --stdin --shell
comando após este gera a mesma mensagem de erro de antes. É de notar que tudo é igual no tty e no terminator, até mesmo esse %
personagem maligno.
Aqui estão todas as informações que posso pensar que alguém possa precisar para me ajudar. Estou no Arch Linux, e todos os pacotes mencionados estão atualizados a partir de hoje (16 ou 15/04/2017, dependendo de onde você estiver no planeta, atualmente 4h na França), o que significa ( pacman -Q
saída):
- núcleo: 4.10.9-1
- zsh: 5.3.1-2
- sudo: 1.8.19.p2-1
- passar: 1.7.1-1
- gnupg: 2.1.20-1
- terminador: 1,91-5
Aqui está umlinkpara o meu .zshrc, também pode ajudar.
E como prometido, aqui estão alguns passos para obter esse %
sinal malicioso pass
. Basta criar uma senha multilinha e pressionar Ctrl+ Dpara finalizar a senha sem adicionar uma nova linha final para imprimir esse caractere. Percebo aqui que apertar Ctrl+ Dnão é suficiente no final de uma linha com texto, é preciso fazer isso duas vezes. Mas se você adicionar uma linha final a essa senha multilinha, acertá-la uma vez encerrará a entrada da senha sem mostrá-la.
$ pass insert --multiline test_evil_percent
Enter contents of test_evil_percent and press Ctrl+D when finished:
fooReturn
barCtrl+DCtrl+D%
Aí está !
Mas com uma linha final, ela não aparece e recebemos nosso prompt de volta ao pressionar Ctrl+ Dapenas uma vez.
$ pass insert --multiline test_evil_percent
Enter contents of test_evil_percent and press Ctrl+D when finished:
fooReturn
barReturn
Ctrl+D
$
Então a questão primordial é: como fazer meu alias funcionar? Para aqueles que estão se perguntando, acabei de escrever um script de uma linha em meu diretório de scripts personalizados invocando pass root_password
e definindo a variável de ambiente SUDO_ASKPASS
para a passagem deste script em meu .zshrc
arquivo. Outra pergunta pode ser: o que diabos está acontecendo com zsh e sudo aqui?
Editar 16/04/2017: Solução
Graças aMichael HomeroEu escolhi esta solução para o meu problema:
- Escreva um script de uma linha chamado
askpass-sudo
que invoquepass root_password
- Defina a
SUDO_ASKPASS
variável de ambiente em my.zprofile
para o caminho deste script - Adicionei a linha
alias sudo='sudo -A'
ao meu.zshrc
E pronto! Invocar sudo command
agora pede, conforme desejado, minha senha de chave GnuPG para descriptografar a senha root, e com sudo --shell
isso obtenho corretamente um shell root.
Responder1
pass root_password | sudo --stdin --shell
teve sucesso. pass
imprimiu a senha e sudo
leu e iniciou um shell; a saída pass
finalizada, o que significa que o fluxo usado para saída pass
e entrada do shell foi fechado; o shell foi encerrado com sucesso no EOF.
As execuções subsequentes dentro dosudo
janela de tempo limitenão exigia autenticação, então o próprio shell recebeu a senha e tentou executá-la como um comando, dando aquele erro.
Seu alias não pode funcionar se você quiser um shell ou executar alguma leitura de comando do stdin (mas funciona para comandos que não funcionam). Você pode configurar um alias que seja executado, pass root_password | sudo --stdin true
se desejar, e então contar com o tempo limite para executar comandos sem uma senha.
Você também pode criar algum tipo de auxiliar askpass que forneça o comportamento desejado. Ele forneceria a senha por meio de pass
quandoperguntadopor isso por sudo
.