Port Knocking Perl Client and ROS Rules (Script Provided)

I wrote this and figured someone else may find it useful. Let me know if you have questions or feedback.

Basically you call it like …

PortKnock.pl -s <HOSTNAME> -k "PORT1:DATA1,PORT2:DATA2,..."'

Below you will find the rules used to make this work as well as the script I use… I have it setup for “2 knocks”, but you can extend this to as many as you want. I’d love to be able to do something more complex as the payload, but given the limitations of the ROS scripting I can’t…

Layer7 Filters

/ip firewall layer7-protocol
add comment="PortKnock #1" name=knock1 regexp=\
    "^DATA1\$"
add comment="PortKnock #2" name=knock2 regexp=\
    "^DATA2\$"

Filter Rules (Currently 2, but you can extend to as many as you want)

/ip firewall filter
add action=add-src-to-address-list address-list=KnockStage1 \
    address-list-timeout=3s chain=input comment="PortKnock #1" dst-port=PORT1 \
    in-interface=ether01-gateway layer7-protocol=knock1 protocol=udp
add action=add-src-to-address-list address-list=KnockStage2 \
    address-list-timeout=10s chain=input comment="PortKnock #2" dst-port=\
    PORT2 in-interface=ether01-gateway layer7-protocol=knock2 protocol=udp \
    src-address-list=KnockStage1
add chain=input comment="PortKnock Allow" in-interface=ether01-gateway \
    src-address-list=KnockStage2

Perl Script

#!/opt/local/bin/perl

use IO::Socket::INET;
use Getopt::Long;

# Flush After Write
$| = 1;

# Get Options
my $SERVER = '';
my $KNOCK_SEQUENCE = '';
my $DELAY = 1;
my $VERBOSE;

GetOptions('server|s=s' => \$SERVER,
		'knock|k=s' => \$KNOCK_SEQUENCE, 
		'delay|d=i' => \$DELAY,
		'verbose|v' => \$VERBOSE) or usage();

if ($SERVER eq '' || $KNOCK_SEQUENCE eq '') {
	print "$SERVER\n$KNOCK_SEQUENCE";
	usage();
}

foreach $knock (split(/,/,$KNOCK_SEQUENCE)) {
	my ($port, $key) = split(/:/, $knock);
	if ($VERBOSE) { 
		print "Knocking (Server = $SERVER, Port = $port, Key = $key, Delay = $DELAY\n";
	}
	knocker($SERVER, $port, $key);
	sleep($DELAY);
}

print "PortKnocked $SERVER\n";

sub knocker {
	my ($server, $port, $data) = @_;

	my $socket = new IO::Socket::INET (
		PeerAddr   	=> $server,
		PeerPort	=> $port,
		Proto       => 'udp'
		) or die "ERROR in Socket Creation : $!\n";

	$socket->send($data);
	$socket->close();
}

sub usage {
	print "Unknown option: @_\n" if ( @_ );
	print "usage: PortKnock.pl --server <IP/HOSTNAME> [--knock <KNOCK SEQUENCE>] [--delay <delay>]\n";
	exit;
}

I’d love to extend this to include some level of encryption or something more advanced in the UDP data… but without some more advanced scripting this isn’t possible…

If we had MD5/SHA1, some sort of OTP algorithm or something else exposed into a script it would be cool.