Introduction
A few months ago, I began seeing our Apache log files fill up with entries, stating: "Failed password for illegal user [username]". Being somewhat alarmed about this I decided to search the Internet to find out if others were experiencing these or similar attacks and to hopefully find a solution.
I did find a lot of information but only a few script-based solutions. Most of the cures included: 1) Moving the ssh port to another number — not much better; 2) Allowing only specific IP addresses — not workable for a road-warrior; 3) Systematically blocking each offending IP address (imagine the eventual buildup). None of these solutions seemed... well... elegant.
What I wanted was a way to stop the attacks altogether, yet allow ssh access from anywhere. In addition, I wanted to avoid using an approach that was so complicated it could lead to more pain than it was worth.
My requirements looked something like this:
Keep port 22 closed until needed
Provide a simple way to open and close port 22 from any remote location
Ensure the method used is reasonably difficult for attackers to discover
Use an "elegant" method (i.e. not a lot of software)
The solution should support simple port knocking using, for example, the following shell prompt activity:
$ ssh username@hostname # No response (Ctrl-C to exit)
^C
$ nmap -P0 --host_timeout 201 -p <port1> hostname &> /dev/null
$ nmap -P0 --host_timeout 201 -p <port2> hostname &> /dev/null
$ history -r # Clear knocking history
$ ssh username@hostname # Now logins are allowed
username@hostname's password:
Note: The ports used to open port 22 (<port1> and <port2>) should also appear closed, say, to a port scanner.
Proposed Solution
The 'recent' module in iptables is designed to detect malicious access attempts and then help block or at least honeypot the potential intruder with delays. I've sort of turned this module on its head and, instead, used it to let users in.
The following represents the contents of an iptables.rules file. The highlighted text outlines the changes needed to support our port knocking scheme.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:TRAFFIC - [0:0]
:SSH-INPUT - [0:0]
-A INPUT -j TRAFFIC
-A FORWARD -j TRAFFIC
# Accepted
-A TRAFFIC -i lo -j ACCEPT
-A TRAFFIC -s 192.168.0.0/24 -j ACCEPT
-A TRAFFIC -p icmp --icmp-type any -j ACCEPT
-A TRAFFIC -m state --state ESTABLISHED,RELATED -j ACCEPT
-A TRAFFIC -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A TRAFFIC -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT
# Port Knocking -- use either port 1234 or port 5678 out of sequence to close port 22
-A TRAFFIC -m state --state NEW -m tcp -p tcp --dport 22 -m recent --rcheck --name SSH1 -j ACCEPT
-A TRAFFIC -m state --state NEW -m tcp -p tcp -m recent --name SSH1 --remove -j DROP
-A TRAFFIC -m state --state NEW -m tcp -p tcp --dport 5678 -m recent --rcheck --name SSH0 -j SSH-INPUT
-A TRAFFIC -m state --state NEW -m tcp -p tcp -m recent --name SSH0 --remove -j DROP
-A TRAFFIC -m state --state NEW -m tcp -p tcp --dport 1234 -m recent --name SSH0 --set -j DROP
-A SSH-INPUT -m recent --name SSH1 --set -j DROP
# Others
-A TRAFFIC -j DROP
COMMIT
Note: The <port1> and <port2> ports used in the above example are ports 1234 and 5678, respectively. For security purposes, please use a unique set of ports.
Knock Yourself... In
Now we can use a simple shell script (knock.sh) to do the knocking:
#!/bin/bash
# $1 host name or IP address
nmap -P0 --host_timeout 201 -p $2 $1 &> /dev/null
nmap -P0 --host_timeout 201 -p $3 $1 &> /dev/null
Here's how the script would work:
$ ssh username@hostname # No response (Ctrl-C to exit)
^C
$ sh knock.sh 1234 5678 hostname; history -r
$ ssh username@hostname # Now logins are allowed
username@hostname's password:
Conclusion
What's really convenient about this method is when you're at a trusted location (e.g. in the office) and you unlock a target site (e.g. a home server) you need only knock once; The port will stay open for that specific source address indefinitely — or until you deliberately close the port or iptables is restarted. If you attempt to use the port from another location (e.g. a client's office) it will appear closed — until you knock.
You can close port 22 by knocking out of sequence from another shell and you won't lose your established ssh connection — like closing the door behind you.
What I find most elegant about this approach is that you don't have to fill up your iptables with dozens of DROP entries in order to block the world of would-be attackers. It's also easy to use three or more knocks if that suits you.
Sample Log
The following is a sample from a log file (details obscured):
Jan 7 09:58:47 hostname sshd[24729]: Illegal user test from [IP_ADDRESS_A]
Jan 7 09:58:50 hostname sshd[24729]: Failed password for illegal user test from [IP_ADDRESS_A] port 51250 ssh2
Jan 7 09:58:52 hostname sshd[24731]: Illegal user guest from [IP_ADDRESS_A]
Jan 7 09:58:54 hostname sshd[24731]: Failed password for illegal user guest from [IP_ADDRESS_A] port 51396 ssh2
Jan 7 09:58:56 hostname sshd[24733]: Illegal user admin from [IP_ADDRESS_A]
Jan 7 09:58:58 hostname sshd[24733]: Failed password for illegal user admin from [IP_ADDRESS_A] port 51546 ssh2
Jan 7 09:59:00 hostname sshd[24735]: Illegal user admin from [IP_ADDRESS_A]
Jan 7 09:59:03 hostname sshd[24735]: Failed password for illegal user admin from [IP_ADDRESS_A] port 51688 ssh2
Jan 7 09:59:04 hostname sshd[24737]: Illegal user user from [IP_ADDRESS_A]
Jan 7 09:59:07 hostname sshd[24737]: Failed password for illegal user user from [IP_ADDRESS_A] port 51828 ssh2
Jan 7 09:59:11 hostname sshd[24739]: Failed password for root from [IP_ADDRESS_A] port 51963 ssh2
Jan 7 09:59:15 hostname sshd[24741]: Failed password for root from [IP_ADDRESS_A] port 52114 ssh2
Jan 7 09:59:20 hostname sshd[24743]: Failed password for root from [IP_ADDRESS_A] port 52288 ssh2
Jan 7 09:59:22 hostname sshd[24745]: Illegal user test from [IP_ADDRESS_A]
Jan 7 09:59:24 hostname sshd[24745]: Failed password for illegal user test from [IP_ADDRESS_A] port 52419 ssh2
Jan 7 16:35:22 hostname sshd[25103]: Failed password for nobody from [IP_ADDRESS_B] port 53721 ssh2
Jan 7 16:35:25 hostname sshd[25105]: Illegal user patrick from [IP_ADDRESS_B]
Jan 7 16:35:28 hostname sshd[25105]: Failed password for illegal user patrick from [IP_ADDRESS_B] port 53832 ssh2
Jan 7 16:35:31 hostname sshd[25107]: Illegal user patrick from [IP_ADDRESS_B]
Jan 7 16:35:33 hostname sshd[25107]: Failed password for illegal user patrick from [IP_ADDRESS_B] port 53907 ssh2
Jan 7 16:35:39 hostname sshd[25109]: Failed password for root from [IP_ADDRESS_B] port 54003 ssh2
Jan 7 16:35:45 hostname sshd[25111]: Failed password for root from [IP_ADDRESS_B] port 54093 ssh2
Jan 7 16:35:50 hostname sshd[25113]: Failed password for root from [IP_ADDRESS_B] port 54181 ssh2
Jan 7 16:35:58 hostname sshd[25115]: Failed password for root from [IP_ADDRESS_B] port 54312 ssh2
Jan 7 16:36:04 hostname sshd[25117]: Failed password for root from [IP_ADDRESS_B] port 54395 ssh2
Jan 7 16:36:07 hostname sshd[25119]: Illegal user rolo from [IP_ADDRESS_B]
Jan 7 16:36:10 hostname sshd[25119]: Failed password for illegal user rolo from [IP_ADDRESS_B] port 54488 ssh2
Jan 7 16:36:14 hostname sshd[25121]: Illegal user iceuser from [IP_ADDRESS_B]
Jan 7 16:36:16 hostname sshd[25121]: Failed password for illegal user iceuser from [IP_ADDRESS_B] port 54577 ssh2
Jan 7 16:36:21 hostname sshd[25123]: Illegal user horde from [IP_ADDRESS_B]
Jan 7 16:36:23 hostname sshd[25123]: Failed password for illegal user horde from [IP_ADDRESS_B] port 54681 ssh2
Jan 7 16:36:26 hostname sshd[25125]: Illegal user cyrus from [IP_ADDRESS_B]
Jan 7 16:36:28 hostname sshd[25125]: Failed password for illegal user cyrus from [IP_ADDRESS_B] port 54786 ssh2
Jan 7 16:36:32 hostname sshd[25127]: Illegal user www from [IP_ADDRESS_B]
Jan 7 16:36:34 hostname sshd[25127]: Failed password for illegal user www from [IP_ADDRESS_B] port 54878 ssh2
Jan 7 16:36:37 hostname sshd[25129]: Illegal user wwwrun from [IP_ADDRESS_B]
Jan 7 16:36:40 hostname sshd[25129]: Failed password for illegal user wwwrun from [IP_ADDRESS_B] port 54966 ssh2
Jan 7 16:36:43 hostname sshd[25131]: Illegal user matt from [IP_ADDRESS_B]
Jan 7 16:36:46 hostname sshd[25131]: Failed password for illegal user matt from [IP_ADDRESS_B] port 55050 ssh2
Jan 7 16:36:50 hostname sshd[25133]: Illegal user test from [IP_ADDRESS_B]
Jan 7 16:36:53 hostname sshd[25133]: Failed password for illegal user test from [IP_ADDRESS_B] port 55152 ssh2
Jan 7 16:36:57 hostname sshd[25135]: Illegal user test from [IP_ADDRESS_B]
Jan 7 16:36:59 hostname sshd[25135]: Failed password for illegal user test from [IP_ADDRESS_B] port 55263 ssh2
Jan 7 16:37:02 hostname sshd[25137]: Illegal user test from [IP_ADDRESS_B]
Jan 7 16:37:04 hostname sshd[25137]: Failed password for illegal user test from [IP_ADDRESS_B] port 55366 ssh2
Jan 7 16:37:08 hostname sshd[25139]: Illegal user test from [IP_ADDRESS_B]
Jan 7 16:37:10 hostname sshd[25139]: Failed password for illegal user test from [IP_ADDRESS_B] port 55457 ssh2
Jan 7 16:37:13 hostname sshd[25141]: Illegal user www-data from [IP_ADDRESS_B]
Jan 7 16:37:16 hostname sshd[25141]: Failed password for illegal user www-data from [IP_ADDRESS_B] port 55548 ssh2
Jan 7 16:37:21 hostname sshd[25143]: Failed password for mysql from [IP_ADDRESS_B] port 55637 ssh2
Jan 7 16:37:26 hostname sshd[25145]: Failed password for operator from [IP_ADDRESS_B] port 55724 ssh2
Jan 7 16:37:33 hostname sshd[25147]: Failed password for adm from [IP_ADDRESS_B] port 55799 ssh2
Jan 7 16:37:42 hostname sshd[25149]: Failed password for apache from [IP_ADDRESS_B] port 55912 ssh2
Jan 7 16:37:52 hostname sshd[25151]: Illegal user irc from [IP_ADDRESS_B]
Jan 7 16:37:54 hostname sshd[25151]: Failed password for illegal user irc from [IP_ADDRESS_B] port 56036 ssh2
Disclaimer
If you use the above information for any purpose you do so at your own risk. This port-knocking method should be used exclusively for purposes of reducing log file waste.
Комментариев нет:
Отправить комментарий