Monday, October 1, 2007

Properly securing SSH.

If you are like me you work in a corporate environment and SSH is needed not just by you but several other Administrators or Application Administrators so shutting down or changing the SSH port isn't applicable. You're in luck, after much research and a lot of brain storming I think I've came up with a very good result. If you've ever checked your system logs (/var/log/secure) you may have noticed copious amounts of SSH failed login attempts this may be why you're searching for new tactics to circumvent the SSH brute-force attempts.

The first thing we'll do is setup iptables rules, if you're running a Red Hat box; vi /etc/sysconfig/iptables and insert the following. Don't forget to add/remove the services you need to be opened.
# resides in /etc/sysconfig/iptables
# Written by Nate Dobbs for NS1 and NS2
# If tables need to be flushed execute
# /usr/sbin/iptables_flush.pl
# Firewall Rules
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
# Handle loopback addresses
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
# Disallow ICMP requests from the world
-A INPUT -p icmp -j DROP
# Allow ICMP pings to the world, drop all others
-A OUTPUT -p icmp -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow outbound packets if state related, and inbound if established
-A OUTPUT -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
-A INPUT -m state --state ESTABLISHED -j ACCEPT
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Drop stealth scans
-A INPUT -p tcp ! --syn -m state --state NEW -j DROP
-A INPUT -i eth0 -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -j DROP
-A INPUT -i eth0 -p tcp -m tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
-A INPUT -i eth0 -p tcp -m tcp --tcp-flags SYN,RST SYN,RST -j DROP
-A INPUT -i eth0 -p tcp -m tcp --tcp-flags FIN,RST FIN,RST -j DROP
-A INPUT -i eth0 -p tcp -m tcp --tcp-flags ACK,FIN FIN -j DROP
-A INPUT -i eth0 -p tcp -m tcp --tcp-flags ACK,URG URG -j DROP
# open ports for some services
# open ssh
-A INPUT -p tcp -i eth0 --dport 22 -j ACCEPT
-A INPUT -p udp -i eth0 --dport 22 -j ACCEPT
# open DNS
-A INPUT -p tcp -i eth0 --dport 53 -j ACCEPT
-A INPUT -p udp -i eth0 --dport 53 -j ACCEPT
# Open Webmin
-A INPUT -p tcp -i eth0 --dport 10000 -j ACCEPT
-A INPUT -p udp -i eth0 --dport 10000 -j ACCEPT
# Define policy - DROP
-P INPUT DROP
-P OUTPUT DROP
-P FORWARD DROP
COMMIT




You may have noticed the iptables flush script I have defined in the comment section of the code I will include this as well.
#!/usr/bin/perl -w
# This is a IPTABLES flushing script
# Written by Nate Dobbs
# Feel free to redistribute and or modify at-will
# Please give all props to original author/s!
# Declare Vars
print "Welcome to the IPTABLES-Flush script!\n"

$_ = ;
chomp $_;
$_ = "Y" if (length($_) == 0);

if ($_ =~ /[Yy]/) {
print "Starting the IPTABLES-FLUSHING process!\n";
}
else {
print "Aborting script..\n";
}


$iptables = "/sbin/iptables";

%iptables_hash = (

reset_policy => "
$IPTABLES -P INPUT ACCEPT;
$IPTABLES -P FORWARD ACCEPT;
$IPTABLES -P OUTPUT ACCEPT",
reset_policy_mangle => "
$IPTABLES -t mangle -P PREROUTING ACCEPT;
$IPTABLES -t mangle -P POSTROUTING ACCEPT;
$IPTABLES -t mangle -P INPUT ACCEPT;
$IPTABLES -t mangle -P OUTPUT ACCEPT;
$IPTABLES -t mangle -P FORWARD ACCEPT",
reset_policy_nat => "
$IPTABLES -F;
$IPTABLES -t nat -F;
$IPTABLES -t mangle -F",
reset_all_non_default_chains => "
$IPTABLES -X;
$IPTABLES -t nat -X;
$IPTABLES -t mangle -X"
);

print "Resetting policies\n\n";
system (%iptables_hash {'reset_policy'});
sleep 5
print "done!\n";

print "Resetting mangle policy's\n\n";
system (%iptables_hash {'reset_policy_mangle'});
sleep 5
print "done!\n";

print "Resetting NAT policy's\n";
system (%iptables_hash {'reset_policy_nat'});
sleep 5
print "done...\n";

print "And finally flushing all non-default chains\n";
system (%iptables_hash {'reset_all_non_default_chains'});
print "Script is completed\n";
exit


Now that we've got proper iptables rules it's time to edit /etc/hosts.deny; add the following.
ALL: ALL

Now edit /etc/hosts.allow and allow each IP or a entire subnet of allowed "trusted users" you will have to literally allow access to each service that is open with the iptables such as named, httpd etc. You can use ALL: ALL if you need unlimited access to a particular server. I would just recommend allowing access to your services such as ssh on a domain-trusted basis.
named: ALL
httpd: ALL
ssh: .yourdomain.com # the period in front of the domain is ESSENTIAL!

Don't forget to setup iptables for boot-time init
chkconfig --level 345 iptables on

Now restart your services
service iptables restart

iptables -L
Chain INPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere
DROP icmp -- anywhere anywhere
ACCEPT icmp -- anywhere anywhere state RELATED,ESTABLISHED
ACCEPT all -- anywhere anywhere state ESTABLISHED
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
DROP tcp -- anywhere anywhere tcp flags:!SYN,RST,ACK/SYN state NEW
DROP tcp -- anywhere anywhere tcp flags:FIN,SYN,RST,PSH,ACK,URG/NONE
DROP tcp -- anywhere anywhere tcp flags:FIN,SYN/FIN,SYN
DROP tcp -- anywhere anywhere tcp flags:SYN,RST/SYN,RST
DROP tcp -- anywhere anywhere tcp flags:FIN,RST/FIN,RST
DROP tcp -- anywhere anywhere tcp flags:FIN,ACK/FIN
DROP tcp -- anywhere anywhere tcp flags:ACK,URG/URG
ACCEPT tcp -- anywhere anywhere tcp dpt:ssh
ACCEPT udp -- anywhere anywhere udp dpt:ssh
ACCEPT tcp -- anywhere anywhere tcp dpt:domain
ACCEPT udp -- anywhere anywhere udp dpt:domain
ACCEPT tcp -- anywhere anywhere tcp dpt:10000
ACCEPT udp -- anywhere anywhere udp dpt:10000

Chain FORWARD (policy DROP)
target prot opt source destination

Chain OUTPUT (policy DROP)
target prot opt source destination
ACCEPT all -- anywhere anywhere
ACCEPT icmp -- anywhere anywhere state NEW,RELATED,ESTABLISHED
ACCEPT all -- anywhere anywhere state NEW,RELATED,ESTABLISHED



No comments: