Domando o MIME
Meu amigo Ayr Müller Gonçalves compartilhou comigo recentemente um vídeo do YouTube mostrando como explorar upload de arquivos para “instalar” um script malicioso em um servidor Apache.
O vídeo em questão (recomendo assistir)demonstra o upload de um arquivo de “dupla extensão” (no caso algo como foo.php.jpg).
O vídeo, embora sucinto e informativo comete dois equívocos:
- Associa a linguagem PHP com o problema em si, coisa comum demais em um mercado extremamente preconceituoso com a linguagem. Na realidade o problema nada tem a ver com PHP em si, mas com um módulo do Apache, o mod-mime. Isso significa que potencialmente qualquer linguagem web é vulnerável a esse exploit, já que o problema reside no módulo do servidor web em si (seguindo o demo do vídeo deve funcionar com foo.js.jpg, foo.py.jpg, foo.rb.jpg, etc…).
- O demo do vídeo é realizado com a versão 2.2 do Apache, que atualmente possui a versão 2.4 como a mais recente. Tentei replicar o demo com a versão 2.4, mas não foi possível, o Apache segue tratando o arquivo como imagem, independente da dupla extensão.
De qualquer maneira, apesar destes equívocos, o problema é sério, pois o mod-mime comporta-se desta maneira propositalmente — o que significa que este comportamento não pode ser classificado como vulnerabilidade ou mesmo bug. Como diz a boa e velha piada, não é um bug, é uma feature.
A boa notícia é que a solução é, na realidade, simples, pois se encontra na própria documentação do mod-mime (Ah, crianças, o bom e velho RTFM, sabem?): Podemos orientar o mod-mime (através até mesmo de um .htaccess) a trabalhar em cima da última extensão apenas, especificando o tipo de arquivo a ser trabalhado. Aqui, um exemplo de uso com arquivos de imagem (como o que foi usado no demo do vídeo):
<FilesMatch “[^.]+\.(jpg|gif|png)$”>
SetHandler default-handler
</FilesMatch>
O exemplo acima faz com que apenas as extensões finais (neste caso jpg, gif ou png) sejam tratadas, definindo o handler default do Apache para estas. O handler default basicamente transmite o conteúdo para o navegador e, embora eu não tenha conseguido reproduzir o bug em primeiro lugar, esta solução é, a princípio, sólida (caso alguém consiga encontrar algum problema por favor me avise que atualizarei este post).
Agora, o que muitos podem se perguntar é “Não dá pra facilitar e aplicar essa solução para qualquer extensão? Algo como substituir o (jpg|gif|png) por algo como .{3}? A resposta é um sonoro e retumbante NÃO!
O motivo pelo qual uma solução “catch-all” seria desastrosa nesse caso é justamente a questão do comportamento do handler default: se você aplicar um “catch-all” arquivos .php (ou de qualquer extensão/linguagem) serão abertos e seu código fonte transmitido diretamente para o browser, tornando o código visível, portanto tome muito cuidado ao aplicar este tipo de solução.