Dados sensíveis na substituição de processos

2

Digamos que eu faça:

#!/bin/bash
#content=$(cat -)
content="foo"
pass=$1
echo $content | ccrypt -f -k <(echo -n $pass)  

A permissão configurada na subestixa de processo /dev/fd/* files é confiável para manter a senha segura pela duração de ccrypt ?

EDIT: Removida linha ofensiva estúpida do script revelando passagem de qualquer maneira

    
por lash 22.07.2017 / 16:38

2 respostas

4

stracing bash -c 'cat <(echo pass)' mostra que <() chama pipe2 , fazendo isso em um fd maior (63 no meu caso) e passando isso como /dev/fd/SOMEFD em vez de <() .

Se ccrypt não conseguir ler a senha muito rápido, ela pode ser teoricamente interceptada pelo terceiro processo executado sob o mesmo usuário.

Você pode verificar isso, por exemplo, emulando a sequência em uma linguagem de script e, em seguida, usando um processo independente para ler a extremidade de leitura do pipe antes do destinatário.

    
por 22.07.2017 / 16:57
4

Vamos dividi-lo:

content=$(cat -)

Isso é o mesmo que content=$(cat) . Isso é usando substituição de comando que em bash usa um pipe. Em uma extremidade do pipe, cat está escrevendo o que lê em seu stdin. E bash na outra ponta está lendo isso para armazenar em $content .

No entanto, antes de fazer isso, ele remove caracteres de nova linha e também sufocaria em caracteres NUL. Para poder armazenar dados arbitrários em uma variável, você não pode usar bash ; você precisaria usar zsh e fazer algo como:

content=$(cat; echo .); content=${content%.}

para contornar o problema da remoção de novas linhas.

Observe que os dados a serem armazenados em $content são alimentados por meio do stdin do seu script. Isso estaria disponível (no Linux) como /proc/pid-of-your-script/fd/0 , que é o mesmo que /proc/pid-of-cat/fd/0 . E como cat copia para um canal para bash , também está em /proc/pid-of-cat/fd/1 e /proc/pid-of-script/fd/fd-to-the-other-end-of-the-pipe .

De qualquer forma, aqui, você está usando o conteúdo de $content apenas uma vez, por isso não faz sentido fazer essa etapa intermediária.

pass=$1

Aqui, você está dizendo que a própria chave é retirada do primeiro argumento do script.

Você não pode passar dados secretos como argumentos de linha de comando. Argumentos de linha de comando não são secretos. Eles aparecem na saída de ps -efwww . No Linux, /proc/pid/cmdline que os contém é legível por todos (por padrão; no Linux, o acesso a /proc/pid pode ser restrito administrativamente a processos com o mesmo euid, embora isso raramente afeta o comportamento ou ps entre outros coisas).

Em alguns sistemas, eles podem até ser registrados por meio de algum mecanismo de contabilidade / auditoria de processos.

echo $content está errado por vários motivos:

  • echo não pode ser usado para dados arbitrários. Ele não funciona adequadamente com argumentos como -n , -neneneene ... e dependendo do ambiente, coisas que contêm barras invertidas.
  • Um $content sem aspas significa a chamada do operador split + glob que você não deseja e causaria problemas se $content contiver caracteres de $IFS ou curingas.
  • adiciona um caractere extra de nova linha.

Você deseja:

printf %s "$content"

E certifique-se de que as novas linhas finais não foram removidas anteriormente.

Como printf faz parte de um pipeline, ele será executado em um processo filho cujo stdout será um pipe. No Linux, /proc/pid-of-that-child-process/fd/1 dará acesso a esse conteúdo. Então vai /proc/pid-of-ccript/0 .

Em

ccrypt -f -k <(echo -n $pass)

Existe novamente o problema de echo e o $pass não referenciado.

echo (novamente em execução em um processo filho) gravará a senha em um canal. A outra extremidade do canal será disponibilizada em fd n para ccrypt e <(...) expandirá para algo como /dev/fd/n ou /proc/self/fd/n . ccrypt abrirá esse arquivo (assim em um novo fd), que novamente (no Linux) estará disponível como /proc/pid-of-ccrypt/fd/that-fd além de /proc/pid-of-ccrypt/fd/n e /proc/pid-of-echo/fd/1

Agora, o principal problema em seu código não é a substituição de processo ou qualquer um desses outros canais, mas o fato de que a senha é fornecida como um argumento de linha de comando de um comando (aqui, seu script).

A substituição do processo envolve um canal normal, assim como com a substituição do comando $(...) e | . /dev/fd/x na maioria dos sistemas, exceto o Linux, só é significativo para o processo correspondente, portanto, não pode vazar para outros processos. Mas outros processos em execução como o mesmo euid (ou como root) poderiam ler a memória desses processos de qualquer maneira (como os depuradores fazem) e recuperar essa senha (ou provavelmente obtê-la da mesma fonte).

No Linux, /dev/fd é um link simbólico para /proc/self/fd e /proc/self um link simbólico dinâmico para /proc/the-pid . /proc/pid/fd é por padrão legível por processos com o mesmo euid (embora possa haver outras restrições adicionadas, as mesmas que restringem quem pode anexar um depurador a um processo).

Para fds que apontam para pipes, /proc/pid/fd/that-fd age como um pipe nomeado. Então, outro processo (novamente rodando como o mesmo euid ou root) poderia roubar o conteúdo do pipe. Mas, em qualquer caso, se eles podem fazer isso, eles também podem ler diretamente o conteúdo da memória do processo, então não adianta tentar se proteger contra isso.

Em vez de passar a senha em uma linha de comando, você poderia passá-la por meio de uma variável de ambiente. O ambiente é muito mais privado que a lista de argumentos. No Linux, /proc/pid/environ só é legível por processos com o mesmo euid (ou root ).

Assim, o seu script pode ser apenas:

#! /bin/sh -
exec ccrypt -f -E PASSWORD

E chame como

PASSWORD=secret-phrase the-script < data-to-encrypt 
    
por 22.07.2017 / 21:08