Monday, November 2, 2015

Detecting torrent clients (uTorrent and Azureus) on your local network with perl and nmap

This week I had some torrent issues at one of my customer's office - torrents consuming the network bandwidth.

Normally you would start to analyze the traffic and enumerate the top consumers, but this was not possible as the network equipment's maintenance was outsourced to a third party and was not possible to tinkle with it - funny thing, the  third party couldn't do anything about these torrents tho.

So I had only my desktop to deal with the issue. Based on the two most popular torrent clients I built a script that detects whether there's an Utorrent or Azureus client - under normal circumstances using high ports. I tried the versions released during the time I was writing this article, not sure if it will work with previous or immediate future versions.

You may want to change the email from and to in bold (email function) and install the additional perl packages using cpan (Email::MIME and LWP::UserAgent).

Syntax is $ perl script.pl <ip or network, nmap format> <port range, nmap format>

#!/usr/bin/perl

use strict;
use warnings;
use WWW::Curl::Easy;
use Email::MIME;
require LWP::UserAgent;


my $ua = LWP::UserAgent->new;
$ua->timeout(10);
$ua->env_proxy;
$ua->agent('Petardo 1.0');

my @strings;
my @targets;
my $text;
my $server="";
my $pointer="";
my $agent="";


my $nmaptarget= $ARGV[0];
my $nmapports= $ARGV[1];
my $tmpfile="/tmp/torrents-Searcher.log";
my $logfile="/tmp/lolo3";
my $nmap="";


if ( ! defined $nmaptarget ) {
        print "Please give me a network to scan ( i.e. 172.172.172.0/24) \n";
        exit (1);
}

if ( ! defined $nmapports ){
        print "You didn't give me a port list to scan (nmap format). Will use 10000-65535\n";
        $nmapports="10000-65535";
}
scan();

open(FILE, "<", $tmpfile) || die "TMP File not found";
my @textlog = <FILE>;
close(FILE);

foreach (@textlog) {

        @strings = split / /;

        my $pointer=1;
        my $server=$strings[$pointer];
        while (defined $strings[$pointer] && $strings[$pointer] !~ /[1-9]{1,3}\.[1-9]{1,3}\.[1-9]{1,3}\.[1-9]{1,3}/ ) {
                $pointer++;
                next;
                }
        $server=$strings[$pointer];

        if (defined $server) {
                $server=~s/\(//;
                $server=~s/\)//;
        }
        $pointer++;

        my $arrSize = @strings;

        while ($pointer < $arrSize) {
                if ($strings[$pointer] ne "" && $strings[$pointer] =~ /[0-9]{1,5}\/tcp/) {
                        $strings[$pointer] =~ s/\/tcp//;
                        findutorrent($server,$strings[$pointer]);
                        findazureus($server,$strings[$pointer]);
                }
                $pointer++;
        }

}

sub findutorrent {
        my $url = "http://" . $_[0] . ":" . "$_[1]" . "/version";
        my $response = $ua->get($url);
        if ($response->is_success) {
                if ( $response->decoded_content =~ /uTorrent/ ) {
                        print $response->decoded_content;
                        print "Utorrent check URL " . $url . " gave code " . $response->code . "\n";
                        print "Found possible utorrent on IP ". $_[0] . " and port " . $_[1] . "\n";
                        emailme($_[0],$_[1],"utorrent");
                }
                else {
                        print "Probe success (" . $response->code . ") but no valid uTorrent response detected on port " . $_[1] . "\n";
                }

         }
         else {
             print "No joy on uTorrent: " . $response->status_line . " on port $_[1] \n";
         }
}

sub findazureus {
        my $url = "http://" . $_[0] . ":" . "$_[1]" . "/service/request1.php?p=789C258CB10EC2300C44FFC5334D44C74A0831302216BA554269E3A61124B1D2A4A022FE1D876EBEF7CEF70152062BAF14B5E1176706A6FD7FBA5BD9D19A5980BD2C129EB39F789A2283D3104C782723F043F5A93E393F594123552964255D46884C6F9910229FD2E2F72C6B8D801E566";
        my $response = $ua->get($url);
        if ($response->is_success) {
                if ( $response->content =~ /[^[:ascii]]/ && length($response->decoded_content ) > 100 && length($response->decoded_content) < 350 && ! $response->decoded_content =~ /SSH/ ) {
                        print $response->content . "\n";
                        print "Lenght: " . length($response->decoded_content) . "\n";
                        print "Azureus check URL " . $url . " gave code " . $response->code . "\n";
                        print "Found possible Azureus on IP ". $_[0] . " and port " . $_[1] . "\n";
                        emailme($_[0],$_[1],"azureus");
                }
                else {
                        print "Probe success (" . $response->code . "," . length($response->decoded_content) . ") but no valid Azureus response detected on port " . $_[1] . "\n";
                }

        }
         else {
             print "No joy on Azureus: " . $response->status_line . " on port $_[1] \n";
         }    
}


sub emailme {

        my $message = Email::MIME->create(
        header_str => [
                From    => 'no.reply@mydomain.com',
                To      => 'andres.martin@mydomain.com',
                Subject => 'torrent client found?',
                ],
        attributes => {
                encoding => 'quoted-printable',
                charset  => 'ISO-8859-1',
                },
        body_str => "Found possible ". $_[2] . " client on IP ". $_[0] . " and port " . $_[1] . "\n",
        );

        # send the message
        use Email::Sender::Simple qw(sendmail);
        sendmail($message);
}

sub scan {

        open (my $fh, '>', $tmpfile) or die "Can't write to file '$tmpfile' $!";
        print "Nmap scanning network " . $nmaptarget . " and ports " . $nmapports . "\n";
        my $nmap = <<`SHELL`;
/usr/bin/nmap -Pn -sT -T5 --open -max-rtt-timeout 50ms --host-timeout 10m -p $nmapports $nmaptarget \n
SHELL
        $nmap =~ s/\n/ /g;
        print $fh $nmap;
        print "Nmap finished scanning\n";
        close $fh
}