Explicação do comando para verificar o shellshock

32

Aqui está o comando que eu usei para verificar meu shell bash para o bug do Shellshock:

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

Alguém pode explicar o comando em detalhes?

heemail
fonte
4
Consulte também: unix.stackexchange.com/q/157329/70524 - A resposta do fixee pode ser útil.
muru

Respostas:

45

Esta resposta é derivada de um artigo original na Revista Fedora de Matthew Miller, licenciado sob a licença Creative Commons Attribution-Share Alike 4.0 .

Deixe-me explicar:

env x='() { :;}; echo OOPS' bash -c :

Isso imprimirá "OOPS" em um sistema vulnerável, mas sairá silenciosamente se o bash tiver sido corrigido.

env x='() { :;}; echo OOPS' bash -c "echo this is a test"

Isso imprimirá "OOPS" em um sistema vulnerável, mas será impresso “this is a test”se o bash tiver sido corrigido.

E você provavelmente já ouviu falar que isso tem algo a ver com variáveis ​​de ambiente. Mas, por que o código nas variáveis ​​de ambiente está sendo executado? Bem, não deveria ser - mas, por causa de um recurso que sou tentado a chamar de inteligente demais para o seu próprio bem, há espaço para uma falha. Bash é o que você vê como um prompt de terminal, mas também é uma linguagem de script e tem a capacidade de definir funções. Você faz assim:

$ Ubuntu()  { echo "Ubuntu is awesome."; }

e então você tem um novo comando. Lembre-se de que o echoaqui ainda não foi executado; é salvo como o que acontecerá quando executarmos nosso novo comando. Isso será importante em um minuto!

$ Ubuntu
 Ubuntu is awesome.

Útil! Mas, digamos, por algum motivo, precisamos executar uma nova instância do bash, como um subprocesso, e queremos executar o meu novo e impressionante comando sob isso. A instrução bash -c somecommandfaz exatamente isso: executa o comando fornecido em um novo shell:

$ bash -c Ubuntu
  bash: Ubuntu: command not found

Ooh. Triste. A criança não herdou a definição da função. Mas, ele inerente ao ambiente - uma coleção de pares de valores-chave que foram exportados do shell. (Esse é um conceito completo; se você não estiver familiarizado com isso, confie em mim por enquanto.) E, ao que parece, o bash também pode exportar funções. Tão:

$ export -f Ubuntu
$ bash -c Ubuntu
  Ubuntu is awesome.

O que é muito bom - exceto que o mecanismo pelo qual isso é realizado é desonesto . Basicamente, como não há mágica do Linux / Unix para executar funções em variáveis ​​de ambiente, a função de exportação na verdade apenas cria uma variável de ambiente regular contendo a definição da função. Então, quando o segundo shell lê o ambiente “recebido” e encontra uma variável com conteúdo que se parece com uma função, ele a avalia.

Em teoria, isso é perfeitamente seguro , porque, lembre-se, definir uma função não é realmente executa de fato . Exceto - e é por isso que estamos aqui - houve um erro no código em que a avaliação não parou quando o final da definição da função foi atingido. Apenas continua indo.

Isso nunca aconteceria quando a função armazenada em uma variável de ambiente é feita legitimamente, com export -f . Mas, por que ser legítimo? Um invasor pode criar qualquer variável de ambiente antiga e, se parecer com uma função, novos shells bash acharão que é!

Então, no nosso primeiro exemplo:

env x='() { :;}; echo OOPS' bash -c "echo this is a test"

O envcomando executa um comando com um determinado conjunto de variáveis. Nesse caso, estamos definindo xalgo que se parece com uma função. A função é apenas uma única :, que na verdade é um comando simples, definido como não fazer nada. Mas então, após o semi-colonque sinaliza o final da definição da função, há umaecho comando. Isso não deveria estar lá, mas não há nada que nos impeça de fazê-lo.

Em seguida, o comando fornecido para executar com este novo ambiente é um novo shell bash, novamente com um “ echo this is a test” ou “não faça nada: comando ", após o qual ele será encerrado, completamente inofensivo.

Mas - oops! Quando esse novo shell é iniciado e lê o ambiente, ele chega à xvariável e, como se parece com uma função, ele a avalia. A definição da função é carregada inofensivamente - e então nossa carga maliciosa também é acionada. Portanto, se você executar o procedimento acima em um sistema vulnerável, será “OOPS”impresso de volta. Ou, um invasor pode fazer muito pior do que apenas imprimir coisas.

αғsнιη
fonte
1
Muchas gracias para uma excelente explicação de por que isso funciona.
Doug R.
2
Note que envnão é necessário. Você pode obter o mesmo resultado (aprovação / reprovação, dependendo se Bash foi atualizado) usando o comando sem ele: x='() { :;}; echo OOPS' bash -c "echo this is a test". Isso ocorre porque o precedente de um comando com uma atribuição de variável passa essa variável e seu valor para o ambiente do comando ( bash -c "..."neste caso).
Pausado até novo aviso.
1
... mas pode ser necessário em alguns dos patches mais recentes. As coisas estão em fluxo.
Pausado até novo aviso.
4
@DennisWilliamson Se envé necessário ou não, é determinado pelo shell a partir do qual se executa o teste, não pelo shell que está sendo testado. (Podem ser os mesmos. Mesmo assim, estamos testando como o bash processa seu próprio ambiente.) Os shell do estilo Bourne aceitam NAME=value commandsintaxe; As conchas no estilo C (por exemplo csh, tcsh) não. Portanto, o teste é um pouco mais portátil env(às vezes, criando confusão sobre como ele funciona).
Eliah Kagan
2

Na versão sem patch debash ele armazena definições de funções exportadas como variáveis ​​de ambiente.

Armazene uma função xcomo,

$ x() { bar; }
$ export -f x

E verifique sua definição como,

$ env | grep -A1 x
x=() {  bar
}

Assim, pode-se explorar isso definindo suas próprias variáveis ​​de ambiente e interpretando-as como definições de função. Por exemplo, env x='() { :;}'seria tratado como

x() { :;
}

O que o comando para verificar o shellshock faz,

env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

De man env,

  1. env - execute um programa em um ambiente modificado.

  2. :não faça nada além de sair com o status de saída 0. veja mais

  3. Quando uma nova instância do bash sem patches é lançada como bash -c "echo this is a test", a variável ambiental criada é tratada como uma função e carregada. Assim, obtém-se a saída

    vulnerável
    isto é um teste

Nota: O eco fora da definição da função foi executado inesperadamente durante a inicialização do bash. A definição da função é apenas um passo para que a avaliação e a exploração ocorram, a própria definição da função e a variável de ambiente usada são arbitrárias. O shell examina as variáveis ​​de ambiente, vê x, que parece atender às restrições que conhece sobre a aparência de uma definição de função, e avalia a linha, também involuntariamente executando o eco (que pode ser qualquer comando, malicioso ou não) . Veja também este

souravc
fonte
Eu ainda descobri que qualquer função bash definida, se exportada, fosse avaliada no shell filho na versão corrigida do bash. Veja isto: chayan @ chayan: ~ / testr $ test () {echo "qualquer coisa"; }; teste de exportação -f; bash -c test Ouput: qualquer coisa Portanto, sua resposta não é direcionada de maneira adequada. A explicação de KasiyA sobre o erro como expandir a variável além de sua definição está correta, eu acho.
heemayl
@heemayl esse comportamento é natural. Mas se você tentar env test='() { echo "anything"; }' bash -c "echo otherthing", verá na saída otherthing. Isso é corrigido no patch. fique à vontade se ainda não estiver claro.
souravc 28/09/14
Por favor, deixe-me claro mais uma vez. No seu último comentário, estamos basicamente definindo a função e depois instruindo o bash para executar o eco. Neste exemplo, não chamamos a função no bash. Isso não teria a mesma saída no bash com e sem patch? Estou tendo uma noção de que o bug era basicamente o bash executando comandos colocados após a definição da função, enquanto a função nunca foi chamada em nenhum lugar posteriormente, por exemplo, se fizermos isso env test = '() {echo "nothing"; }; echo "foo" 'bash -c "echo otherthing". Por favor, esclareça-me neste contexto.
heemayl
@ heemayl Eu editei minha resposta, espero que agora esteja claro. Você está correto no exemplo do meu último comentário que não chamamos de função. Mas a diferença é que em um unpatched bashvocê pode chamar a função conforme definida, mas em um patch basha definição em si não existe.
souravc 28/09/14
@ heemayl: Não, isso está incorreto. Um Bash corrigido ainda passará a definição da função para o ambiente da criança. A diferença que o patch faz é que o código que segue a definição da função ( echo vulnerable) não é executado. Observe que nos patches mais recentes, a função passada deve ter um prefixo específico ( env 'BASH_FUNC_x()'='() { :;}; echo vulnerable' bash -c "echo this is a test"). Alguns patches mais recentes podem ser usados ​​em %%vez do primeiro ().
Pausado até novo aviso.