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/