Decred atrás de Portknocking

7 minutos de leitura

1. Introdução

Portknocking é uma técnica usada para ocultar a existência de um serviço sendo executado em um servidor. O portknocking (do inglês ‘bater na porta’) exige que uma combinação específica de portas receba tráfego para que a porta do serviço seja aberta.

O uso do portknocking torna virtualmente impossível que um atacante descubra o serviço executado naquela porta. Por exemplo, para um portknocking que requer que o usuário acerte 3 portas diferentes em 5 segundos, seria necessário testar 65535^3 portas.

ATENÇÃO! Se o seu acesso à Internet está sendo monitorado é possível que o censor perceba esse tipo de tráfego na rede. Uma solução parcial seria executar a abertura de portas a partir de outro endereço IP.

Para saber mais sobre o knockd, veja https://linux.die.net/man/1/knockd.

2. Instalação

Os passos a seguir foram executados em um Debian 9 64-bit.

a) Instale o knockd a partir do repositório padrão.

$ sudo apt-get install knockd

b) Edite o arquivo /etc/default/knockd.conf e configure o parâmetro START_KNOCKD=1 para que o serviço seja iniciado automaticamente na próxima inicialização do sistema operacional.

3. Modo simples

3.1. Configuração

O exemplo a seguir mostra a configuração do knockd feita no arquivo /etc/knockd.conf para o serviço sshd.

$ cat /etc/knockd.conf

[options]
    UseSyslog

[openSSH]
 sequence = 7532,2357,7235
 seq_timeout = 5
 command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -m comment --comment "Knockd: SSH" -j ACCEPT
 tcpflags = syn

[closeSSH]
 sequence = 7235,2357,7532
 seq_timeout = 5
 command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -m comment --comment "Knockd: SSH" -j ACCEPT
 tcpflags = syn

Se há alguma regra no iptables que permita ou proiba o tráfego que será controlado pelo Portknocking essa regra terá de ser excluída. Para bloquear todo o tráfego que não é explicitamente permitido use a policy da chain do iptables:

$ sudo iptables -P INPUT DROP

Se foi feita alguma modificação nas regras ou nas policies do iptables, o comando iptables-save deve ser usado para escrever as regras permanentemente.

Inicie o serviço manualmente.

$ sudo service knockd start

Sempre que as regras de portknocking forem alteradas será necessário reiniciar o serviço.

$ sudo service knockd restart

3.2. Execução

Crie no dispositivo cliente os seguintes scripts que serão usados para abrir as portas do serviços protegidos pelo Portknocking. Nos exemplos a seguir o telnet foi usado para fazer o knocking, mas o knockd vem com um aplicativo knock que permite a escolha da porta e do protocolo no formato porta:protocolo. O site do projeto contém clientes para outras plataformas.

$ cat dcrd_ssh_open.sh

telnet $DCRD_IP 7532
telnet $DCRD_IP 2357
telnet $DCRD_IP 7235
$ cat dcrd_ssh_close.sh

telnet $DCRD_IP 7235
telnet $DCRD_IP 2357
telnet $DCRD_IP 7532

Execute o script e ao término se conecte normalmente na porta que foi aberta.

$ ./dcrd_ssh_open.sh

4. Modo avançado: despistando o censor

Como foi explicado na seção 1, se o censor monitora o tráfego, uma opção é abrir a porta a partir de outro endereço IP. Nesse caso, o comando do iptables não poderá incluir o endereço de origem (-s %IP%) como restrição ao tráfego ou terá que manter um endereço IP fixo na configuração. Mas há alternativas mais práticas para tentar despistar o censor, como configurar outras sequências com portas comuns, por exemplo.

Os exemplos a seguir ocultam a porta TCP/22 do SSH, mas poderiam ser usados para ocultar a porta da VPN ou de conexão dos componentes do Decred.

  • Interface: a interface padrão onde o knockd espera pacotes é a “eth0”, mas pode ser alterada com essa diretiva.
  • seq_timeout: determina o tempo máximo de espera por todos os pacotes da sequência.
  • tcpflags: determina as flags que devem estar marcadas nos pacotes TCP. knockd irá ignorar pacotes que não tenham as flags selecionadas, ou seja, não invalidarão toda a sequência forçando o cliente a começar novamente. Múltiplas flags podem ser separadas com vírgulas (ex: tcpflags = syn,ack,urg) e podem ser explicitamente negadas com “!” (ex: tcpflags = syn,!ack).
  • command: o comando do iptables que será executado quando o cliente acertar a sequência correta de knocks.

Exemplo 1

$ cat /etc/knockd.conf

[options]
    UseSyslog
    Interface = ens33
    
[openSSH]
 sequence = 80,443,21
 seq_timeout = 29
 command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 --syn -m comment --comment "Knockd: SSH" -j ACCEPT
 tcpflags = syn

Exemplo 2

$ cat /etc/knockd.conf

[options]
    UseSyslog
    Interface = ens33
    
[openSSH]
 sequence = /etc/knockd_ssh_sequences
 seq_timeout = 29
 command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 --syn -m comment --comment "Knockd: SSH" -j ACCEPT
 tcpflags = syn

Exemplo 3

Nesse exemplo também são usadas as diretivas:

  • start_command: o comando do iptables que será executado quando o cliente acertar a sequência correta de knocks. A variável %IP% será substituída pelo IP endereço IP do knocker.
  • cmd_timeout: tempo de espera entre a execução de start_command e stop_command. É opcional e só é necessária se stop_command for usada.
  • stop_command: o comando do iptables que será executado quando os segundos de cmd_timeout acabarem. A variável %IP% será substituída pelo IP endereço IP do knocker. É opcional.
$ cat /etc/knockd.conf

[options]
    UseSyslog
    Interface = ens33
    
[opencloseSSH]
    one_time_sequences = /opt/knockd_ssh_sequences
    seq_timeout   = 29
    tcpflags      = syn
    start_command = /usr/sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 --syn -m comment --comment "Knockd: SSH" -j ACCEPT
    cmd_timeout   = 25
    stop_command  = /usr/sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 --syn -m comment --comment "Knockd: SSH" -j ACCEPT
    
[openclosedcrwallet]
    one_time_sequences = /opt/knockd_dcrwallet_sequences
    seq_timeout   = 29
    tcpflags      = syn
    start_command = /usr/sbin/iptables -A INPUT -s %IP% -p tcp --dport 9109 --syn -m comment --comment "Knockd: dcrwallet" -j ACCEPT
    cmd_timeout   = 25
    stop_command  = /usr/sbin/iptables -D INPUT -s %IP% -p tcp --dport 9109 --syn -m comment --comment "Knockd: dcrwallet" -j ACCEPT

O terceiro exemplo usa sequências de portas pré-definidas, o que evita que o censor perceba mais fácilmente o comportamento do censurado.

O arquivo /opt/knockd_ssh_sequences contém as sequências de portas que serão usadas. Cada vez que uma sequência for usada, será invalidada com um comentário (#) no início da linha. Esse comentário substituirá o primeiro caracter da linha, por isso deve-se deixar um espaço em branco no início de cada linha.

$ cat /opt/knockd_ssh_sequences

 80:tcp,443:tcp,53:udp
 21:tcp,123:udp,995:tcp
 53:udp,23:tcp,135:tcp

Exemplo 4: executar o knocking do ‘Exemplo 3’ com o aplicativo knock

Esse exemplo assume que o mesmo arquivo knockd_ssh_sequences existe também no cliente.

$ cat dcrd_open.sh

# Author: Marcelo Martins (stakey.club)
#! /bin/sh -

# Example: dcrd_open.sh [line number] [server IP address] [seq_file]

KNOCK_BIN=knock
SEQ_FILE="knockd_ssh_sequences"
SLEEP_MAX=14
SRV_IP="11.12.13.14"

if [ -z $1 ]; then
        echo "Line number must be provided."
        exit 1
fi
if [[ $(( $1 % 1 )) -eq $1 ]]; then
        echo "Argument \"$1\" is not a number."
        exit 1
fi
if [ ! -z $2 ]; then
        SRV_IP=$2
fi
if [ ! -z $3 ]; then
        SEQ_FILE=$3
fi
SEQ=`head -"$1" $SEQ_FILE | tail -1`
PORT1=`echo $SEQ | awk -F "," '{print $1}'`
PORT2=`echo $SEQ | awk -F "," '{print $2}'`
PORT3=`echo $SEQ | awk -F "," '{print $3}'`
if [[ -z $PORT1 || -z $PORT2 || -z $PORT3 ]]; then
        echo "Unable to extract port:protocol from file $SEQ_FILE, line was [$SEQ]."
        exit 1
fi
SLEEP1=$(( RANDOM % $SLEEP_MAX ))  # do not use RANDOM for simulations
SLEEP2=$(( RANDOM % $SLEEP_MAX ))  # or crypto, it's flawed

echo "Knocking on server's door... at $SRV_IP, ports $PORT1 $PORT2 $PORT3"
$KNOCK_BIN $SRV_IP $PORT1
echo "Sleeping for $SLEEP1 seconds."
sleep $SLEEP1
echo "Knock, knocking on server's dooor... at $SRV_IP, ports $PORT1 $PORT2 $PORT3"
$KNOCK_BIN $SRV_IP $PORT2
echo "Sleeping for $SLEEP2 seconds."
sleep $SLEEP2
echo "Knock, knock, knocking on server's dooooor... at $SRV_IP, ports $PORT1 $PORT2 $PORT3"
$KNOCK_BIN $SRV_IP $PORT3
echo "We're done knocking on server's door. The door must be open."

5. Filtros no firewall

Para evitar que atacantes tentem fazer um portscan ou acertar a combinação do Portknocking, é possível configurar regras no firewall para bloquear temporariamente endereços IP que demonstrarem esse comportamento.

a) Primeiro é criada a blacklist que manterá por 180 segundos, por exemplo, os endereços para ela enviados. Essa regra ignora (drop) todos os pacotes vindo de endereços que estão na lista.

$ sudo iptables -A INPUT -m recent --name blacklist_180 --rcheck --seconds 180 -m comment --comment "IP Blacklisted for 180 sec" -j DROP

b) Em seguida criamos a chain TCP_SYN, na primeira linha. A segunda linha direciona para a chain TCP_SYN todos os pacotes TCP com essa flag marcada. É na chain TCP_SYN que será verificado se, após 3 pacotes com a flag SYN marcada por segundo, continuamos a receber um número de pacotes superior ao limite (1/s), como mostra a terceira linha. Se isso acontecer, a chain segue o processamento e envia o endereço IP para a blacklist, por determinação da quarta linha. Se não acontecer, a terceira regra retorna o processamento para a chain INPUT.

$sudo iptables -N TCP_SYN
$sudo iptables -A INPUT -p tcp --syn -j TCP_SYN
$sudo iptables -A TCP_SYN -m limit --limit 1/s --limit-burst 3 -m comment --comment "Limit TCP SYN rate" -j RETURN
$ sudo iptables -A TCP_SYN -m recent --name blacklist_180 --set -m comment --comment "Blacklist source IP address" -j DROP