Como fazer bot de rede em shell script

Alto lá. O assunto não é prática cracker, mas sim automação de serviços que tenham um padrão, mas dependem de interação humana. Veremos como fazer um bot de rede bem simples e rápido, utilizando uma ferramenta auxiliar, da qual já fiz bot para backup de switches na Catho e recuperação de dispositivos no BGP de uma empresa de telecom na Colômbia, a Edatel. Claro que usei em outros lugares, mas essas foram as tarefas mais notórias.

O que é um bot de rede

Se procurar no Google, só vai encontrar ataques de “botnets” (usados para ataques DDoS, disseminação de malware etc), infelizmente. Mas os bots são ferramentas essenciais para a administração de redes para automatizar tarefas como a citada, backup de switches. Isso porque é necessário paginar, ler toda a saída e armazenar em um arquivo; ou dependendo do switch, é necessário baixar um arquivo de configuração, cujo arquivo só se obtém acesso após efetuar login com usuário e senha. Esses serviços que são rotina padronizada podem ser executadas por bots, inclusive em redes IoT, para interagir externa ou internamente ao sistema. O Raspberry é um bom exemplo para uso.

Caso de uso

Se desejar, nesse outro artigo discorro sobre o backup de switches que fiz utilizando Python como linguagem. Dessa vez optei por shell script, utilizando o mesmo recurso como estrela: o expect!

expect

O expect é um programa (ou biblioteca, se usado em Python) disponível no repositório de pacotes do Linux. No caso, para qualquer Linux baseado em Debian (Ubuntu, Raspbian, Devuan etc) utiliza-se o gerenciador de pacotes apt:

sudo su
apt-get update
apt-get install expect

Como usar o expect

Não pretendo entrar a fundo no assunto por uma simples razão: não quero explicar expressões regulares, que são diferentes em C-Shell, Perl, Qt, sed etc.

Um exemplo básico que podemos rodar dentro de um script só para teste:

#!/usr/bin/expect -f
#  File: <name>
#  Author: Djames Suhanko
#  Created: qua 19 ago 2020 18:07:15
#  Last Update: qua 19 ago 2020 18:07:15
#  Notes:
puts  "Oi. Qual seu nome?"
expect {
    "a\n" {puts "E ai gata?"; exit}
    "o\n" {puts "E ai, fera?"; exit}
}
expect eof

Depois dá-se um bit de execução e:

bot de rede

Claro que tem nomes terminados com “s” (Djames), com “m”, “n”, “z” etc. Mas esse é um teste de duas condições, na qual o expect toma uma decisão diferente, conforme a entrada.

Elaborando um pouco mais, por exemplo, fazer uma conexão ssh automaticamente.

#!/usr/bin/expect
    set user [lindex $argv 1]
    set ip [lindex $argv 0]
    set password [lindex $argv 2]
    spawn ssh $user@$ip
    expect "password"
    send "$password\r"
    expect "$ "
    send "clear\r"
    interact

Nesse caso, o script recebe o parâmetro <host> <user> <password>, então após fazer o login ele limpa a tela e deixa o usuário interagir. Poderia ser algo automático, incluindo as credenciais no script, removendo a interação e executando os comandos desejados remotamente. Por exemplo, um script que conecta a um servidor de base de dados ou web, para reiniciar o serviço quando se tornar indisponível.

Existem outras possibilidades para se fazer conexão automática por ssh, como o programa sshpass:

sshpass -ptest1324 ssh user@192.168.1.200 ls -l /tmp

Mas é um bom exemplo do que pode ser feito.

Uma simples listagem em um servidor ftp:

#!/usr/bin/expect

spawn ftp ftp.servidor.com.br
set timeout -1
expect "Name"
send -- "usuario\r"
expect "Password"
send -- "senha\r"
expect "to transfer files."
send -- "ls\r"
 
expect eof

Como criar uma rotina com expect?

Mas como criar sua própria rotina?

Quando usamos a chave expect dentro do script, estamos esperando ler algum padrão. Repare na conexão SSH que o script espera “$ ” antes de mandar o clear. Não importa o fluxo dos dados, apenas quando chegar ao fim, casando com o padrão, o script envia um comando clear. Também não precisa obedecer um padrão completo, exceto desejado. No primeiro exemplo, defini o gênero pela letra final do nome; apenas “o” e “a”. Portanto, uma mulher pode ser “Matilda”, “Flávia”, “Maria” ou qualquer outro, desde que termine com “a”.  Se for “Matilde”, “Flaviane”, “Elainy” ou qualquer outro fora do padrão, não faz nada.

Normalmente esperamos pela PS1 de qualquer terminal. No Linux normalmente é “$” para usuário comum e “#” para root. Se estiver fazendo um script que se loga como root para, por exemplo, reiniciar um serviço, Os últimos 2 bytes serão o suficiente para saber se o login ocorreu normalmente.

Para criar uma rotina, não é necessário ficar experimentando passo-a-passo. Atualmente dá pra fazer com o autoexpect, que vai reproduzir o que for digitado e gravará tudo em um script ao final. Depois é só implementá-lo junto às demais funções do script principal – mas cuidado para não errar, porque os erros também serão reproduzidos. Eu usei esse bot de rede para criar um script que se conectasse ao github, autenticasse e fizesse o clone do repositório. A execução dos demais passos (que interagem com o conteúdo clonado) foram implementados no script principal.

O código do script gerado pelo autoexpect foi esse:

#!/usr/bin/expect -f

set force_conservative 0  ;# set to 1 to force conservative mode even if
        ;# script wasn't run conservatively originally
if {$force_conservative} {
  set send_slow {1 .1}
  proc send {ignore arg} {
    sleep .1
    exp_send -s -- $arg
  }
}

#
# 2) differing output - Some programs produce different output each time
# they run.  The "date" command is an obvious example.  Another is
# ftp, if it produces throughput statistics at the end of a file
# transfer.  If this causes a problem, delete these patterns or replace
# them with wildcards.  An alternative is to use the -p flag (for
# "prompt") which makes Expect only look for the last line of output
# (i.e., the prompt).  The -P flag allows you to define a character to
# toggle this mode off and on.
#
# Read the man page for more info.
#
# -Don


set timeout 10
spawn git clone https://github.com/REPOSITORIO_SECRETO/SEGREDO
match_max 100000
expect -exact "Cloning into 'REPOSITORIO_SECRETO'...\r
Username for 'https://github.com': "
send -- "USUARIO_SECRETO\r"
expect -exact "ESPERADO_SECRETO\r
Password for 'https://REPOSITORIO_SECRETO@github.com': "
send -- "SENHA_SECRETA\r"
expect eof

Não tive que escrever 1 linha sequer. Bem, para publicar aqui tive que substituir os dados reais das últimas linhas.

Também fiz um script usando o pexpect com Python, para fazer conexão ssh sem troca de chaves.

No artigo anterior mostrei como criar certificado para usar uma conexão SSL no broker MQTT, cujo processo pode ser automatizado com expect para a geração do próximo certificado. Aproveite!

 

Revisão: Ricardo Amaral de Andrade

Djames Suhanko

Djames Suhanko é Perito Forense Digital. Já atuou com deployer em sistemas de missão critica em diversos países pelo mundão. Programador Shell, Python, C, C++ e Qt, tendo contato com embarcados ( ora profissionalmente, ora por lazer ) desde 2009.