Thursday, 14 August 2014

NetSecL Legacy, GrSecurity, Linux Security, IPTables.

When I began with NetSecL Linux as a Linux distribution my main concern was the security of that system, so I decided to make this article explaining some of the approaches that were used in the distribution. I am open to help if you would like a server or any other system hardened (NAS, mail, create secure virtual environments etc.) - please contact me.

So let's begin with the vulnerabilities - one of the things that NetSecL protected against were different methods of exploitation.  Here naturally GrSecurity was the solution to this problems. Apart from the patch that grsecurity supplies I needed also to recompile gcc and binutils - you will probably need to check this https://pax.grsecurity.net/. I now see that the binutils is out of date. But you can check this paxtest - it will test your security.

bash-3.1# paxtest blackhat
Executable anonymous mapping : Killed
Executable bss : Killed
Executable data : Killed
Executable heap : Killed
Executable stack : Killed
Executable anonymous mapping (mprotect) : Killed
Executable bss (mprotect) : Killed
Executable data (mprotect) : Killed
Executable heap (mprotect) : Killed
Executable shared library bss (mprotect) : Killed
Executable shared library data (mprotect): Killed
Executable stack (mprotect) : Killed
Anonymous mapping randomisation test : 16 bits (guessed)
Heap randomisation test (ET_EXEC) : 13 bits (guessed)
Heap randomisation test (ET_DYN) : 25 bits (guessed)
Main executable randomisation (ET_EXEC) : 16 bits (guessed)
Main executable randomisation (ET_DYN) : 16 bits (guessed)
Shared library randomisation test : 16 bits (guessed)
Stack randomisation test (SEGMEXEC) : 23 bits (guessed)
Stack randomisation test (PAGEEXEC) : 24 bits (guessed)
Return to function (strcpy) : Vulnerable
Return to function (strcpy, RANDEXEC) : Vulnerable
Return to function (memcpy) : Vulnerable
Return to function (memcpy, RANDEXEC) : Vulnerable
Executable shared library bss : Killed
Executable shared library data : Killed
Writable text segments : Killed

Lets begin with the patch we have from https://grsecurity.net/ . Choose the appropriate kernel version and apply the patch to it. If you are using it on OpenSuse you might want to recompile the gcc itself to have the needed plugins otherwise you will get a warning (as far as I remember) and you will miss some of the important features.

patch -p1 < 
make menuconfig

Lets begin with the kernel configuration, this is one of my config files - after applying GrSecurity patch:

#
# Security options
#

#
# Grsecurity
#
CONFIG_TASK_SIZE_MAX_SHIFT=47
CONFIG_PAX_USERCOPY_SLABS=y
CONFIG_GRKERNSEC=y
# CONFIG_GRKERNSEC_CONFIG_AUTO is not set
CONFIG_GRKERNSEC_CONFIG_CUSTOM=y
CONFIG_GRKERNSEC_PROC_GID=10

#
# Customize Configuration
#

#
# PaX
#
CONFIG_PAX=y

#
# PaX Control
#
CONFIG_PAX_SOFTMODE=y
CONFIG_PAX_EI_PAX=y
CONFIG_PAX_PT_PAX_FLAGS=y
CONFIG_PAX_XATTR_PAX_FLAGS=y
# CONFIG_PAX_NO_ACL_FLAGS is not set
CONFIG_PAX_HAVE_ACL_FLAGS=y
# CONFIG_PAX_HOOK_ACL_FLAGS is not set

#
# Non-executable pages
#
CONFIG_PAX_NOEXEC=y
CONFIG_PAX_PAGEEXEC=y
# CONFIG_PAX_EMUTRAMP is not set
CONFIG_PAX_MPROTECT=y
# CONFIG_PAX_MPROTECT_COMPAT is not set
# CONFIG_PAX_ELFRELOCS is not set
# CONFIG_PAX_KERNEXEC is not set
CONFIG_PAX_KERNEXEC_PLUGIN_METHOD=""

#
# Address Space Layout Randomization
#
CONFIG_PAX_ASLR=y
# CONFIG_PAX_RANDKSTACK is not set
CONFIG_PAX_RANDUSTACK=y
CONFIG_PAX_RANDMMAP=y

#
# Miscellaneous hardening features
#
# CONFIG_PAX_MEMORY_STACKLEAK is not set
# CONFIG_PAX_MEMORY_UDEREF is not set
CONFIG_PAX_REFCOUNT=y
# CONFIG_PAX_USERCOPY is not set
# CONFIG_PAX_SIZE_OVERFLOW is not set
# CONFIG_PAX_LATENT_ENTROPY is not set

#
# Memory Protections
#
CONFIG_GRKERNSEC_KMEM=y
# CONFIG_GRKERNSEC_IO is not set
CONFIG_GRKERNSEC_PROC_MEMMAP=y
CONFIG_GRKERNSEC_BRUTE=y
CONFIG_GRKERNSEC_MODHARDEN=y
CONFIG_GRKERNSEC_HIDESYM=y
# CONFIG_GRKERNSEC_KERN_LOCKOUT is not set

#
# Role Based Access Control Options
#
# CONFIG_GRKERNSEC_NO_RBAC is not set
# CONFIG_GRKERNSEC_ACL_HIDEKERN is not set
CONFIG_GRKERNSEC_ACL_MAXTRIES=3
CONFIG_GRKERNSEC_ACL_TIMEOUT=30

#
# Filesystem Protections
#
CONFIG_GRKERNSEC_PROC=y
# CONFIG_GRKERNSEC_PROC_USER is not set
CONFIG_GRKERNSEC_PROC_USERGROUP=y
# CONFIG_GRKERNSEC_PROC_ADD is not set
CONFIG_GRKERNSEC_LINK=y
# CONFIG_GRKERNSEC_SYMLINKOWN is not set
CONFIG_GRKERNSEC_FIFO=y
# CONFIG_GRKERNSEC_SYSFS_RESTRICT is not set
# CONFIG_GRKERNSEC_ROFS is not set
CONFIG_GRKERNSEC_CHROOT=y
CONFIG_GRKERNSEC_CHROOT_MOUNT=y
CONFIG_GRKERNSEC_CHROOT_DOUBLE=y
CONFIG_GRKERNSEC_CHROOT_PIVOT=y
CONFIG_GRKERNSEC_CHROOT_CHDIR=y
CONFIG_GRKERNSEC_CHROOT_CHMOD=y
CONFIG_GRKERNSEC_CHROOT_FCHDIR=y
CONFIG_GRKERNSEC_CHROOT_MKNOD=y
CONFIG_GRKERNSEC_CHROOT_SHMAT=y
CONFIG_GRKERNSEC_CHROOT_UNIX=y
CONFIG_GRKERNSEC_CHROOT_FINDTASK=y
CONFIG_GRKERNSEC_CHROOT_NICE=y
CONFIG_GRKERNSEC_CHROOT_SYSCTL=y
CONFIG_GRKERNSEC_CHROOT_CAPS=y

#
# Kernel Auditing
#
# CONFIG_GRKERNSEC_AUDIT_GROUP is not set
# CONFIG_GRKERNSEC_EXECLOG is not set
CONFIG_GRKERNSEC_RESLOG=y
# CONFIG_GRKERNSEC_CHROOT_EXECLOG is not set
CONFIG_GRKERNSEC_AUDIT_PTRACE=y
# CONFIG_GRKERNSEC_AUDIT_CHDIR is not set
CONFIG_GRKERNSEC_AUDIT_MOUNT=y
CONFIG_GRKERNSEC_SIGNAL=y
CONFIG_GRKERNSEC_FORKFAIL=y
CONFIG_GRKERNSEC_TIME=y
CONFIG_GRKERNSEC_PROC_IPADDR=y
CONFIG_GRKERNSEC_RWXMAP_LOG=y
# CONFIG_GRKERNSEC_AUDIT_TEXTREL is not set

#
# Executable Protections
#
CONFIG_GRKERNSEC_DMESG=y
CONFIG_GRKERNSEC_HARDEN_PTRACE=y
CONFIG_GRKERNSEC_PTRACE_READEXEC=y
CONFIG_GRKERNSEC_SETXID=y
# CONFIG_GRKERNSEC_TPE is not set

#
# Network Protections
#
CONFIG_GRKERNSEC_RANDNET=y
CONFIG_GRKERNSEC_BLACKHOLE=y
# CONFIG_GRKERNSEC_SOCKET is not set

#
# Sysctl Support
#
CONFIG_GRKERNSEC_SYSCTL=y
CONFIG_GRKERNSEC_SYSCTL_ON=y

#
# Logging Options
#
CONFIG_GRKERNSEC_FLOODTIME=10
CONFIG_GRKERNSEC_FLOODBURST=4
CONFIG_KEYS=y
CONFIG_TRUSTED_KEYS=m
CONFIG_ENCRYPTED_KEYS=m
CONFIG_KEYS_DEBUG_PROC_KEYS=y
# CONFIG_SECURITY_DMESG_RESTRICT is not set
CONFIG_SECURITY=y
CONFIG_SECURITYFS=y
CONFIG_SECURITY_NETWORK=y
CONFIG_SECURITY_NETWORK_XFRM=y
CONFIG_SECURITY_PATH=y
CONFIG_INTEL_TXT=y
CONFIG_LSM_MMAP_MIN_ADDR=65536
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SELINUX_BOOTPARAM=y
CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=0
# CONFIG_SECURITY_SELINUX_DISABLE is not set
CONFIG_SECURITY_SELINUX_DEVELOP=y
CONFIG_SECURITY_SELINUX_AVC_STATS=y
CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set
# CONFIG_SECURITY_SMACK is not set
# CONFIG_SECURITY_TOMOYO is not set
# CONFIG_SECURITY_APPARMOR is not set
CONFIG_INTEGRITY=y
CONFIG_IMA=y
CONFIG_IMA_MEASURE_PCR_IDX=10
CONFIG_IMA_AUDIT=y
CONFIG_IMA_LSM_RULES=y
CONFIG_DEFAULT_SECURITY_SELINUX=y
# CONFIG_DEFAULT_SECURITY_DAC is not set
CONFIG_DEFAULT_SECURITY="selinux"
CONFIG_XOR_BLOCKS=m
CONFIG_ASYNC_CORE=m
CONFIG_ASYNC_MEMCPY=m
CONFIG_ASYNC_XOR=m
CONFIG_ASYNC_PQ=m
CONFIG_ASYNC_RAID6_RECOV=m
CONFIG_ASYNC_TX_DISABLE_PQ_VAL_DMA=y
CONFIG_ASYNC_TX_DISABLE_XOR_VAL_DMA=y
CONFIG_CRYPTO=y



This is a script that applies the permissions paxctl permission and the key to a normally workin system - mprotect "breaks" some of the working software and permissions should be applied:

#!/bin/sh
#These are permissions for PaX and the paranoid kernels
#Don't edit unless you know what you are doing!
#This script is ran once and then "chmod -x"-ed if you change it you must make it executable again (chmod +x) and re-run it.
#Please report any problems on i_stanchev@ml1.net
echo "Converting /sbin/ adding PT_PAX_FLAGS"
paxctl -Cq /sbin/*
echo "Converting /usr/sbin/ adding PT_PAX_FLAGS"
paxctl -Cq /usr/sbin/*
echo "Converting /usr/bin/ adding PT_PAX_FLAGS"
paxctl -Cq /usr/bin/*
echo "Converting /usr/lib/ adding PT_PAX_FLAGS"
paxctl -Cq /usr/lib64/gimp/2.0/plug-ins/*
paxctl -Cq /usr/lib64/gnome-vfs-2.0/*
paxctl -Cq /usr/lib/gnome-vfs-2.0/*
paxctl -Cq /usr/lib/YaST2/bin/*
paxctl -Cq /usr/lib64/YaST2/plugin/*
echo "Converting /usr/libexec/ adding PT_PAX_FLAGS"
paxctl -Cq /usr/libexec/*
echo "Converting /usr/libexec/xfce4/panel-plugins/ adding PT_PAX_FLAGS"
paxctl -Cq /usr/libexec/xfce4/panel-plugins/*
echo "Converting /usr/lib/qt/bin/ adding PT_PAX_FLAGS"
paxctl -Cq /usr/lib/qt/bin/*
echo "Applying paxctl permissions"
paxctl -mq /usr/lib/gnome-vfs-2.0/*
paxctl -mq /usr/lib64/gnome-vfs-2.0/*
paxctl -mq /usr/lib/YaST2/bin/*
paxctl -mq /usr/lib64/YaST2/plugin/*
paxctl -mq /usr/lib64/gimp/2.0/plug-ins/*
paxctl -mq /usr/bin/*
paxctl -permsx /usr/bin/wine-preloader
paxctl -permsx /usr/bin/wine
paxctl -permsx /usr/bin/wine64-preloader
paxctl -permsx /usr/bin/wine64
paxctl -Cq /usr/lib64/firefox/firefox-bin
paxctl -mq /usr/lib64/firefox/firefox-bin
paxctl -mq /usr/sbin/*
paxctl -mq /sbin/*
paxctl -mq /usr/libexec/*
echo "Done applying paxctl permissions"

Compiling the kernel:

make bzImage
make modules
make INSTALL_MOD_STRIP=1 modules_install
make install
mkinitrd



This is another component the NetSecL Firewall which ensures the network security of the OS. Here you should consider also stopping your unneeded services and comment them out in inetd. This is the script of the firewall:

#!/bin/bash
#This program is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public License
#as published by the Free Software Foundation; either version 2
#of the License, or (at your option) any later version.
#
#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# This firewall was developed for the NetSecL Linux Distribution and was put together by:
# Yuriy Stanchev - NetSecL Maintainer
#
#The nesecl-firewall is being discussed in the NetSecL forum
#http://netsecl.com/forum/index.php?PHPSESSID=030b98b33c1707cfa364e792b17bfbfe&board=2.0

if [ "$1" = "start" ]; then

IPTABLES="/usr/sbin/iptables"

#Probe internet interfaces
if [ "$(/sbin/ifconfig | /bin/grep eth0)" ]; then
INTERNET="eth0"

elif [ "$(/sbin/ifconfig | /bin/grep eth1)" ]; then
INTERNET="eth1"

else INTERNET="ppp0"

fi

#Get the DNS Server
NAMESERVER=`grep nameserver /etc/resolv.conf |head -1|awk '{print $2}'`
#NAMESERVER="" #Manually set this variabale if the DNS is not recognized
if [ "$NAMESERVER" = "" ]; then
 echo "You must manually set the DNS Server. Please edit /etc/rc.d/rc.firewall or use netconfig to configure your network and set a DNS Server."
fi

CLASS_A="127.0.0.0/8"
CLASS_B="172.16.0.0/12"
CLASS_C="192.168.0.0/16"
CLASS_D_MULTICAST="224.0.0.0/4"
CLASS_E_RESERVED_NET="240.0.0.0/5"
BROADCAST_DEST="255.255.255.255"

# FTP (20, 21) SSH (22) SMTP (25) WHOIS (43) WWW (80) POP (110) IDENT (113) USENET (119)  IMAP (143) SSL (443) PROXY (8008, 8080)
PORTS="20 21 25 80 110 113 443 8080" #
PASSIVEFTP="Y"
PINGDEATH="Y" 
LOGGING="N" 
BEHINDROUTER="Y" #This Rules allows connections from your Router to you (Assumes that the DNS is the IP of your Router/Gateway) 
SSHDREQ="N" #ALLOW INCOMING SSHD REQUESTS
SSHSRV="N" #ALLOW SSH server

$IPTABLES -P INPUT DROP
$IPTABLES -P OUTPUT DROP
$IPTABLES -P FORWARD DROP

# Firewall initialization, remove everything, start with clean tables
$IPTABLES -F # remove all rules
$IPTABLES -t nat -F # remove all rules
$IPTABLES -t mangle -F # delete all user-defined chains
$IPTABLES -X # delete all user-defined chains
$IPTABLES -t nat -X # remove all rules
$IPTABLES -t mangle -X # delete all user-defined chains

#enable broadcast echo protection
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts

echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all

#Disable Source Routed packets
for f in /proc/sys/net/ipv4/conf/*/accept_source_route; do
echo 0 > $f
done

#Enable TCP SYN Cookie Protection
echo 1 > /proc/sys/net/ipv4/tcp_syncookies

# Reduce SYN Floods
echo 4096 >/proc/sys/net/ipv4/tcp_max_syn_backlog 

#IP Forward
echo 0 > /proc/sys/net/ipv4/ip_forward

#Disable ICMP redirect Acceptance
for f in /proc/sys/net/ipv4/conf/*/accept_redirects; do
echo 0 > f$
done

# Send Redirect Messges
for f in /proc/sys/net/ipv4/conf/*/send_redirects; do
echo 0 > $f
done

echo 0 > /proc/sys/net/ipv4/conf/all/secure_redirects

# Drop Spoofed Packets coming in on an interface, which, if replied to,
# would result in the reply goingout a different interface.
for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
echo 1 > f$
done

# Log packets with impossible addresses
for f in /proc/sys/net/ipv4/conf/*/log_martians; do
echo 1 > $f
done

# Set up our logging and packet 'executing' chains
$IPTABLES -N LOG_DROP
$IPTABLES -N logaborted2
$IPTABLES -A logaborted2 -j LOG --log-prefix "ABORTED " --log-level 4 --log-ip-options --log-tcp-options --log-tcp-sequence
$IPTABLES -A logaborted2 -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -N logaborted
$IPTABLES -A logaborted -m limit --limit 1/second --limit-burst 10 -j logaborted2
$IPTABLES -A logaborted -m limit --limit 2/minute --limit-burst 1 -j LOG --log-prefix "LIMITED " --log-level 4 

# allow everything for loop device
$IPTABLES -A INPUT -i lo -j ACCEPT

$IPTABLES -A OUTPUT -j ACCEPT

if [ "$BEHINDROUTER" = "Y" ]; then
$IPTABLES -A INPUT -i $INTERNET -s $NAMESERVER -j ACCEPT
fi

#portscan detector
$IPTABLES -N PORTSCAN
#portscan detection module
# NMAP FIN/URG/PSH
$IPTABLES -A INPUT -p tcp --tcp-flags ALL FIN,URG,PSH -m recent --set -j PORTSCAN
$IPTABLES -A FORWARD -p tcp --tcp-flags ALL FIN,URG,PSH -m recent --set -j PORTSCAN
# SYN/RST
$IPTABLES -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -m recent --set -j PORTSCAN
$IPTABLES -A FORWARD -p tcp --tcp-flags SYN,RST SYN,RST -m recent --set -j PORTSCAN
# SYN/FIN -- Scan(probably)
$IPTABLES -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -m recent --set -j PORTSCAN
$IPTABLES -A FORWARD -p tcp --tcp-flags SYN,FIN SYN,FIN -m recent --set -j PORTSCAN
# NMAP FIN Stealth
$IPTABLES -A INPUT -p tcp --tcp-flags ALL FIN -m recent --set -j PORTSCAN
$IPTABLES -A FORWARD -p tcp --tcp-flags ALL FIN -m recent --set -j PORTSCAN
# ALL/ALL Scan
$IPTABLES -A INPUT -p tcp --tcp-flags ALL ALL -m recent --set -j PORTSCAN
$IPTABLES -A FORWARD -p tcp --tcp-flags ALL ALL -m recent --set -j PORTSCAN
# NMAP Null Scan
$IPTABLES -A INPUT -p tcp --tcp-flags ALL NONE -m recent --set -j PORTSCAN
$IPTABLES -A FORWARD -p tcp --tcp-flags ALL NONE -m recent --set -j PORTSCAN
#XMAS
$IPTABLES -A INPUT -p tcp --tcp-flags ALL URG,ACK,PSH,RST,SYN,FIN -m recent --set -j PORTSCAN
$IPTABLES -A FORWARD -p tcp --tcp-flags ALL URG,ACK,PSH,RST,SYN,FIN -m recent --set -j PORTSCAN

if [ "$LOGGING" = "Y" ]; then
$IPTABLES -A LOG_DROP -m limit --limit 6/minute --limit-burst 1 -j LOG --log-prefix "PORTSCAN SHUN " --log-level 4 --log-ip-options --log-tcp-options --log-tcp-sequence
$IPTABLES -A LOG_DROP -j DROP 
fi

$IPTABLES -A PORTSCAN -j DROP


# Drop packets with bad tcp flags
$IPTABLES -N BAD_FLAGS
$IPTABLES -A INPUT -p tcp --tcp-option 64 -m recent --set -j BAD_FLAGS
$IPTABLES -A INPUT -p tcp --tcp-option 128 -m recent --set -j BAD_FLAGS

if [ "$LOGGING" = "Y" ]; then
$IPTABLES -A LOG_DROP -m limit --limit 6/minute --limit-burst 1 -j LOG --log-prefix "BADFLAGS SHUN " --log-level 4 --log-ip-options --log-tcp-options --log-tcp-sequence
$IPTABLES -A LOG_DROP -j DROP 
fi

$IPTABLES -A BAD_FLAGS -j DROP

# Drop packets that are too small Note:
$IPTABLES -N SMALL
$IPTABLES -A INPUT -p udp -m length --length 0:27 -m recent --set -j SMALL
$IPTABLES -A INPUT -p tcp -m length --length 0:39 -m recent --set -j SMALL
$IPTABLES -A INPUT -p icmp -m length --length 0:27 -m recent --set -j SMALL
$IPTABLES -A INPUT -p 30 -m length --length 0:31 -m recent --set -j SMALL
$IPTABLES -A INPUT -p 47 -m length --length 0:39 -m recent --set -j SMALL
$IPTABLES -A INPUT -p 50 -m length --length 0:49 -m recent --set -j SMALL
$IPTABLES -A INPUT -p 51 -m length --length 0:35 -m recent --set -j SMALL
$IPTABLES -A INPUT -m length --length 0:19 -m recent --set -j SMALL

if [ "$LOGGING" = "Y" ]; then
$IPTABLES -A LOG_DROP -m limit --limit 6/minute --limit-burst 1 -j LOG --log-prefix "SMALL SHUN " --log-level 4 --log-ip-options --log-tcp-options --log-tcp-sequence
$IPTABLES -A LOG_DROP -j DROP 
fi 

$IPTABLES -A SMALL -j DROP

# Reject all BOGUS packets
$IPTABLES -N BOGUS
$IPTABLES -t filter -p all -A INPUT -m conntrack --ctstate INVALID -j BOGUS
$IPTABLES -t filter -p all -A OUTPUT -m conntrack --ctstate INVALID -j BOGUS
$IPTABLES -t filter -p all -A FORWARD -m conntrack --ctstate INVALID -j BOGUS

if [ "$LOGGING" = "Y" ]; then
$IPTABLES -A LOG_DROP -m limit --limit 6/minute --limit-burst 1 -j LOG --log-prefix "BOGUS SHUN " --log-level 4 --log-ip-options --log-tcp-options --log-tcp-sequence
$IPTABLES -A LOG_DROP -j DROP 
fi 

$IPTABLES -A BOGUS -j REJECT

#Enforce SYN only connections on NEW connections
$IPTABLES -A INPUT -p tcp ! --syn -m conntrack --ctstate NEW -j LOG --log-prefix "New not syn:"
$IPTABLES -A INPUT -p tcp ! --syn -m conntrack --ctstate NEW -j DROP
$IPTABLES -A FORWARD -p tcp ! --syn -m conntrack --ctstate NEW -j LOG --log-prefix "New not syn:"
$IPTABLES -A FORWARD -p tcp ! --syn -m conntrack --ctstate NEW -j DROP

# Drop packets to "odd" ports
$IPTABLES -N ODDPORTS
$IPTABLES -A INPUT -p udp --sport 2:21 -m recent --set -j ODDPORTS
$IPTABLES -A INPUT -p udp --dport 2:21 -m recent --set -j ODDPORTS
$IPTABLES -A INPUT -p tcp --dport 0 -m recent --set -j ODDPORTS
$IPTABLES -A INPUT -p tcp --sport 0 -m recent --set -j ODDPORTS
$IPTABLES -A FORWARD -i $INTERNET -p udp --dport 2:21 -m recent --set -j ODDPORTS
$IPTABLES -A FORWARD -i $INTERNET -p tcp --dport 0 -m recent --set -j ODDPORTS
$IPTABLES -A FORWARD -i $INTERNET -p tcp --sport 0 -m recent --set -j ODDPORTS

if [ "$LOGGING" = "Y" ]; then
$IPTABLES -A LOG_DROP -m limit --limit 6/minute --limit-burst 1 -j LOG --log-prefix "ODDPORTS SHUN " --log-level 4 --log-ip-options --log-tcp-options --log-tcp-sequence
$IPTABLES -A LOG_DROP -j DROP 
fi 

$IPTABLES -A ODDPORTS -j DROP

#BLOCK OS Fingerprint Detection
$IPTABLES -N os-fingerprint
$IPTABLES -F os-fingerprint
$IPTABLES -A os-fingerprint -p tcp --dport 0 -j DROP
$IPTABLES -A os-fingerprint -p udp --dport 0 -j DROP
$IPTABLES -A os-fingerprint -p tcp --sport 0 -j DROP
$IPTABLES -A os-fingerprint -p udp --sport 0 -j DROP
$IPTABLES -A os-fingerprint -p icmp --icmp-type address-mask-request -j DROP
$IPTABLES -A os-fingerprint -p icmp --icmp-type address-mask-reply -j DROP

#
#refuse packets claiming to be from a Class_A private network.
$IPTABLES -A INPUT -i $INTERNET -s $CLASS_A -j DROP

#refuse packets claiming to be from a Class_B private network.
$IPTABLES -A INPUT -i $INTERNET -s $CLASS_B -j DROP

#refuse packets claiming to be from a Class_C private network.
$IPTABLES -A INPUT -i $INTERNET -s $CLASS_C -j DROP

#Refuse Class E reserved IP
$IPTABLES -A INPUT -i $INTERNET -s $CLASS_D_MULTICAST -j DROP

#Refuse Class D multicast address
$IPTABLES -A INPUT -s $CLASS_E_RESERVED_NET -j DROP

#refuse malformed broadcacst packets
$IPTABLES -A INPUT -i $INTERNET -s $BROADCAST_DEST -j LOG
$IPTABLES -A INPUT -i $INTERNET -s $BROADCAST_DEST -j DROP

$IPTABLES -A INPUT -i $INTERNET -d $BROADCAST_DEST -j LOG
$IPTABLES -A INPUT -i $INTERNET -d $BROADCAST_DEST -j DROP

#Refuse addresses defined as reserved by the IANA
$IPTABLES -A INPUT -i $INTERNET -s 0.0.0.0/8 -j DROP
$IPTABLES -A INPUT -i $INTERNET -s 169.254.0.0/16 -j DROP
$IPTABLES -A INPUT -i $INTERNET -s 192.0.2.0/24 -j DROP

COMBLOCK="0:1 2 8 13 98 111 137:139 161:162 445 901 1214 1524 1999 2049 3049 4329 6346 3128 8000 12345 27444 27665 31335 31337 65535"
TCPBLOCK="$COMBLOCK 512:515 1080 2000 3128 6000:6063"
UDPBLOCK="$COMBLOCK 520 123 517:518 1427 4045 9000"

$IPTABLES -N bad-ports
$IPTABLES -F bad-ports

echo -n "FW: Blocking attacks to TCP port "
for i in $TCPBLOCK;
do
echo -n "$i "
$IPTABLES -A INPUT -p tcp --dport $i -j DROP
$IPTABLES -A OUTPUT -p tcp --dport $i -j DROP
$IPTABLES -A FORWARD -p tcp --dport $i -j DROP
$IPTABLES -A bad-ports -p tcp --dport $i -j DROP
done
echo ""

echo -n "FW: Blocking attacks to UDP port "
for i in $UDPBLOCK;
do
echo -n "$i "
$IPTABLES -A INPUT -p udp --dport $i -j DROP
$IPTABLES -A OUTPUT -p udp --dport $i -j DROP
$IPTABLES -A FORWARD -p udp --dport $i -j DROP
$IPTABLES -A bad-ports -p udp --dport $i -j DROP
done
echo ""

# allow DNS in all directions
if [ "$NAMESERVER" != "" ]; then
$IPTABLES -A OUTPUT -p tcp --sport 0:65535 -d $NAMESERVER --dport 53:53 -j ACCEPT
$IPTABLES -A INPUT -p tcp ! --syn -s $NAMESERVER --sport 53:53 --dport 0:65535 -j ACCEPT
fi



#ALLOW INCOMING SSHD REQUESTS.

if [ "$SSHDREQ" = "Y" ]; then
$IPTABLES -N allow-ssh-input
$IPTABLES -F allow-ssh-input
$IPTABLES -A allow-ssh-input -m limit --limit 1/second -p tcp --tcp-flags ALL RST --dport 22 -j ACCEPT
$IPTABLES -A allow-ssh-input -m limit --limit 1/second -p tcp --tcp-flags ALL FIN --dport 22 -j ACCEPT
$IPTABLES -A allow-ssh-input -m limit --limit 1/second -p tcp --tcp-flags ALL SYN --dport 22 -j ACCEPT
$IPTABLES -A allow-ssh-input -m state --state ESTABLISHED,RELATED -p tcp --dport 22 -j ACCEPT
fi

if [ "$SSHSRV" = "Y" ]; then
$IPTABLES -A INPUT -j allow-ssh-input
fi

# Detect aborted TCP connections.
$IPTABLES -A INPUT -m state --state ESTABLISHED,RELATED -p tcp --tcp-flags RST RST -j logaborted

# Allow previously established connections
$IPTABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

$IPTABLES -A INPUT -m state --state INVALID -j LOG --log-prefix "INVALID input: "
$IPTABLES -A INPUT -m state --state INVALID -j DROP

$IPTABLES -A OUTPUT -m state --state INVALID -j LOG --log-prefix "INVALID output: "
$IPTABLES -A OUTPUT -m state --state INVALID -j DROP

#Ping of death
if [ "$PINGDEATH" = "Y" ]; then
$IPTABLES -N ping-death
$IPTABLES -A ping-death -m limit --limit 1/s --limit-burst 4 -j ACCEPT
$IPTABLES -A ping-death -j LOG --log-prefix "iptables ping-death: "
$IPTABLES -A ping-death -j DROP
$IPTABLES -A INPUT -i $INTERNET -p icmp --icmp-type echo-request -j ping-death
$IPTABLES -A FORWARD -i $INTERNET -p icmp --icmp-type echo-request -j ping-death
fi

#Stealth scan
$IPTABLES -N stealth-scan
$IPTABLES -A stealth-scan -j LOG --log-prefix "iptables stealth-scan: "
$IPTABLES -A stealth-scan -j DROP
$IPTABLES -A INPUT -i $INTERNET -p tcp ! --syn -m state --state NEW -j stealth-scan
$IPTABLES -A FORWARD -i $INTERNET -p tcp ! --syn -m state --state NEW -j stealth-scan

# Allow certain critical ICMP types
$IPTABLES -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT # Dest unreachable
$IPTABLES -A OUTPUT -p icmp --icmp-type destination-unreachable -j ACCEPT # Dest unreachable
$IPTABLES -A FORWARD -p icmp --icmp-type destination-unreachable -j ACCEPT &> /dev/null # Dest unreachable
$IPTABLES -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT # Time exceeded
$IPTABLES -A OUTPUT -p icmp --icmp-type time-exceeded -j ACCEPT # Time exceeded
$IPTABLES -A FORWARD -p icmp --icmp-type time-exceeded -j ACCEPT &> /dev/null # Time exceeded
$IPTABLES -A INPUT -p icmp --icmp-type parameter-problem -j ACCEPT # Parameter Problem
$IPTABLES -A OUTPUT -p icmp --icmp-type parameter-problem -j ACCEPT # Parameter Problem
$IPTABLES -A FORWARD -p icmp --icmp-type parameter-problem -j ACCEPT &> /dev/null # Parameter Problem

$IPTABLES -A INPUT --fragment -p icmp -j LOG --log-prefix "Fragmented IMCP: "
$IPTABLES -A INPUT --fragment -p icmp -j DROP

# PORTS
if [ "$NAMESERVER" != "" ]; then

for i in $PORTS;
do
$IPTABLES -A OUTPUT -p tcp --sport 1024:65535 -d $NAMESERVER --dport $i:$i -m state --state NEW -j ACCEPT
$IPTABLES -A OUTPUT -p tcp --sport 1024:65535 -d $NAMESERVER --dport $i:$i -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A INPUT -p tcp ! --syn -s $NAMESERVER --sport $i --dport 1024:65535 -m state --state ESTABLISHED,RELATED -j ACCEPT
done

# Passive ftp
if [ "$PASSIVEFTP" = "Y" ]; then
$IPTABLES -A OUTPUT -p tcp --sport 1024:65535 -d $NAMESERVER --dport 1024:65535 -m state --state NEW -j ACCEPT
$IPTABLES -A OUTPUT -p tcp --sport 1024:65535 -d $NAMESERVER --dport 1024:65535 -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A INPUT -p tcp ! --syn -s $NAMESERVER --sport 1024:65535 --dport 1024:65535 -m state --state ESTABLISHED,RELATED -j ACCEPT
fi

fi

if [ "$LOGGING" = "Y" ]; then
$IPTABLES -A LOG_DROP -j LOG --log-prefix "Attack log: "
$IPTABLES -A LOG_DROP -j DROP
$IPTABLES -A INPUT -j LOG_DROP # drop all incomming
$IPTABLES -A FORWARD -j LOG_DROP # drop all forwarded
fi 

elif [ "$1" = "stop" ]; then
iptables -F
iptables -X
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P INPUT ACCEPT


elif [ "$1" = "status" ]; then
iptables -L -v

else
echo "usage: $0 start|stop|status"
fi

exit $RETVAL

I will stop here - there is much more to it, however if you are interested in any way please contact me. I hope you liked the article and it was helpful for you to harden the system you have.

References: