Decred atrás de Portknocking
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 outros endereços 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 endereço IP do knocker.cmd_timeout
: tempo de espera entre a execução destart_command
estop_command
. É opcional e só é necessária sestop_command
for usada.stop_command
: o comando do iptables que será executado quando os segundos decmd_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