Aparentemente, existe uma vulnerabilidade (CVE-2014-6271) no bash: ataque de injeção de código por variáveis de ambiente Bash especialmente criadas
Estou tentando descobrir o que está acontecendo, mas não tenho muita certeza de entendê-lo. Como pode echo
ser executado como está entre aspas simples?
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
vulnerable
this is a test
EDIT 1 : Um sistema corrigido fica assim:
$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"
bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
this is a test
EDIT 2 : Existe uma vulnerabilidade / correção relacionada: CVE-2014-7169 que usa um teste ligeiramente diferente:
$ env 'x=() { :;}; echo vulnerable' 'BASH_FUNC_x()=() { :;}; echo vulnerable' bash -c "echo test"
saída sem remendo :
vulnerable
bash: BASH_FUNC_x(): line 0: syntax error near unexpected token `)'
bash: BASH_FUNC_x(): line 0: `BASH_FUNC_x() () { :;}; echo vulnerable'
bash: error importing function definition for `BASH_FUNC_x'
test
saída parcialmente corrigida (versão inicial) :
bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `x'
bash: error importing function definition for `BASH_FUNC_x()'
test
saída corrigida até e incluindo CVE-2014-7169:
bash: warning: x: ignoring function definition attempt
bash: error importing function definition for `BASH_FUNC_x'
test
EDIT 3 : a história continua com:
bash
shellshock
vulnerability
jippie
fonte
fonte
vulnerable
aparece na saída. O principal problema é que o bash também está analisando e executando o código após a definição da função. Veja a/bin/id
parte de seclists.org/oss-sec/2014/q3/650 para outro exemplo.Respostas:
O bash armazena definições de funções exportadas como variáveis de ambiente. As funções exportadas são assim:
Ou seja, a variável de ambiente
foo
possui o conteúdo literal:Quando uma nova instância do bash é iniciada, ele procura essas variáveis de ambiente especialmente criadas e as interpreta como definições de função. Você pode até escrever um você mesmo e ver que ele ainda funciona:
Infelizmente, a análise das definições de função de strings (as variáveis de ambiente) pode ter efeitos mais amplos do que o pretendido. Nas versões sem patch, ele também interpreta comandos arbitrários que ocorrem após o término da definição da função. Isso ocorre devido a restrições insuficientes na determinação de cadeias semelhantes a funções aceitáveis no ambiente. Por exemplo:
Observe que 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 olha para as variáveis de ambiente, vê
foo
, que parece atender às restrições que conhece sobre a aparência de uma definição de função, e avalia a linha, sem querer também executando o eco (que poderia ser qualquer comando, malicioso ou não).Isso é considerado inseguro, porque normalmente não é permitido ou esperado que as variáveis causem diretamente a invocação do código arbitrário nelas contido. Talvez seu programa defina variáveis de ambiente a partir de entradas não confiáveis do usuário. Seria altamente inesperado que essas variáveis de ambiente pudessem ser manipuladas de forma que o usuário pudesse executar comandos arbitrários sem a sua intenção explícita de fazê-lo usando essa variável de ambiente por um motivo declarado no código.
Aqui está um exemplo de ataque viável. Você executa um servidor Web que executa um shell vulnerável, em algum lugar, como parte de sua vida útil. Esse servidor da Web passa variáveis de ambiente para um script bash, por exemplo, se você estiver usando CGI, informações sobre a solicitação HTTP geralmente são incluídas como variáveis de ambiente do servidor da Web. Por exemplo,
HTTP_USER_AGENT
pode estar definido para o conteúdo do seu agente de usuário. Isso significa que se você falsificar seu agente de usuário para algo como '() {:; }; echo foo ', quando esse script shell for executado,echo foo
será executado. Novamente,echo foo
poderia ser qualquer coisa, maliciosa ou não.fonte
export bar='() { echo "bar" ; }'; zsh -c bar
e é exibido embar
vez dezsh:1: command not found: bar
? Tem certeza de que não está confundindo o shell que está invocando com o shell que está usando para configurar o teste?Isso pode ajudar a demonstrar ainda mais o que está acontecendo:
Se você estiver executando um shell vulnerável, quando iniciar um novo subshell (aqui, simplesmente usando a instrução bash), verá que o código arbitrário (
echo "pwned"
) é imediatamente executado como parte de sua iniciação. Aparentemente, o shell vê que a variável de ambiente (fictícia) contém uma definição de função e avalia a definição para definir essa função em seu ambiente (observe que não está executando a função: isso imprimiria 'oi').Infelizmente, ele não apenas avalia a definição da função, mas também o texto inteiro do valor da variável de ambiente, incluindo as declarações possivelmente maliciosas que seguem a definição da função. Observe que, sem a definição inicial da função, a variável de ambiente não seria avaliada, seria apenas adicionada ao ambiente como uma sequência de texto. Como Chris Down apontou, este é um mecanismo específico para implementar a importação de funções shell exportadas.
Podemos ver a função que foi definida no novo shell (e que foi marcada como exportada para lá) e podemos executá-la. Além disso, o manequim não foi importado como uma variável de texto:
Nem a criação dessa função, nem qualquer coisa que ela faria se fosse executada, não faz parte da exploração - é apenas o veículo pelo qual a exploração é executada. O ponto é que, se um invasor puder fornecer código malicioso, precedido por uma definição de função mínima e sem importância, em uma sequência de texto inserida em uma variável de ambiente exportada, ela será executada quando um subshell for iniciado, o que é um evento comum. em muitos scripts. Além disso, será executado com os privilégios do script.
fonte
export
comando enquanto os outros tinhamenv
? Eu estava pensando que estavaenv
sendo usado para definir as variáveis ambientais que seriam chamadas quando outro shell bash for lançado. então como é este trabalho comexport
env
eexport
exportar definições de ambiente para que estejam disponíveis em um subshell. O problema está na maneira como essas definições exportadas são importadas para o ambiente de um subshell e, especificamente, no mecanismo que importa definições de funções.env
executa um comando com algumas opções e variáveis de ambiente definidas. Observe que, nos exemplos de perguntas originais,env
definex
como uma sequência e chamabash -c
com um comando para executar. Se você o fizerenv x='foo' vim
, o Vim será iniciado e, aí, você poderá chamar seu shell / ambiente contendo!echo $x
e ele será impressofoo
, mas se você sair eecho $x
ele não será definido, pois só existia enquanto o vim estava em execução. através doenv
comando Emexport
vez disso, o comando define valores persistentes no ambiente atual para que um subshell executado posteriormente os utilize.Eu escrevi isso como uma reformulação em estilo tutorial da excelente resposta de Chris Down acima.
No bash, você pode ter variáveis de shell como esta
Por padrão, essas variáveis não são herdadas pelos processos filhos.
Mas se você os marcar para exportação, o bash definirá um sinalizador que significa que eles entrarão no ambiente de subprocessos (embora o
envp
parâmetro não seja muito visto, omain
programa C tem três parâmetros:main(int argc, char *argv[], char *envp[])
onde a última matriz de ponteiros é uma matriz de variáveis shell com suas definições).Então, vamos exportar da
t
seguinte maneira:Considerando que acima
t
foi indefinido no subshell, agora aparece após a exportação (useexport -n t
se você deseja parar de exportar).Mas as funções no bash são um animal diferente. Você os declara assim:
E agora você pode simplesmente chamar a função chamando-a como se fosse outro comando shell:
Mais uma vez, se você gerar um subshell, nossa função não será exportada:
Podemos exportar uma função com
export -f
:Aqui está a parte complicada: uma função exportada como
fn
é convertida em uma variável de ambiente, assim como nossa exportação da variável shellt
estava acima. Isso não acontece quandofn
era uma variável local, mas após a exportação podemos vê-la como uma variável do shell. No entanto, você também pode ter uma variável de shell regular (ou seja, sem função) com o mesmo nome. bash distingue com base no conteúdo da variável:Agora podemos usar
env
para mostrar todas as variáveis de shell marcadas para exportaçãofn
e a função regular e a funçãofn
aparecem:Um sub-shell ingerirá as duas definições: uma como variável regular e outra como função:
Você pode definir
fn
como fizemos acima, ou diretamente como uma atribuição de variável regular:Observe que isso é algo altamente incomum a se fazer! Normalmente, definiríamos a função
fn
como fizemos acima com afn() {...}
sintaxe. Mas como o bash o exporta para o ambiente, podemos "atalhos" diretamente para a definição regular acima. Observe que (ao contrário da sua intuição, talvez) isso não resulta em uma nova funçãofn
disponível no shell atual. Mas se você gerar uma concha ** sub **, ela será.Vamos cancelar a exportação da função
fn
e deixar o novo regularfn
(como mostrado acima) intacto.Agora, a função
fn
não é mais exportada, mas a variável regularfn
é, e ela contém() { echo "direct" ; }
.Agora, quando um subshell vê uma variável regular que começa com
()
ele, interpreta o restante como uma definição de função. Mas isso é apenas quando um novo shell começa. Como vimos acima, apenas definir uma variável de shell comum começando com()
não faz com que ela se comporte como uma função. Você precisa iniciar um subshell.E agora o bug "shellshock":
Como acabamos de ver, quando um novo shell ingere a definição de uma variável regular começando com
()
ele, interpreta-o como uma função. No entanto, se houver mais dados após a chave de fechamento que define a função, ele executará o que estiver lá também.Estes são os requisitos, mais uma vez:
Nesse caso, um bash vulnerável executará os últimos comandos.
Exemplo:
A variável exportada regular
ex
foi passada para o subshell, que foi interpretado como uma função,ex
mas os comandos à direita foram executados (this is bad
) à medida que o subshell era gerado.Explicando o teste de uma linha liso
Uma linha popular para testar a vulnerabilidade do Shellshock é a citada na pergunta da @ jippie:
Aqui está um detalhamento: primeiro, o
:
in bash é apenas uma abreviação detrue
.true
e:
ambos avaliam como (você adivinhou) verdade, no bash:Segundo, o
env
comando (também incorporado ao bash) imprime as variáveis de ambiente (como vimos acima), mas também pode ser usado para executar um único comando com uma variável (ou variáveis) exportada dada a esse comando ebash -c
executa um único comando a partir de seu linha de comando:Então, costurando todas essas coisas juntas, podemos executar o bash como um comando, dar a ele alguma coisa fictícia para fazer (como
bash -c echo this is a test
) e exportar uma variável que começa com,()
para que o subshell a interprete como uma função. Se o shellshock estiver presente, ele também executará imediatamente qualquer comando à direita no subshell. Como a função que passamos é irrelevante para nós (mas devemos analisar!), Usamos a menor função válida imaginável:A função
f
aqui apenas executa o:
comando, que retorna true e sai. Agora acrescente a isso algum comando "maligno" e exporte uma variável regular para um subshell e você ganha. Aqui está o one-liner novamente:Portanto,
x
é exportado como uma variável regular com uma função válida simples, comecho vulnerable
aderência até o final. Isso é passado para o bash, e o bash interpretax
como uma função (da qual não nos importamos) e, em seguida, talvez execute oecho vulnerable
shellshock se estiver presente.Poderíamos encurtar um pouco a linha removendo a
this is a test
mensagem:Isso não se preocupa,
this is a test
mas executa o:
comando silencioso mais uma vez. (Se você deixar de fora-c :
, sente-se no subshell e precisará sair manualmente.) Talvez a versão mais amigável seja a seguinte:fonte
{ :;};
realmente diz. Essa seria uma boa adição à sua resposta na minha opinião. Pode explicar como você obtém do seu exemplo o comando original na pergunta?Se você pode alimentar variáveis de ambiente arbitrárias em um programa, pode fazer com que ele faça praticamente tudo, carregando as bibliotecas de sua escolha. Na maioria dos casos, isso não é considerado uma vulnerabilidade no programa que recebe essas variáveis de ambiente, mas no mecanismo pelo qual um estranho pode alimentar variáveis de ambiente arbitrárias.
No entanto, o CVE-2014-6271 é diferente.
Não há nada errado em ter dados não confiáveis em uma variável de ambiente. Basta garantir que ele não seja colocado em nenhuma daquelas variáveis de ambiente que podem modificar o comportamento do programa. Em outras palavras, para uma chamada específica, você pode criar uma lista de permissões de nomes de variáveis de ambiente, que podem ser especificadas diretamente por alguém de fora.
Um exemplo que foi apresentado no contexto do CVE-2014-6271 são os scripts usados para analisar arquivos de log. Esses podem ter uma necessidade muito legítima de transmitir dados não confiáveis em variáveis de ambiente. Obviamente, o nome para essa variável de ambiente é escolhido de forma que não tenha efeitos adversos.
Mas aqui está o que há de ruim nessa vulnerabilidade específica do bash. Pode ser explorado através de qualquer nome de variável. Se você criar uma variável de ambiente chamada
GET_REQUEST_TO_BE_PROCESSED_BY_MY_SCRIPT
, não esperaria que nenhum outro programa, além do seu próprio script, interpretasse o conteúdo dessa variável de ambiente. Mas, ao explorar esse bug do bash, todas as variáveis de ambiente se tornam um vetor de ataque.Observe que isso não significa que os nomes das variáveis de ambiente sejam secretos. O conhecimento dos nomes das variáveis de ambiente envolvidas não facilita o ataque.
Se
program1
chamadasprogram2
que, por sua vezprogram3
, chamam ,program1
podem passar dados paraprogram3
através de variáveis de ambiente. Cada programa possui uma lista específica de variáveis de ambiente que define e uma lista específica em que atua. Se você escolher um nome não reconhecidoprogram2
, poderá passar dados deprogram1
paraprogram3
sem se preocupar com issoprogram2
.Um invasor que conhece os nomes exatos das variáveis exportadas
program1
e os nomes das variáveis interpretadas porprogram2
não pode explorar esse conhecimento para modificar o comportamento do 'programa2' se não houver sobreposição entre o conjunto de nomes.Mas isso quebrou se
program2
fosse umbash
script, porque devido a esse errobash
interpretaria todas as variáveis de ambiente como código.fonte
É explicado no artigo que você vinculou ...
O que significa que o bash chamado com
-c "echo this is a test"
executa o código entre aspas simples quando é chamado.Significa que o exemplo de código que você postou explora o fato de que o bash invocado não para de avaliar essa sequência após executar a atribuição. Uma atribuição de função neste caso.
A coisa realmente especial sobre o trecho de código que você postou, como eu o entendo, é que, ao colocar uma definição de função antes do código que queremos executar, alguns mecanismos de segurança podem ser contornados.
fonte