Information Security News mailing list archives

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


From: InfoSec News <isn () c4i org>
Date: Wed, 6 Aug 2003 02:27:23 -0500 (CDT)

+------------------------------------------------------------------+
|  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 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: