#!/usr/bin/perl -w
# VIAGRA v1.0
# lewk@csh.rit.edu
# This program was based off of the article:
# http://www.securityfocus.com/infocus/1711
use strict;

my $iface = "eth0";
my $editor = "vi";
my $backup = "";
my $restore = "";

print "VIAGRA v1.0 - Linux network hardening script\n";
print "Hardens your network, so you can keep it up longer!\n";
print "Usage: $0 [options]\n";
print "Options:\n  -i interface     Specifiy interface (default is eth0)\n";
print "  -b file          Backup original settings to file\n";
print "  -r file          Restore a previously backed up file\n";
print "  -e editor        Use specific edtor (default is vi)\n";

# Validate command line arguments
if ( $#ARGV % 2 != 1 ) {
	print STDERR "Error: Invalid arguments\n"; 
	exit(1);
}

for (my $i=0; $i <= $#ARGV; $i++) {
	$iface = $ARGV[$i+1] if ($ARGV[$i] eq "-i");
	unless (-d "/proc/sys/net/ipv4/conf/$iface") { 
		print STDERR "Invalid interface: $iface\n"; 
		exit(1);
	} 
	if ($ARGV[$i] eq "-b") {
		$backup = $ARGV[$i+1];
		if (-e $backup) {
			print STDERR "Error: Backup file already exists\n";
			exit(1);
		}
	}
	if ($ARGV[$i] eq "-r") {
		$restore = $ARGV[$i+1];
		unless (-e $restore) {
			print STDERR "Error: Restore file does not exist\n";
			exit(1);
		}
	}
	$editor = $ARGV[$i+1] if ($ARGV[$i] eq "-e");
}

# Hardcoded pseudo files along with brief descriptions
my %files = (
	"icmp_echo_ignore_all", 
	"When enabled, ignore all ICMP ECHO REQUEST (ping) packets. Does nothing " .
	"to actually increase security, but can hide you from ping sweeps, which " .
	"may prevent you from being port scanned. Nmap, for example, will not " .
	"scan unpingable hosts unless -P0 is specified. This will prevent normal " .
	"network connectivity tests, however.",
	"icmp_echo_ignore_broadcasts",
	"When enabled, ignore broadcast and multicast pings. It's a good idea to " .
	"ignore these to prevent you from becoming an inadvertent participant in " .
	"a distributed denial of service attack, such as Smurf.",
	"conf/$iface/accept_source_route",
	"When source routed packets are allowed, an attacker can forge the " .
	"source IP address of connections by explicitly saying how a packet " .
	"should be routed across the Internet. This could enable them to abuse " .
	"trust relationships or get around TCP Wrapper-style access lists. " .
	"There's no need for source routing on today's Internet.",
	"conf/$iface/rp_filter",
	"When enabled, if a packet comes in on one interface, but our response " .
	"would go out a different interface, drop the packet. Unnecessary on " .
	"hosts with only one interface, but remember, PPP and VPN connections " .
	"usually have their own interface, so it's a good idea to enable it " .
	"anyway. Can be a problem for routers on a network that has dynamically " .
	"changing routes. However on firewall/routers that are the single " .
	"connection between networks, this automatically provides spoofing " .
	"protection without network ACLs.",
	"conf/$iface/accept_redirects",
	"When you send a packet destined to a remote machine you usually send it " .
	"to a default router. If this machine sends an ICMP redirect, it lets " .
	"you know that there is a different router to which you should address " .
	"the packet for a better route, and your machine will send the packet " .
	"there instead. A cracker can use ICMP redirects to trick you into " .
	"sending your packets through a machine it controls to perform " .
	"man-in-the-middle attacks. This should certainly never be enabled on a " .
	"well configured router.",
	"conf/$iface/secure_redirects",
	"Honor ICMP redirects only when they come from a router that is " .
	"currently set up as a default gateway. Should only be enabled if you " .
	"have multiple routers on your network. If your network is fairly static " .
	"and stable, it's better to leave this disabled.",
	"conf/$iface/send_redirects",
	"If you're a router and there are alternate routes of which you should " .
	"inform your clients (you have multiple routers on your networks), " .
	"you'll want to enable this. If you have a stable network where hosts " .
	"already have the correct routes set up, this should not be necessary, " .
	"and it's never needed for non-routing hosts.",
	"ip_forward",
	"If you're a router this needs to be enabled. This applies to VPN " .
	"interfaces as well. If you do need to forward packets from one " .
	"interface to another, make sure you have appropriate kernel ACLs set " .
	"to allow only the traffic you want to forward.",
	"ipfrag_high_thresh",
	"The kernel needs to allocate memory to be able to reassemble " .
	"fragmented packets. Once this limit is reached, the kernel will start " .
	"discarding fragmented packets. Setting this too low or high can leave " .
	"you vulnerable to a denial of service attack. While under an attack of " .
	"many fragmented packets, a value too low will cause legitimate " .
	"fragmented packets to be dropped, a value too high can cause excessive " .
	"memory and CPU use to defragment attack packets.",
	"ipfrag_low_thresh",
	"Similar to ip_frag_high_thresh, the minimum amount of memory you want " .
	"to allow for fragment reassembly.",
	"ipfrag_time",
	"The number of seconds the kernel should keep IP fragments before " .
	"discarding them. Thirty seconds is usually a good time. Decrease this " .
	"if attackers are forging fragments and you'll be better able to service " .
	"legitimate connections.",
	"ip_always_defrag",
	"Always defragment fragmented packets before passing them along through " .
	"the firewall. Linux 2.4 and later kernels do not have this /proc entry, " .
	"defragmentation is turned on by default.",
	"tcp_max_orphans",
	"The number of local sockets that are no longer attached to a process " .
	"that will be maintained. These sockets are usually the result of " .
	"failed network connections, such as the FIN-WAIT state where the remote " .
	"end has not acknowledged the tear down of a TCP connection. After this " .
	"limit has been reached, orphaned connections are removed from the " .
	"kernel immediately. If your firewall is acting as a standard packet " .
	"filter, this variable should not come into play, but it is helpful on " .
	"connection endpoints such as Web servers. This variable is set at boot " .
	"time to a value appropriate to the amount of memory on your system.",
	"icmp_ratelimit",
	"This variable allows you to limit how frequently specified specified " .
	"ICMP packets are generated. icmp_ratelimit defines how many packets " .
	"that match the icmp_ratemask per jiffie (a unit of time, a 1/100th of a " .
	"second on most architectures) are allowed.",
	"icmp_ratemask",
	"The ratemask is a logical OR of all the ICMP codes you wish to rate " .
	"limit. (See /usr/include/linux/icmp.h for the actual values.) The " .
	"default mask includes destination unreachable, source quench, time " .
	"exceeded and parameter problem. If you increase the limit, you can slow " .
	"down or potentially confuse port scans, but you may inhibit legitimate " .
	"network error indicators.",
	"conf/$iface/log_martians",
	"Have the kernel send syslog messages when packets are received with " .
	"addresses that are illegal.",
	"neigh/$iface/locktime",
	"Reject ARP address changes if the existing entry is less than this many " .
	"jiffies old. If an attacker on your LAN uses ARP poisoning to perform a " .
	"man-in-the-middle attack, raising this variable can prevent ARP cache " .
	"thrashing.",
	"neigh/$iface/gc_stale_time",
	"How often in seconds to clean out old ARP entries and make a new ARP " .
	"request. Lower values will allow the server to more quickly adjust to " .
	"a valid IP migration (good) or an ARP poisoning attack (bad).",
	"conf/$iface/proxy_arp",
	"Reply to ARP requests if we have a route to the host in question. This " .
	"may be necessary in some firewall or VPN/router setups, but is " .
	"generally a bad idea on hosts.",
	"tcp_syncookies",
	"A very popular denial of service attack involves a cracker sending " .
	"many (possibly forged) SYN packets to your server, but never completing " .
	"the TCP three way handshake. This quickly uses up slots in the kernel's " .
	"half open queue, preventing legitimate connections from succeeding. " .
	"Since a connection does not need to be completed, there need be no " .
	"resources used on the attacking machine, so this is easy to perform and " .
	"maintain.  If the tcp_syncookies variable is set (only available if " .
	"your kernel was compiled with CONFIG_SYNCOOKIES) then the kernel " .
	"handles TCP SYN packets normally until the queue is full, at which " .
	"point the SYN cookie functionality kicks in.  SYN cookies work by not " .
	"using a SYN queue at all. Instead the kernel will reply to any SYN " .
	"packet with a SYN|ACK as normal, but it will present a " .
	"specially-crafted TCP sequence number that encodes the source and " .
	"destination IP address and port number and the time the packet was " .
	"sent. An attacker performing the SYN flood would never have gotten " .
	"this packet at all if they're spoofing, so they wouldn't respond. A " .
	"legitimate connection attempt would send the third packet of the three " .
	"way handshake which includes this sequence number, and the server can " .
	"verify that it must be in response to a valid SYN cookie and allows the " .
	"connection, even though there is no corresponding entry in the SYN queue."
);

my $id = `whoami`;
chomp($id);

# Restore backed up settings
if ($restore ne "") {
	print "[-] Restoring backup\n";
	if ($id ne "root") {
		print STDERR "Error: You must be root to restore your settings\n";
		exit(1);
	}
	open(IN, $restore) || die "Error: Cannot open $restore: $!\n";
	foreach my $line ( <IN> ) {
		if ( $line =~ /^([^#\n ]*) -> (\d*)/ ) {
			my $val = $2;
			my $name = $1; $name =~ s/\./\//g;
			print "Restoring /proc/sys/net/ipv4/$name -> $val\n";
			open(OUT, "> /proc/sys/net/ipv4/$name") ||
				die "Error: Cannot open /proc/sys/net/ipv4/$name: $!\n";
			print OUT "$val\n";
			close(OUT);
		}
	}
	close(IN);
	print "[-] Settings restored\n";
	exit(0);
}

print "[-] Loading previous settings\n";
print "[-] Creating backup file\n" if ($backup ne "");


open(BACKUP, "> $backup") if ($backup ne "");
open(OUT, "> .settings.tmp");
while ( my ($key, $value) = each(%files) ) {
	if (-e "/proc/sys/net/ipv4/$key") {
		open(IN, "/proc/sys/net/ipv4/$key");
		my @data = <IN>;
		print BACKUP "$key -> $data[0]" if ($backup ne "");
		my $tmp = $key; $tmp =~ s/\//\./g;
		print OUT "# $value\n\nipv4.$tmp -> $data[0]\n";
		close(IN);
	}
}
close(OUT);
close(BACKUP) if ($backup ne "");

print "Press enter to launch editor...\n";
my $x = <STDIN>;
system("$editor .settings.tmp");
print "Press enter to save settings...\n";
$x = <STDIN>;

if ($id ne "root") {
	print STDERR "Error: You must be root to save your settings\n";
	exit(1);
}

open(IN, ".settings.tmp");
foreach my $line ( <IN> ) {
	if ( $line =~ /^([^#\n ]*) -> (\d*)/ ) {
		my $val = $2;
		my $name = $1; $name =~ s/\./\//g;
		open(OUT, "> /proc/sys/net/$name");
		print OUT "$val\n";
		close(OUT);
	}	
}

print "[-] Settings saved\n";
