Running Programs In Response To Sniffed DNS Packets - Stealthily Managing Iptables Rules Remotely, Part 2
By Brian Hatch
Expert Author
Article Date: 2004-01-26
Last time we set up a Perl script that would use the Net::Pcap module to sniff the network and print information about DNS requests to standard output. The output looks like this
sourceipaddr -> destipaddr: dnshostname
The handle_remote_dns_commands program below will run watch_dns and act on the output. This program could be modified to suit any purpose. Currently it is set up to manipulate a new iptables chain called allow_in which is called before packets are DROPped. Remember, this chain must be created with the following command at a point in your iptables setup before inbound SYN packets are DROPped.
# iptables --new-chain allow_in
# iptables -A INPUT -j allow_in
The code below is pretty well commented, so rather than breaking it up to discuss the components, just read the comments. After all, code is speech.
#!/usr/bin/perl
#
# handle_remote_dns_commands
#
# Copyright 2003, Brian Hatch, released under the GPL.
#
# This program allows you to run commands based on DNS requests that are
# sniffed by the 'watch_dns' command described at
# http://www.hackinglinuxexposed.com/articles/20030730.html
#
# It reads lines from the watch_dns DNS sniffer which are of the form
#
# sourceipaddr -> destipaddr: dnshostname
#
# The actual commands rules that are run are found by looking up the
# dnshostname in the %mapping hash. The command to be run (the
# associated hash value) is an anonymous array. The array values
# are analysed, and if the string SSSSSSSS or DDDDDDDD is present
# it is replaced by the source or destination IP address, respectively.
#
# While the current example is for maintaining an iptables chain,
# there's no reason you couldn't use it to run any other commands, such
# as triggering an outbound SSH connection with remote forward back into
# the machine itself, etc. Use your imagination.
#
# BUGS
#
# The script doesn't actually verify an IP address is valid - ie it should
# reject "999.888.777.666" because it's outside the valid range. The
# command you call should be able to deal appropriately with invalid IP
# addresses.
#
# Since most host/nslookup/dig tools will retry DNS queries if no
# response is received, you may end up calling the command multiple
# times. For iptables rules, this isn't terribly important.
#
# Using SIGALRM isn't the most brilliant method to periodically call our
# periodic commands. As written, if a cracker knows valid commands, he
# can spew DNS commands that will cause them to be run very quickly. In
# the case of iptables cleaning, this would make it difficult for a
# legitimate connection to get in. However if the SIGALRM were written
# to always cause an X second timer without the fancy footwork, then
# said cracker could cause our chain to grow infinitely long, eventually
# maxing it out and causing a DoS, because the chain would never be
# cleared. The code used is probably best, since it will fail closed.
#
# There is obviously no authentication or encryption whatsoever. If
# someone gleans your magical commands, they can cause the associated
# commands to be run.
use strict ;
# If $PERIODIC_SECONDS nonzero, then every (this many) seconds we should
# run the commands stored in the @periodic_commands array.
my $PERIODIC_SECONDS = 10;
# Location of watch_dns command (will be run as our UID which must be
# root since it's sniffing. If you are running this script as a normal
# user, you'll want to run watch_dns via sudo.)
my $WATCH_DNS="/opt/bin/watch_dns";
# Should debug and warning messages be written to STDOUT?
my $DEBUG=0;
my $WARNING=0;
####
# Modify this section to include your actual functionality.
# Local variables we may need
my $IPTABLES="/sbin/iptables";
# Which chain should we enter the temporary access rules?
my $CHAIN="allow_in";
my @periodic_commands = (
[ $IPTABLES, '-F', $CHAIN ],
);
# The actual rules to run when incoming DNS hostnames match.
#
my %mapping = (
openssh =>
[ $IPTABLES, '-A', $CHAIN,
qw( -p tcp --source SSSSSSSS/32 --dport 22 -j ACCEPT ) ],
# Other examples.
#
#opensmtp =>
# [ $IPTABLES, '-A', $CHAIN,
# qw( -p tcp --source SSSSSSSS/32 --dport 25 -j ACCEPT ) ],
#
#allow-all =>
# [ $IPTABLES, '-A', $CHAIN, qw( -p tcp -j ACCEPT ) ],
#
#flush.subdomain.example.com =>
# [ $IPTABLES, '-A", $CHAIN, '-F' ],
);
# End of purpose-specific modifications.
####
# No more changes needed hereafter.
if ( $PERIODIC_SECONDS ) {
# Set up an alarm signal handler. When an alarm is received,
# flush the iptables exceptions table, denying everyone again.
$SIG{ALRM} = &run_periodic_commands;
}
# Start our pcap sniffer, and snag it's output.
open WATCH_DNS, "$WATCH_DNS |" or die "Can't start watch_dns";
# Loop indefinitely
while () {
my $iptables_cmd;
my @iptables_cmd;
# Let's be very paranoid about checking our input.
# Both the source and destination IP addresses we
# get could be forged by an attacker. Let's at least
# make sure that they're in the correct form.
my ($src, $dst, $command) =
/^ (d+ . d+ . d+ . d+) # first IP address
s* -> s* # arrow
(d+ . d+ . d+ . d+) # second IP address
: s* (S+) # command (hostname)
/x;
unless ( $command ) { # ignore bad input.
print "ignoring $_" if $DEBUG;
next;
}
if ( @iptables_cmd = @{$mapping{$command}} ) {
map { s/DDDDDDDD/$dst/g } @iptables_cmd;
map { s/SSSSSSSS/$src/g } @iptables_cmd;
print "Running @iptables_cmdn" if $DEBUG;
# System used with a list doesn't invoke a shell.
system @iptables_cmd;
if ( $PERIODIC_SECONDS ) {
# Set the alarm
my $oldtimer = alarm($PERIODIC_SECONDS);
if ( $oldtimer == 1 ) {
&{$SIG{ALRM}}; # Call immediately
} elsif ( $oldtimer ) {
alarm(--$oldtimer); # Decrement alarm
}
}
} else {
print "ignoring $commandn" if $WARNING;
}
}
sub run_periodic_commands {
print "run_periodic_commands called.n" if $DEBUG;
# System used with a list doesn't invoke a shell.
for my $command_arrayref ( @periodic_commands ) {
system @$command_arrayref;
}
}Ok, next time I'll show you how to send the DNS query to the sniffer so we can wrap this whole thing up. The end is near...
About the Author: Brian Hatch is Chief Hacker at Onsight, Inc and author of
Hacking Linux Exposed
and Building Linux VPNs.
Brian can be reached at brian@hackinglinuxexposed.com.
|