分类: LINUX
2006-05-22 11:15:58
Updated: 2006-03-20
Introduction
A few months ago, I began seeing our 'secure' 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,
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 I was experiencing from the original problem.
The solution should support simple port knocking, for example, using the following shell prompt activity:
$ ssh username@hostname # No response (Ctrl-C to exit)
^C
$ nmap -P0 --host_timeout 201 -phostname &> /dev/null
$ nmap -P0 --host_timeout 201 -phostname &> /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 (
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 people in.
The following represents the contents of an iptables.rules file. The highlighted text outlines the changes needed to support our style of port knocking.
*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
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 (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.
Also, if you feel the need for more security, you can close port 22 -- sort of like closing the door behind you -- by knocking out of sequence (say, from another shell) and you won't lose your established ssh connection.
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. In addition, it wouldn't be too difficult to add a third knock, just to keep attackers guessing.
Sample Log
The following is a representative sample from a 'secure' log file:
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
Security gained from using the above information cannot be guaranteed.
If you use the above information for any purpose, you do so at your own
risk.