#!/bin/perl
#--------------------------------------------------------------------
# https://badtrans.monkeybrains.net/ for more information.
#
# Parse the badtrans keylogger emails.
# http://securityresponse.symantec.com/avcenter/venc/data/w32.badtrans.b@mm.html
# Version 1.0
#
# Grab Subject line from header -- subject line is an identifier for
# the hosts machine. Usually, the users email address.
# Next, rip out passwords and then sort keystroked info.
# Passwords come like this:
# Res: _RESOURCE_, Pas: _PASSWORD_
# Info comes as keystokes from programs in this format:
# Title: "_TITLE_", _TIMESTAMP_\n_MESSAGE_
# Some login thing (currently discarding this info):
# _TIMESTAMP_, Computer: "_COMPUTER_" User: "_USER_"
#
# From the Password lines, there types of data are gleaned:
# 1) website passwords
# 2) Remote Network Access passwords
# 3) WebPost info (used by the Microsoft OS)
#
# Project Methodolgy:
# 1) parse data and store in db.
# 2) classify data (eg title types: email, sms, document, etc)
# 3) create frontend for viewing data
#
# Notes:
# need to update MySQL to use the FULLTEXT text search feature.
#--------------------------------------------------------------------
#--------------------------------------------------------------------
# INSTRUCTIONS:
# To use this software, you need to set the message_separator variable,
# and set up a mysql database to load the parsed data. To use the
# parsed data, you need a frontend for viewing the data, or you need
# to be proficient in command line sql statements. Here is the sql desc:
# CREATE TABLE res (
# id int(9) unsigned DEFAULT '0' NOT NULL auto_increment PRIMARY KEY,
# kind enum('rna','webpost','webpost2','ftp','basicauth') NOT NULL,
# username varchar(64) DEFAULT '' NOT NULL,
# password varchar(64) DEFAULT '' NOT NULL,
# realm varchar(64) DEFAULT '' NOT NULL,
# owner_id int(9) unsigned DEFAULT '0' NOT NULL,
# KEY byRealm (realm),
# KEY byKind (kind)
# );
#
# CREATE TABLE message (
# id int(9) unsigned DEFAULT '0' NOT NULL auto_increment PRIMARY KEY,
# kind enum('document','browser','email','chat','excel','na') NOT NULL,
# title varchar(64) DEFAULT '' NOT NULL,
# body text DEFAULT '' NOT NULL,
# owner_id int(9) unsigned DEFAULT '0' NOT NULL,
# KEY byKind (kind),
# #FULLTEXT (body)
# );
#
# CREATE TABLE owner (
# id int(9) unsigned DEFAULT '0' NOT NULL auto_increment PRIMARY KEY,
# email varchar(64) DEFAULT '' NOT NULL,
# KEY (email)
# );
# Read this link: http://www.mysql.com/doc/F/u/Fulltext_Search.html
# Adding the index after entering the data is faster than having an
# index during the INSERTs.
#--------------------------------------------------------------------
$filename = shift;
if ($filename =~ /\.gz$/) {
open IN, "/usr/bin/gunzip -c $filename |" or die "they say...\nUsage: $0 filename\n";
} else {
open IN, "<$filename" or die "they say...\nUsage: $0 filename\n";
}
#--------------------------------------------------------------------
# every message begins with a spiffy record separator. Set it.
#--------------------------------------------------------------------
$DEBUG = undef;
my $message_separator = 'From suck_my_prick@ijustgotfired.com';
$/ = $message_separator; # We are going to process one email at a time
<IN> eq $message_separator or die "Bad start, I quit!\n";
#--------------------------------------------------------------------
# connect to database and grab handle.
#--------------------------------------------------------------------
use DBI; # pulls in all the code from the DBI module.
my ($dbh, $sql_resource, $sql_message, $sql_owner, $sth_owner_select);
{ my %db = qw (info dbi:mysql:smp:localhost user smp pass TruthSetFree);
$dbh = DBI->connect($db{'info'}, $db{'user'}, $db{'pass'} ) or die "No db connection";
}
$sql_resource = "INSERT into res VALUES (NULL,?,?,?,?,?) ;";
$sql_message = "INSERT into message VALUES (NULL,?,?,?,?) ;";
$sql_owner = "INSERT into owner VALUES (NULL,?) ;";
$sth_owner_select = $dbh->prepare(q{ SELECT id FROM owner WHERE email = ? });
open UNKNOWN_RES,'>unknown_resources.txt' or die "Fark!";
#--------------------------------------------------------------------
# start reading in the STDIN
#--------------------------------------------------------------------
$| = 666;
my $total_messages;
while (<IN>) {
$total_messages++;
$total_messages =~ /00$/o and (print '.');
$total_messages =~ /000$/o and (print 'o');
my ($passwords_found,$subject,$oid);
# id owner - this is not anonymous ;)
/Subject: (.*)/o or do {
print "no Subject line!\n" if $DEBUG;
next;
};
$subject = $1 || 'NA';
#print STDERR "Subject: $subject\n" if $DEBUG;
$sth_owner_select->execute($subject);
$oid = ($sth_owner_select->fetchrow_array())[0] or do {
$dbh->do($sql_owner,undef,($subject));
$sth_owner_select->execute($subject);
$oid = ($sth_owner_select->fetchrow_array())[0] or 0;
};
# grab passwords
my ($webpost_site, $webpost_username, $webpost_password);
while (s/^Res: (.*), Pas: (.*)$//om) {
my ($type, $user, $password, $realm);
my ($res, $pas) = ($1, $2);
($res eq 'MAPI' or $res eq 'NNTP') and next; #useless info?
if ($res =~ /^\*Rna\\([^\\]*)\\(.*)$/o) {
($type, $user, $password, $realm) = ('rna', $2, $pas, $1);
} elsif ($res =~ /^\*WebPost\\(.*)$/o) {
if (/^\*WebPost\\HKCU\\Software\\Microsoft\\WebPost\\Sites\\(.*)\\UserName$/o) {
if ($webpost_site eq $1) {
($type, $user, $password, $realm) =
('webpost', $pas, $webpost_password, $webpost_site);
undef $webpost_site;
} else {
$webpost_site = $1; $webpost_username = $pas;
next;
}
} elsif ($res =~ /^\*WebPost\\HKCU\\Software\\Microsoft\\WebPost\\Sites\\(.*)\\Password$/o) {
if ($webpost_site eq $1) {
($type, $user, $password, $realm) =
('webpost', $webpost_username, $pas, $webpost_site);
undef $webpost_site;
} else {
$webpost_site = $1; $webpost_password = $pas;
next;
}
} else {
($type, $user, $password, $realm) = ('webpost2', undef, $pas, $1);
undef $webpost_site; #probably not needed
}
} elsif ($pas =~ /^(.*):(.*)$/o) {
($1 and $2) or next;
$res =~ s!^http://!!;
($type, $user, $password, $realm) = ('basicauth', $1, $2, $res);
} elsif ($res =~ /^ftp:\/\/(.+)\@(.+)$/o) {
($type, $user, $password, $realm) = ('ftp', $1, $pas, $2);
} else {
print UNKNOWN_RES $&;
next;
}
$user ||= 'NA'; $password ||= 'NA'; $realm ||= 'NA';
$passwords_found++;
$dbh->do($sql_resource,undef,($type, $user, $password, $realm, $oid));
}
#print STDERR "Passwords found: $passwords_found\n" if $DEBUG;
# ignore domain login crap (to ignore it, substitute it OUT of the message.
s/[A-Z][a-z]{2}, \d\d [A-Z][a-z]{2} \d{4} \d\d:\d\d:\d\d, Computer: ".*?" User: ".*?"//og;
# grab the keystroke sniffer stuff.
$_ .= 'Title: "';
while (s/Title: "(.*?)", (\d\d:\d\d:\d\d)(.*?)(Title: ")/$4/os) {
my ($title, $time, $body) = ($1, $2, $3);
$title ||= 'NONE';
$body =~ /\w\w\w/o or next; #skip if body is emptyish
$body =~ s/^\s+//o; #clean
$body =~ s/\s+$//o; #clean
$body =~ s/[\015\012]+/\012/og;
if ($title =~ /Microsoft Word|Notepad/o) {$type = 'document';}
elsif ($title =~ /^(Internet Explorer|Netscape|Mozilla)/o) {$type = 'browser';}
elsif ($title =~ /(America Online|SMS|Messaggio|AOL|Instant Message|Chat|Conversation)/o) {$type = 'chat';}
elsif ($title =~ /^(Fwd?|Re):/o) {$type = 'email';}
elsif ($title =~ /(Outlook|Euroda)/o) {$type = 'email';}
elsif ($title =~ /Microsoft Excel/o) {$type = 'excel';}
else {$type = 'na';}
$dbh->do($sql_message,undef,($type, $title, $body, $oid)) or (print STDERR "$DBI::errstr\n");
}
#print STDERR "\n LEFT OVER CRAP [$_]\n";
}
$sth_owner_select->finish();
$dbh->disconnect();
TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2025 AOH