Information Security News mailing list archives

Running programs in response to sniffed DNS packets - stealthily managing iptables rules remotely, Part 2


From: InfoSec News <isn () c4i org>
Date: Fri, 15 Aug 2003 02:59:01 -0500 (CDT)

+------------------------------------------------------------------+
|  Linux Security: Tips, Tricks, and Hackery                       |
|  Published by Onsight, Inc.                                      |
|                                                                  |
|  14-August-2003                                                  |
|  http://www.hackinglinuxexposed.com/articles/20030814.html       |
+------------------------------------------------------------------+

This issue sponsored by Onsight, your Security Solution.

Onsight, Inc is a consulting firm specializing in network security.
Our network consultants have extensive experience in host security
for all Unix flavours, from Solaris and *BSD to HP-UX and Irix. We
can provide custom firewall solutions, Intrusion Detection systems,
assist in log monitoring and analysis, and clean up your systems
after successful cracker intrusions.

Of course, our speciality is Linux security and systems - we wrote
the book on it: Hacking Linux Exposed. If you want to protect your
Unix network from crackers, you want to talk to us.

For more information, see http://www.onsight.com or email 
security () onsight com

--------------------------------------------------------------------

Running programs in response to sniffed DNS packets - stealthily
managing iptables rules remotely, Part 2
By Brian Hatch

Summary: In combination to the watch_dns program which uses Net::Pcap
to sniff DNS packets, we complete our stealthy remote execution
setup.

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.[1]

  # 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 (<WATCH_DNS>) {
    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_cmd\n" 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 $command\n" 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 week 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...

NOTES:

[1] In the script we created two weeks ago, it could go any place
before the DROP rule is called.

                            -------------                            
Brian Hatch is Chief Hacker at Onsight, Inc and author of Hacking
Linux Exposed and Building Linux VPNs. He loves to create
functionality from horrible bastardizations of existing protocols.
Brian can be reached at brian () hackinglinuxexposed com.

--------------------------------------------------------------------
This newsletter is distributed by Onsight, Inc.

The list is managed with MailMan (http://www.list.org). You can
subscribe, unsubscribe, or change your password by visiting
http://lists.onsight.com/ or by sending email to
linux_security-request () lists onsight com.

Archives of this and previous newsletters are available at
http://www.hackinglinuxexposed.com/articles/

--------------------------------------------------------------------

Copyright 2003, Brian Hatch.



-
ISN is currently hosted by Attrition.org

To unsubscribe email majordomo () attrition org with 'unsubscribe isn'
in the BODY of the mail.


Current thread: