comportamento estranho do zsh com sudo --stdin e senha com caracteres aleatórios

comportamento estranho do zsh com sudo --stdin e senha com caracteres aleatórios

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_rootusuá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 sudocom o targetpwsinalizador, o que significa que a senha solicitada pelo sudo é a senha do usuário alvo, não a minha. Eu uso o passgerenciador 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/passworddescriptografa 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_passwordpara 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

0aestá NLde 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 sudoo 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 --shellcomando 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 -Qsaí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_passworde definindo a variável de ambiente SUDO_ASKPASSpara a passagem deste script em meu .zshrcarquivo. 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-sudoque invoquepass root_password
  • Defina a SUDO_ASKPASSvariável de ambiente em my .zprofilepara o caminho deste script
  • Adicionei a linha alias sudo='sudo -A'ao meu.zshrc

E pronto! Invocar sudo commandagora pede, conforme desejado, minha senha de chave GnuPG para descriptografar a senha root, e com sudo --shellisso obtenho corretamente um shell root.

Responder1

pass root_password | sudo --stdin --shellteve sucesso. passimprimiu a senha e sudoleu e iniciou um shell; a saída passfinalizada, o que significa que o fluxo usado para saída passe entrada do shell foi fechado; o shell foi encerrado com sucesso no EOF.

As execuções subsequentes dentro dosudojanela 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 truese 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 passquandoperguntadopor isso por sudo.

informação relacionada