[ISN] Sniffing with Net::Cap to stealthily managing iptables rules remotely, Part 1

From: InfoSec News (isnat_private)
Date: Wed Aug 06 2003 - 00:27:23 PDT

  • Next message: InfoSec News: "[ISN] Robot 'guard dog' protects Wi-Fi setups"

    +------------------------------------------------------------------+
    |  Linux Security: Tips, Tricks, and Hackery                       |
    |  Published by Onsight, Inc.                                      |
    |                                                                  |
    |  30-July-2003                                                    |
    |  http://www.hackinglinuxexposed.com/articles/20030730.html       |
    +------------------------------------------------------------------+
    
    This issue sponsored by Open Source Web Development with LAMP
    
    OSWD w/ LAMP by James Lee and Brent Ware presents a comprehensive
    overview of LAMP technologies - Linux, Apache, MySQL, Perl, PHP, WML,
    Embperl, and Mason - and allows the reader to decide which tool may
    be appropriate for the particular task at hand. It focuses on the
    most important core material necessary so that developers can "hit
    the ground running" and begin buliding applications right away, while
    improving reliability and dramatically cutting costs.
    
    For reviews, sample chapters, or to order, go to
    www.opensourcewebbook.com
    
    --------------------------------------------------------------------
    
    Sniffing with Net::Cap to stealthily managing iptables rules
    remotely, Part 1
    By Brian Hatch
    
    Summary: Net::Pcap allows you to process captured network packets and
    set up routines to process them in any mode imaginable.
    
    In our saga that began several weeks ago, we're trying to create a
    firewall setup that allows no inbound access by default that can be
    modified remotely to allow a small window of inbound SSH
    connectivity. Remember that this machine must have no inbound TCP
    ports accessible to pass muster with the Windows-biased IT
    administrators, yet we want to allow inbound SSH dynamically when
    needed.[1]
    
    So the trick was to find a way to dynamically allow inbound SSH
    access from 'authorized' machines. Since the machines he was going to
    be connecting from were Windows machines with almost no useful
    software[2] it was a bit of a trick to find something simple.
    
    Using our 10 minute firewall setup, we had already effectively
    blocked inbound SSH because the initial SYN packet would always be
    discarded.[3] What we needed was a simple way to allow those inbound
    SYN packets for a short window.
    
    I came up with the idea of using DNS queries to 'authorise' a client
    machine brief access to the SSH server. Recent Windows machines have
    a nslookup client by default, as do Mac OS X machines, so this seemed
    most portable. In order to open up the firewall for SSH access, you'd
    run request a specific DNS record from his machine. If the machine
    saw this packet, it would create a temporary rule allowing inbound
    SSH.
    
    I didn't want to write a full blown DNS-like server or anything for
    two reasons. First, it's a lot of wasted time for such a simple need.
    Secondly, it would have shown another open port when the IT folks
    scanned the host, and that would have raised their suspicions - why
    would an end host be running a DNS server, after all?
    
    Instead, the easiest plan seemed to be to run a sniffer on his
    machine which would see the DNS request and inform a second program
    of what it sees. This second program (to be covered next time) will
    handle opening the temporary inbound SSH access.
    
    Because I'm extremely lazy, I wrote my sniffer using Perl's Net::Pcap
    module. This module uses the standard libpcap code that is part of
    tcpdump and other packet sniffers. The way you use this library in
    Perl is extremely similar to the way you do it in C, naturally. The
    Net::Pcap man page can help if the code below isn't sufficiently
    commented for your tastes.
    
    The code does the following things:
    
      * Open up a capturing device
      * Drop root privileges for security reasons
      * Set up our filter to only snag DNS packets
      * Whenever a packet is received, call the process_pkt routine,
        which
          + Extracts the IP address that sent the request
          + Extracts the destination IP address
          + Extracts the DNS host name that was requested
          + Prints these values to STDOUT
    
    This program doesn't do anything to the firewall rules at all, it
    simply writes data to it's standard output. The intent is that a
    second program, running as root, will analyse this output and create
    the appropriate rules. Separation of functionality is a good thing
    when it comes to security, of course.
    
    Here's the code:
    
    
    #!/usr/bin/perl -w
    #
    # Copyright 2003, Brian Hatch, released under the GPL
    #
    # watch_dns:
    #   A program to watch for inbound DNS queries, and print the
    #   source, destination, and requested domain name of the queries.
    
    # You'll need to fill this in with your actual IP address
    # (If we didn't restrict the destination IP address, we'd
    # catch all our outbound queries too.)
    my $MY_IP_ADDRESS='10.1.1.1';
    
    # The unprivileged uid/gid under which we should run.
    my $UNPRIV="200";
    
    
    
    # No changes required hereafter
    
    use Net::Pcap;
    use FileHandle;
    use strict;
    use English;    # for example purposes only - I prefer obfuscated code.
    
    STDOUT->autoflush(1);
    
    while ( 1 ) {
    
        my $pid = fork();
        if ( ! defined $pid ) { die "Unable to fork.  Yikes." };
    
        if ( $pid ) {
            # Parent process (running as root) will wait for
            # child.  If child exits, we'll create another one.
            wait(); 
            sleep(1);  # To keep us from respawning too fast if necessary.
        } else {
            print "Child starting\n";
    
            # Child process will do actual sniffing.
            # First, create our packet capturing device
            my($pcap_t) = create_pcap();
    
            unless ( $pcap_t ) {
                die "Unable to create pcap";
            }
    
            # Let's stop running as root.  Since we already
            # have our pcap descriptor, we can still use it.
            $EGID="$UNPRIV $UNPRIV";        # setgid and setgroups()
            $GID=$UNPRIV;
            $UID=$UNPRIV; $EUID=$UNPRIV;
    
            # Capture packets forever.
            Net::Pcap::loop($pcap_t, -1, \&process_pkt, 0);
    
            # Technically, we shouldn't get here since the loop
            # is infinite (-1), but just in case, close and exit.
            Net::Pcap::close($pcap_t);
            exit 1;
        }
    }
    
    sub create_pcap {
        my $promisc = 0;   # We're only looking for packets destined to us,
                           # so no need for promiscuous mode.
        my $snaplen = 135; # Allows a max of 80 characters in the domain name
    
        my $to_ms = 0;                      # timeout
        my $opt=1;                          # Sure, optimisation is good...
        my($err,$net,$mask,$dev,$filter_t);
    
        my $filter = "udp dst port 53 and dst host $MY_IP_ADDRESS";
    
        # Look up an appropriate device (eth0 usually)
        $dev = Net::Pcap::lookupdev(\$err);
        $dev or die "Net::Pcap::lookupdev failed.  Error was $err";
        
        if ( (Net::Pcap::lookupnet($dev, \$net, \$mask, \$err) ) == -1 ) {
            die "Net::Pcap::lookupnet failed.  Error was $err";
        }
        
        # Actually open up our descriptor
        my $pcap_t = Net::Pcap::open_live($dev, $snaplen, $promisc, $to_ms, \$err);
        $pcap_t || die "Can't create packet descriptor.  Error was $err";
        
        if ( Net::Pcap::compile($pcap_t, \$filter_t, $filter, $opt, $net) == -1 ) {
            die "Unable to compile filter string '$filter'\n";
        }
    
        # Make sure our sniffer only captures those bytes we want in
        # our filter.
        Net::Pcap::setfilter($pcap_t, $filter_t);
    
        # Return our pcap descriptor
        $pcap_t;
    }
    
    # Routine to process the packet -- called by Net::Pcap::loop()
    # every time an appropriate packet is snagged.
    sub process_pkt {
        my($user_data, $hdr, $pkt) = @_;
    
        my($src_ip) = 26;           # start of the source IP in the packet
        my($dst_ip) = 30;           # start of the dest IP in the packet
        my($domain_start) = 55;     # start of the domain in the packet
        my($data);
    
        # extract the source IP addr into dotted quad form.
        my($source) = sprintf("%d.%d.%d.%d",
            ord( substr($pkt, $src_ip, 1) ),
            ord( substr($pkt, $src_ip+1, 1) ),
            ord( substr($pkt, $src_ip+2, 1) ),
            ord( substr($pkt, $src_ip+3, 1) ));
    
        # extract the destination IP addr into dotted quad form.
        my($destination) = sprintf("%d.%d.%d.%d",
            ord( substr($pkt, $dst_ip, 1) ),
            ord( substr($pkt, $dst_ip+1, 1) ),
            ord( substr($pkt, $dst_ip+2, 1) ),
            ord( substr($pkt, $dst_ip+3, 1) ));
    
        $data = substr($pkt, $domain_start);
    
        $data =~ s/\00.*//g;             # strip off everything after the domain
        $data =~ s/[^-a-zA-Z0-9]/./g;    # change the domain component separators
                                         # back int to dots.
    
        print "$source -> $destination: $data\n"
            if ( $source and $destination and $data);  
    }
    
    
    As you can see, Net::Pcap is very easy to use. Since the actual
    packet capture code is written in C, it's very fast. However the
    actual packet processing is in Perl, so if you have huge numbers of
    packets and your script can't keep up, you may need to write your
    programs using libpcap in C natively. But for simple needs, Net::Pcap
    is your friend. The corresponding C code for the above program would
    probably take three times as many lines. God, I love Perl.
    
    While I was writing this code back in May[4], Linux Journal published
    an article (available at http://www.linuxjournal.com/article.php?sid=
    6811) by Martin Krzywinski, which describes a technique called Port
    Knocking. His method is much more robust, allowing actual encryption
    and authentication. It requires that you have the port knocking
    client software, which makes it less appealing for situations where
    you aren't able to install software on the client machine, or you're
    stuck on a client as unfriendly as a Windows box....
    
    NOTES:
    
    [1] Of course, this sort of trickery will piss off those network
    administrators if they figure it out...
    
    [2] Putty was already installed on most machines, but beyond that, he
    was stuck with whatever was already available
    
    [3] You could tell that an SSH server was running using Nmap, but you
    could not actually SSH to the machine. This was sufficient to the IT
    folks.
    
    [4] Yes, sometimes I do write articles before the deadline
    
                                -------------                            
    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 brianat_private
    
    --------------------------------------------------------------------
    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-requestat_private
    
    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 majordomoat_private with 'unsubscribe isn'
    in the BODY of the mail.
    



    This archive was generated by hypermail 2b30 : Wed Aug 06 2003 - 03:02:16 PDT