Knockd in Linux

Descrição

Knockd é um servidor port-knock. Ele escuta todo o tráfego em uma interface ethernet (ou PPP), procurando por “batidas” sequências de requisições. Um cliente faz esses requisições para o servidor enviando um pacote TCP (ou UDP) para uma porta no servidor. Esta porta não precisa estar aberta – já que knockd escuta o nível da camada de link, ele vê todo o tráfego mesmo que seja destinado a uma porta fechada. Quando o servidor detecta uma sequência específica de tentativas de acesso a porta, ele executa um comando definido em seu arquivo de configuração. Isso pode ser usado para abrir furos em um firewall para acesso rápido.

Em resumo voce envia uma sequencia de requisições TCP/UDP e essa combinação aciona um comando predeterminado.

Mais informações visite o site do projeto.

Configuração do servidor:

Instale das dependencias:

# yum install libpcap*

Baixe o rpm:

# wget http://li.nux.ro/download/nux/dextop/el7/x86_64//knock-server-0.5-7.el7.nux.x86_64.rpm

Instale:

# rpm -ivh knock-server-0.5-7.el6.nux.i686.rpm

Como eu logado via ssh vou criar uma regra pra bloquear outra porta ssh, por enquanto que configuro, então:

# echo "Port 2222" >> /etc/ssh/sshd_config && service sshd restart

Vamos ao arquivo de configuração:

O primeiro passo e escolher as portas das batidas, escolhi para a abertura: 6390 7832 6276 e para fechar a conexão: 5232 2984 6253. E editei o arquivo de configuração:

# cp /etc/knockd.conf{,.orig}
# echo > /etc/knockd.conf
# cat /etc/knockd.conf
[options]
        logfile = /var/log/knockd.log

[SSH]
        sequence = 6390 7832 6276 
        seq_timeout = 30
        tcpflags = syn
        Start_command = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT
        cmd_timout = 1800
        stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 2222 -j ACCEPT

Explicando o arquivo:

  • sequence:  Sao passados as portas para a conexão.
  • seq_timeout: Tempo para a sequencia ser completada, se o tempo acabar antes da sequencia ser completa o comando e descartado.
  • tcpflags: Quais flags serão usadas.
  • start_command: Comando que sera usado quando a sequencia for feita corretamente.
  • cmd_timeout: Determina os segundos que o knockd vai demorar para executar o stop_command
  • stop_command: Comando que será executado após o cmd_timeout.

Pode ser feitas varias sessões com vários outros protocolos, mas a minha necessidade e apenas o ssh.

Iniciei o serviço:

# service knockd start

Fechei a porta 2222, com o iptables:

# iptables -A INPUT -p tcp --dport 2222 -j REJECT

No cliente

Para fazer acesso existem vários utilitários, o desenvolvedor menciona alguns clients, porém prefiro fazer meu propio.

Usando os arquivos especiais de conexão do linux, a ideia e simples e foi explicada nesse post.

# echo > /dev/tcp/ip_do_servidor/porta

Usando essa logica, vamos testar a se a porta esta aberta:

# echo > /dev/tcp/192.168.0.21/2222 && echo "Porta aberta"

Sem resultado, logo a porta esta fechada. Para abrir usei:

# echo > /dev/tcp/192.168.0.21/6390
# echo > /dev/tcp/192.168.0.21/7832
# echo > /dev/tcp/192.168.0.21/6276
# echo > /dev/tcp/192.168.0.21/2222 && echo "Porta aberta"

O comando acima resultou na saida: Pora aberta

Então testei fechar:

# echo > /dev/tcp/192.168.0.21/5232 
# echo > /dev/tcp/192.168.0.21/2984 
# echo > /dev/tcp/192.168.0.21/6253
# echo > /dev/tcp/192.168.0.21/2222 && echo "Porta aberta"

A saída foi vazia, o que significa que a porta está fechada. Logo tendo como abrir e fechar a porta, comecei a montar o client em bash.

Script

Montei uma usagem básica, exibindo uma forma de executar o script e em baixo uma condição no qual, se um dos argumentos de 1 a 6 estejam vazios a função printUsage será chamada.

function printUsage() {
 echo ""
 echo "$(basename "$0") -s Server_Knockd -p PORTA1 PORTA2 PORTA2 -P Pora_ssh_diferente"
 echo "$(basename "$0") -s 192.168.0.222 -p 7000 8000 9000 -P 2222"
 echo ""
 echo "-s ou --host: Especifica o servidor alvo"
 echo "-p ou --port: Especifica as portas para batida (knock)"
 echo "-P ou --port-ssh: Especifica as porta padrao, se nada for passado sera usado as padrao (22)"
 echo ""
 exit
}
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ] || [ -z "$5" ] || [ -z "$6" ];then
 printUsage
fi

Criei uma repetição que obriga ao usuário a entrar com um numero IP após o argumento -s ou –host, uma sequencia de portas depois do  -p ou –port , um argumento opcional que o -P  ou –port-ssh que passa uma porta para testar a conexão ssh, caso não seja passado nada a porta padrão (22) é usada.

while true ;do
 case "$1" in
   -s | --host) ip="$2"; shift 2 ;;
   -p | --port) porta0="$2"
                porta1="$3"
                porta2="$4"
                shift 4 ;;

   -P | --port-ssh) portaSSH="$2"; shift 2 ;;
   -h | --help) printUsage ; exit ;;
   *) break ;;
 esac
done

Criei uma função que verifica se as portas são validas, testa se a porta está entre 1 e 65535:

function verificaPorta() {
 for port in "$porta0" "$porta1" "$porta2"; do
     if [ "$port" -lt 1 ] || [ "$port" -gt 65535 ];then
         echo "A porta deve ser um numero entre 1 e 65535"
         printUsage
     fi
 done
}

Para verificar a conexão após o knockd então usei o arquivo /dev/tcp do linux para testar.

function testaSSH() {
     timeout 1 bash -c "echo > /dev/tcp/$ip/$1"
}

A próxima função é o coração do script, a conexão que recebe o IP  como argumento, um laço que recebe passa uma porta de cada vez e espera dois segundos para passar a próxima porta.

function openClose() {
  for i in "$porta0" "$porta1" "$porta2"; do
     echo > /dev/tcp/"$ip"/"$i"
     sleep 2
  done
}

E finalmente criei o método main do script:

function main() {
  verificaPorta
  openClose
  test -z "$portaSSH" && testaSSH 22
  test -n "$portaSSH" && testaSSH "$portaSSH"
}

Onde o meu método chama a função que verifica se as portas estão entre 1 e 65535, depois faz a conexão com o servidor knockd, testa se a variável $portaSSH está vazia, se estiver passa a porta padrão se não passa a porta informada.

O script final ficou assim:

#! /bin/bash

 # Knock Open Close 0.1
 # @Author: Bruno Viana


function printUsage() {
 echo ""
 echo "$(basename "$0") -s Server_Knockd -p PORTA1 PORTA2 PORTA2 -P Pora_ssh_diferente"
 echo "$(basename "$0") -s 192.168.0.222 -p 7000 8000 9000 -P 2222"
 echo ""
 echo "-s ou --host: Especifica o servidor alvo"
 echo "-p ou --port: Especifica as portas para batida (knock)"
 echo "-P ou --port-ssh: Especifica as porta padrao, se nada for passado sera usado as padrao (22)"
 echo ""
 exit
}
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] || [ -z "$4" ] || [ -z "$5" ] || [ -z "$6" ];then
 printUsage
fi
function openClose() {
 for i in "$porta0" "$porta1" "$porta2"; do
    echo > /dev/tcp/"$ip"/"$i"
    sleep 2
 done
}
function testaSSH() {
  timeout 1 bash -c "echo > /dev/tcp/$ip/$1"
}

function verificaPorta() {
 for port in "$porta0" "$porta1" "$porta2"; do
    if [ "$port" -lt 1 ] || [ "$port" -gt 65535 ];then
       echo "A porta deve ser um numero entre 1 e 65535"
       printUsage
       exit
    fi
 done
}

while true ;do
 case "$1" in
      -s | --host) ip="$2"; shift 2 ;;
      -p | --port) porta0="$2"
                   porta1="$3"
                   porta2="$4"
                   shift 4 ;;

     -P | --port-ssh) portaSSH="$2"; shift 2 ;;
     -h | --help) printUsage ; exit ;;
     *) break ;;
 esac
done

function main() {
 verificaPorta
 openClose
 test -z "$portaSSH" && testaSSH 22
 test -n "$portaSSH" && testaSSH "$portaSSH"
}

main

# End of scrpit

PoC

Para teste tenho dois cliente um com o IP: 192.168.0.138, o outro com 192.168.0.205  e um servidor com o IP: 192.168.0.21. A ideia é abrir a porta somente para o primeiro cliente.

Então vamos escanear a porta 2222 do nosso servidor com o cliente 01:

[root@cliente01 ~]# nmap -p 2222 192.168.0.21

Starting Nmap 6.40 ( http://nmap.org ) at 2017-01-18 11:41 BRT
Nmap scan report for 192.168.0.21
Host is up (0.00045s latency).
PORT STATE SERVICE
2222/tcp filtered EtherNet/IP-1
MAC Address: CA:47:3B:65:11:F3 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 0.04 seconds

Agora com o cliente 02:

[root@cliente02 ~]# nmap -p 2222 192.168.0.21

Starting Nmap 7.40 ( https://nmap.org ) at 2017-01-18 14:00 Hora Padrão de Buenos Aires 
Nmap scan report for 192.168.0.21 
Host is up (0.0020s latency). 
PORT STATE SERVICE 
2222/tcp filtered EtherNetIP-1 
MAC Address: CA:47:3B:65:11:F3 (Unknown) 
 
Nmap done: 1 IP address (1 host up) scanned in 12.88 seconds

No cliente 01, vamos executar o nosso script.

[root@cliente01 scripts]# ./knockOC.sh -s 192.168.0.21 -p 6390 7832 6276
./knockOC.sh: connect: Conexão recusada
./knockOC.sh: line 23: /dev/tcp/192.168.0.21/6390: Conexão recusada
./knockOC.sh: connect: Conexão recusada
./knockOC.sh: line 23: /dev/tcp/192.168.0.21/7832: Conexão recusada
./knockOC.sh: connect: Conexão recusada
./knockOC.sh: line 23: /dev/tcp/192.168.0.21/6276: Conexão recusada

Podemos acompanhar as batidas com o comando, no servidor:

root@server05:~$ tail -f /var/log/knockd.log

[2017-01-18 12:00] waiting for child processes...
[2017-01-18 12:00] shutting down
[2017-01-18 12:00] starting up, listening on eth0
[2017-01-18 14:06] 192.168.0.138: SSH: Stage 1
[2017-01-18 14:06] 192.168.0.138: SSH: Stage 2
[2017-01-18 14:06] 192.168.0.138: SSH: Stage 3
[2017-01-18 14:06] 192.168.0.138: SSH: OPEN SESAME
[2017-01-18 14:06] SSH: running command: /sbin/iptables -I INPUT -s 192.168.0.138 -p tcp --dport 2222 -j ACCEPT

SSH: Stage 1: significa que a primeira porta foi batida com sucesso, Stage 2 a segunda e assim sucessivamente.

Se fizermos um scan novamente na porta 2222 a partir do primeiro cliente, teremos o seguinte resultado:

[root@cliente01 scripts]# nmap -p 2222 192.168.0.21

Starting Nmap 6.40 ( http://nmap.org ) at 2017-01-18 12:15 BRT
Nmap scan report for 192.168.0.21
Host is up (0.00044s latency).
PORT STATE SERVICE
2222/tcp open EtherNet/IP-1
MAC Address: CA:47:3B:65:11:F3 (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 0.04 seconds

No cliente 2, que não executou o script:

[root@cliente02 ~]# nmap -p 2222 192.168.0.21

Starting Nmap 7.40 ( https://nmap.org ) at 2017-01-18 14:21 BRT
Nmap scan report for 192.168.0.21 
Host is up (0.0020s latency). 
PORT STATE SERVICE 
2222/tcp filtered EtherNetIP-1 
MAC Address: CA:47:3B:65:11:F3 (Unknown) 
 
Nmap done: 1 IP address (1 host up) scanned in 12.25 seconds

A porta foi aberta apenas para o primeiro cliente. Agora podemos acessar via ssh normalmente.

Good Hacking !

Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s