Recuperar O Controle do Sistema Após um rm -rf /

Introdução

No reddit rolou uma discussão muito boa sobre como recuperar o sistema após um rm -rf / [1]. O assunto foi tão interessante que o OP resolveu criar uma postagem no blog dele [2]. A ideia é recuperar as funções básicas do sistema, como listar arquivos e mostrar o conteúdo deles, já que os comandos ls e cat foram deletados.

O conteúdo dessa postagem foi retirado totalmente de [1] e [2]. Aconselho a leitura para mais informações.

O problema

Suponha que você esteja em uma conexão ssh no servidor e alguém execute o seguinte comando:

# rm -rf --no-preserve-root /

quase todos os arquivos do servidor serão deletados, porém você ainda terá os arquivos dispositivos (/dev/*), os arquivos virtuais (/proc/*) e também, diga-se de passagem, a sua conexão ssh ainda estará de pé, pois o binário foi deletado mas o processo continua rodando.

Se você reiniciar o servidor, tudo estará perdido, mas se você detectou isso a tempo, como prosseguir para recuperar o controle do sistema?

Suas armas

Tudo que você dispõe é da sua conexão ssh e da sessão do bash atual. Isso significa que poderá executar somente os bult-ins do shell, como echo, read e printf.

Listando arquivos

Como listar arquivos se o /bin/ls foi apagado?

# ls
-bash: /bin/ls: No such file or directory

Ah! Podemos usar a expansão de glob:

# echo *
dev proc run sys

É fácil obter os arquivos de /proc, fazendo echo /proc/*. Uma alternativa mais elegante é usar o printf. Para facilitar, vamos colocá-lo dentro de uma função chamada ls:

# type ls
ls is aliased to `ls --color=auto'
# unalias ls
# ls() { printf '%s\n' ${1:+${1%/}/}*; }
# ls /dev/pts
/dev/pts/0
/dev/pts/3
/dev/pts/ptmx

Os primeiros dois comandos verificam se há um alias chamado ls, se sim, ele é desabilitado e uma função com nome ls é criada sem conflitos. O printf recebe um arquivo como parâmetro e mostra seu nome na tela, caso nada seja passado, o glob é aplicado e todos os arquivos são mostrados.

A partir de agora, você já consegue listar arquivos. Podemos fazer mais?

Simulando um cat

Através de um read, simulamos um cat + arquivo:

# cat() { while read; do echo $REPLY; done < "$1" ;}

Baixando programas

É possível conectar em algum servidor sem usar programas como wget, curl e nc? Claro que sim. Se seu bash dá suporte a sockets [4] então será fácil baixar arquivos. O interessante seria baixarmos todos os programas do sistema e assim não precisarmos simulá-los com built-ins do bash. O busybox é um canivete suíte para sistemas embarcados linux e vem acompanhado de diversos programas, como wget, curl, nc, dd, tar, etc.

Para baixar programas é necessário uma outra máquina, indicada por $, que será responsável pelo download do busybox e também por criar um servidor para que a máquina problemática consiga transferir o arquivo.

$ mkdir $(xxd -p -l 16 /dev/urandom)
$ cd $_
$ apt-get download busybox-static
$ dpkg -x *.deb .

Aqui criamos um diretório de nome aleatório, entramos nele com cd $_ e salvamos o binário do busybox dentro dele.

$ alias encode='{ tr -d \\n | sed "s#\\(..\\)#\\\\x\\1#g"; echo; }'
$ alias upload='{ xxd -p | encode | nc -q0 -l 5050; }'
$ upload < bin/busybox

Colocamos um servidor rodando na porta 5050 usando o netcat. O servidor é criado ao se usar o alias upload e os dados transferidos por ele estão na forma de texto, que vem da saída do xxd[5].

O alias encode transforma a saída hexadecimal do xxd em um formato hexadecimal no estilo printf de C, ou seja, o que era 41414141 virará \x41\x41\x41\x41. Isso é porque faremos o decode na máquina problemática usando o comando printf (lembre-se não há um xxd lá).

Agora na máquina problemática:

# cd /
# alias decode='while read -ru9 line; do printf "$line"; done'

O decode receberá os dados da conexão no formato \x41\x41\x41\x41 e criará o binário equivalente usando printf.

Fazemos, então, a transferência do busybox a partir da máquina problemática:

# alias download='( exec 9<>/dev/tcp/200.200.200.200/5050; decode )'
# download > busybox

O IP 200.200.200.200 é o da segunda máquina. O exec associará ao descritor 9 o socket da conexão no host alvo na porta 5050. O decode lerá justamente os dados desse socket (read -ru9) e usará o printf para criar o binário.

Agora instalamos o busybox e teremos acesso a todos os comandos do sistema de volta:

# mkdir .bin
# /busybox  --install -s .bin
bash: /busybox: Permission denied

Ah nããão!! O busybox foi criado sem o bit +x de execução, por isso não pode ser executado. O pior de tudo é que não temos o chmod para setar esse bit.

Setando +x

A solução para esse problema é bastante engenhosa. Iremos criar uma shared lib e carregá-la como built-in do bash. Primeiro, vamos para a segunda máquina:

$ cat > setx.c <<EOF
extern int chmod(const char *pathname, unsigned int mode);

int entry(void) {

        return !! chmod("busybox", 0700);
}
char *desc[] = {0};

struct quick_hack {

        char *name; int (*fn)(void); int on;
        char **long_doc, *short_doc, *other;

} setx_struct = { "setx", entry, 1, desc, "chmod 0700 busybox", 0 };
EOF
$ gcc -Wall -Wextra -pedantic -nostdlib -Os -fpic -shared setx.c -o setx
$ upload < setx

A biblioteca simplesmente chama a syscall chmod (e não o comando chmod) para setar o bit +x no busybox. Em seguida é feito o upload.

Para os curiosos, o formato para se criar um bult-in do bash segue um formato específico. Um exemplo pode ser visto em [3].

Na máquina problemática:

# download > setx
# enable -f ./setx setx
# setx

O enable -f faz load da lib para dentro do bash. Executamos o setx no mesmo diretório do busybox para setar o bit +x nele.

Agora basta instalar o busybox:

# /busybox mkdir /.bin
# /busybox --instal -s /.bin
# PATH=/.bin
# ls

Pronto!! Recuperamos todos os principais programas do sistema. Podemos agora utilizá-los para algum tipo de análise caso a deleção do diretório raiz tenha vindo de um ataque.

Conclusão

Em se tratando do linux, nada sempre está perdido, mesmo após um rm -rf no diretório raiz. Vimos que o conhecimento de bash e shell scripting foi bastante útil na recuperação dos comandos do sistema.

Referências

[1] rm -rf remains by lambdaops.com (Acessado em: Junho/2014)
http://www.reddit.com/r/linux/comments/27is0x/rm_rf_remains/

[2] rm -rf remains by lambdaops.com (Acessado em: Junho/2014)
http://lambdaops.com/rm-rf-remains

[3] bash plugins by Clifford Wolf (Acessado em: Junho/2014)
http://www.clifford.at/cfun/elfstuff/hellobash.c

[4] Sockets no Bash by daemonio (Acessado em: Junho/2014)
https://daemoniolabs.wordpress.com/2012/03/10/sockets-no-bash/

[5] Hex Dump com o xxd by daemonio (Acessado em: Junho/2014)
https://daemoniolabs.wordpress.com/2011/07/07/hex-dump-com-o-xxd/

Deixe um comentário