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