芝麻开门的秘密:Port Knocking

简介

寒潮来袭,不如学习 :)

端口碰撞的概念及作用[1]:

In  computer networking , port knocking is a method of externally opening  ports  on a firewall by generating a connection attempt on a set of prespecified closed ports. Once a correct sequence of connection attempts is received, the firewall rules are dynamically modified to allow the host which sent the connection attempts to connect over specific port(s).
The primary purpose of port knocking is to prevent an attacker from scanning a system for potentially exploitable services by doing a  port scan , because unless the attacker sends the correct knock sequence, the protected ports will appear closed.

端口碰撞的原理其实很简单,设想这样一个场景:目标主机T上运行着防火墙F和一个后台守护进程D。防火墙F配置了规则,禁止所有外界主机连接端口port-n;守护进程D配置了端口序列口令,如port-a、port-b、port-c,持续监视着本机网络连接状态。如果外部客户端C首先依次访问了主机T的端口port-a、port-b、port-c,则守护进程D自动向防火墙F中插入一条规则R,允许客户端C连接port-n(芝麻开门)。另外,也可以通过配置守护进程D,实现“如果外部客户端C依次访问了主机T的端口port-d、port-e、port-f,则删除规则R”的效果(芝麻关门)。

现实中,目标主机T通常是Linux,防火墙F通常是iptables,后台守护进程D通常是knockd,port-n通常是SSH的默认监听端口22。

环境搭建

我们来简单测试一下,参考Rapid7的博文[2],测试环境为Ubuntu 16.04。

首先做一些基本配置,禁止外部主机连接SSH端口10001:

# stop ubuntu firewall
ufw disable
# install iptables
apt-get install iptables iptables-persistent
# allow all firstly
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# reject all connections to port 10001
iptables -A INPUT -p tcp --dport 10001 -j REJECT
netfilter-persistent save
netfilter-persistent reload

此时使用Nmap去扫描,可以发现端口已经被过滤:

➜  ~ nmap -p 10001 192.168.1.100

Starting Nmap 7.01 ( https://nmap.org ) at 2021-01-07 19:31 CST
Nmap scan report for victim (192.168.1.100)
Host is up (0.00062s latency).
PORT      STATE    SERVICE
10001/tcp filtered scp-config
MAC Address: 00:50:56:82:86:B0 (VMware)

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

安装knockd:

apt-get install knockd -y

配置knockd启动参数,编辑/etc/default/knockd,其中网卡名一定要正确:

# PLEASE EDIT /etc/knockd.conf BEFORE ENABLING
START_KNOCKD=1

# command line options
KNOCKD_OPTS="-i eth0"

配置knockd动作,编辑/etc/knockd.conf

[options]
	UseSyslog

[openSSH]
	sequence    = 11003,11002,11001
	seq_timeout = 5
	command     = /sbin/iptables -I INPUT -s %IP% -p tcp --dport 10001 -j ACCEPT
	tcpflags    = syn

[closeSSH]
	sequence    = 11001,11002,11003
	seq_timeout = 5
	command     = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 10001 -j ACCEPT
	tcpflags    = syn

上述命令的意思是:如果外界客户端依次访问了11003、11002、11001端口,则允许其访问10001;如果它依次访问了11001、11002、11003端口,则删除前面的允许规则。

OK,启动knockd。在我的环境中,使用systemctl命令无法启动knockd,也没有任何日志报错,knockd的服务状态为active (exited),需要使用service命令启动才可以,具体原因尚未研究:

service knockd start

测试

客户端也安装knockd,然后执行:

knock 192.168.1.100 11003 11002 11001

/var/log/syslog报日志如下:

Jan  7 19:41:06 victim knockd: 192.168.1.101: openSSH: Stage 1
Jan  7 19:41:06 victim knockd: 192.168.1.101: openSSH: Stage 2
Jan  7 19:41:06 victim knockd: 192.168.1.101: openSSH: Stage 3
Jan  7 19:41:06 victim knockd: 192.168.1.101: openSSH: OPEN SESAME
Jan  7 19:41:06 victim knockd: openSSH: running command: /sbin/iptables -I INPUT -s 192.168.1.101 -p tcp --dport 10001 -j ACCEPT

然后再次用Nmap扫描:

➜  ~ nmap -p 10001 192.168.1.100

Starting Nmap 7.01 ( https://nmap.org ) at 2021-01-07 19:41 CST
Nmap scan report for victim (192.168.1.100)
Host is up (0.00066s latency).
PORT      STATE SERVICE
10001/tcp open  scp-config
MAC Address: 00:50:56:82:86:B0 (VMware)

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

OK,尝试一下“芝麻关门”:

knock 192.168.1.100 11001 11002 11003

日志如下:

Jan  7 19:42:26 victim knockd: 192.168.1.101: closeSSH: Stage 1
Jan  7 19:42:26 victim knockd: 192.168.1.101: closeSSH: Stage 2
Jan  7 19:42:26 victim knockd: 192.168.1.101: closeSSH: Stage 3
Jan  7 19:42:26 victim knockd: 192.168.1.101: closeSSH: OPEN SESAME
Jan  7 19:42:26 victim knockd: closeSSH: running command: /sbin/iptables -D INPUT -s 192.168.1.101 -p tcp --dport 10001 -j ACCEPT

总结

这个idea挺不错的,把端口访问序列当作口令了,如果访问序列复杂度上升,爆破的难度还是挺大的。

参考文献

  1. https://en.wikipedia.org/wiki/Port_knocking
  2. https://blog.rapid7.com/2017/10/04/how-to-secure-ssh-server-using-port-knocking-on-ubuntu-linux/
Per Aspera Ad Astra