sabkabox
October 23rd, 2007, 08:10
Brave reversers, hear my sad tale of woe
I am a poor but honest and hardworking little minnow reverser. Recently while trudging wearily through the great Internet forest, i came across a torrent pig farm by the name of Oink. The colours were garish, the text verbose and obscure. Naturally, seeing the soft and clearly lit sign that said "Chat", I clicked and was promptly kicked; out, i might add - Into the cold, cruel air that i had just crawled out off! Never had i been so humiliated and I swore a solemn oath that day, to get even.
My Perl code is below. It works, and the handshake with uTorrent is accepted. Unfortunately there is a slight problem: uTorrent signals my client as a fake uTorrent client. What i want to know is how the heck do they figure that out.
Tcpdump output taken via tcpdump -X -s 0 -i lo -w dumpFile1
http://pastebin.com/m5e4cd280
Just grep for BitTorrent. That would be the handshake packets. There are data strings that go like so: ei0e1:mde1
i70 That would be the extension protocol packets. The extension protocol data can also be embedded within the Handshake itself.
My fake-client is running on 127.0.0.1:5000 and the genuine clients (2 of them) are running on 192.168.0.1:7000 and the other port is irrelevant.
I also used Wireshark to study the dump and the only difference to my untrained eye is the TCP checksum which is NOT correct in ALL the uTorrent handshake packets. Only some pure ack packets (not SYN ACK) have correct checksums. This is signalled as a error by Wireshark but my faker-clients handshakes have no such error.
Of course it would be easier to abandon uTorrent and fake Azureus
but that is less fun 
-------------------------------------------------------------------------------------------------
#! /usr/bin/perl -w
use strict;
use Socket;
use constant LSRV_TCP_PORT => 5000;
use constant MAX_RECV_LEN => 65536;
my %dataHash = {
'strLen' => 19,
'str' => 'BitTorrent protocol',
'reserved' => '00000000',
'infoHash' => 'xxx',
'peerID1' => 'xxx',
'peerID2' => 'xxx',
'extension' => 'xxx', };
sub RandomString( $ ) {
my ($lenOfRanStr) = @_;
my @chars=('a'..'z','A'..'Z','0'..'9','_');
my $ranStr;
foreach (1..$lenOfRanStr) {
# rand @chars will generate a random
# number between 0 and scalar @chars
$ranStr .= $chars[rand @chars];
}
return $ranStr;
}
sub SetupSocket {
my $lPort = shift || LSRV_TCP_PORT;
my $lAddr = sockaddr_in($lPort, INADDR_ANY);
my $proto = getprotobyname('tcp');
socket(TCP_SOCK, PF_INET, SOCK_STREAM, $proto)
or die "tcp_s1: socket creation failed: $!\n";
setsockopt(TCP_SOCK, SOL_SOCKET, SO_REUSEADDR, 1)
or warn "tcp_s1: could not set socket option: $!\n";
bind(TCP_SOCK, $lAddr)
or die "tcp_s1: bind to address failed: $!\n";
listen(TCP_SOCK, SOMAXCONN)
or die "tcp_s1: listen couldn't: $!\n";
print "Server starting up on port: $lPort.\n";
}
sub UnpackData( $ ) {
my ($data) = @_;
@dataHash{ qw(strlen str reserved infoHash peerID1 peerID2 extension) } =
unpack "a a19 a8 a20 a10 a10 a*", $data;
}
sub PrintHash {
print unpack("C*", $dataHash{strlen} ), "\n";
print unpack("a*", $dataHash{str} ), "\n";
print unpack("C*", $dataHash{reserved} ), "\n";
print unpack("H40", $dataHash{infoHash} ), "\n";
print unpack("A8H*", $dataHash{peerID1} ), "\n";
print unpack("H*", $dataHash{peerID2} ), "\n";
print unpack("H*", $dataHash{extension}), "\n";
}
sub MakeExtension {
my $uTorrentExt;
$uTorrentExt = pack "a3";
# Size, from 1400 to 000
$uTorrentExt .= pack "H*", "2d";
# Decimal 20
$uTorrentExt .= pack "H*", "1400";
$uTorrentExt .= pack "A*", "d1:ei0e1:mde1
i5000e1:v15:";
$uTorrentExt .= pack "H*", "c2b5";
$uTorrentExt .= pack "A*", "Torrent 1.7.5e";
# $uTorrentExt .= pack "a3";
return $uTorrentExt;
# = 'd1:ei0e1:mde1
i22029e1:v15:'
}
sub TalkToClient {
my $handShake = join "",
@dataHash{ qw(strlen str reserved infoHash peerID1) };
print unpack("H*", $handShake), "\n";
# my $peerID2 = pack "A*", "x6kBPUAD4ugG";
my $peerID2 = RandomString(10);
$handShake .= $peerID2;
$handShake .= MakeExtension;
send( CLIENT_SOCK, $handShake, 0 )
or print "problem with send: $!\n";
my $extension = pack "H*", "00000003091b58";
send( CLIENT_SOCK, $extension, 0 )
or print "problem with send: $!\n";
# my $handShake = ."EvilHacker12";
# $cReserved = pack "a8";
}
sub RWSocket {
my ($fromWho, $data);
SetupSocket;
while ($fromWho=accept(CLIENT_SOCK, TCP_SOCK)) {
$fromWho =
recv(CLIENT_SOCK, $data, MAX_RECV_LEN, 0);
if ($fromWho eq ""
{
UnpackData($data);
PrintHash;
} else {
print "problem with recv: $!\n";
next;
}
print "Sending data to client... \n";
TalkToClient;
} continue {
# close CLIENT_SOCK
# or print "close failed: $!\n";
}
#close TCP_SOCK;
}
RWSocket;


My Perl code is below. It works, and the handshake with uTorrent is accepted. Unfortunately there is a slight problem: uTorrent signals my client as a fake uTorrent client. What i want to know is how the heck do they figure that out.
Tcpdump output taken via tcpdump -X -s 0 -i lo -w dumpFile1
http://pastebin.com/m5e4cd280
Just grep for BitTorrent. That would be the handshake packets. There are data strings that go like so: ei0e1:mde1

My fake-client is running on 127.0.0.1:5000 and the genuine clients (2 of them) are running on 192.168.0.1:7000 and the other port is irrelevant.
I also used Wireshark to study the dump and the only difference to my untrained eye is the TCP checksum which is NOT correct in ALL the uTorrent handshake packets. Only some pure ack packets (not SYN ACK) have correct checksums. This is signalled as a error by Wireshark but my faker-clients handshakes have no such error.
Of course it would be easier to abandon uTorrent and fake Azureus


-------------------------------------------------------------------------------------------------
#! /usr/bin/perl -w
use strict;
use Socket;
use constant LSRV_TCP_PORT => 5000;
use constant MAX_RECV_LEN => 65536;
my %dataHash = {
'strLen' => 19,
'str' => 'BitTorrent protocol',
'reserved' => '00000000',
'infoHash' => 'xxx',
'peerID1' => 'xxx',
'peerID2' => 'xxx',
'extension' => 'xxx', };
sub RandomString( $ ) {
my ($lenOfRanStr) = @_;
my @chars=('a'..'z','A'..'Z','0'..'9','_');
my $ranStr;
foreach (1..$lenOfRanStr) {
# rand @chars will generate a random
# number between 0 and scalar @chars
$ranStr .= $chars[rand @chars];
}
return $ranStr;
}
sub SetupSocket {
my $lPort = shift || LSRV_TCP_PORT;
my $lAddr = sockaddr_in($lPort, INADDR_ANY);
my $proto = getprotobyname('tcp');
socket(TCP_SOCK, PF_INET, SOCK_STREAM, $proto)
or die "tcp_s1: socket creation failed: $!\n";
setsockopt(TCP_SOCK, SOL_SOCKET, SO_REUSEADDR, 1)
or warn "tcp_s1: could not set socket option: $!\n";
bind(TCP_SOCK, $lAddr)
or die "tcp_s1: bind to address failed: $!\n";
listen(TCP_SOCK, SOMAXCONN)
or die "tcp_s1: listen couldn't: $!\n";
print "Server starting up on port: $lPort.\n";
}
sub UnpackData( $ ) {
my ($data) = @_;
@dataHash{ qw(strlen str reserved infoHash peerID1 peerID2 extension) } =
unpack "a a19 a8 a20 a10 a10 a*", $data;
}
sub PrintHash {
print unpack("C*", $dataHash{strlen} ), "\n";
print unpack("a*", $dataHash{str} ), "\n";
print unpack("C*", $dataHash{reserved} ), "\n";
print unpack("H40", $dataHash{infoHash} ), "\n";
print unpack("A8H*", $dataHash{peerID1} ), "\n";
print unpack("H*", $dataHash{peerID2} ), "\n";
print unpack("H*", $dataHash{extension}), "\n";
}
sub MakeExtension {
my $uTorrentExt;
$uTorrentExt = pack "a3";
# Size, from 1400 to 000
$uTorrentExt .= pack "H*", "2d";
# Decimal 20
$uTorrentExt .= pack "H*", "1400";
$uTorrentExt .= pack "A*", "d1:ei0e1:mde1

$uTorrentExt .= pack "H*", "c2b5";
$uTorrentExt .= pack "A*", "Torrent 1.7.5e";
# $uTorrentExt .= pack "a3";
return $uTorrentExt;
# = 'd1:ei0e1:mde1

}
sub TalkToClient {
my $handShake = join "",
@dataHash{ qw(strlen str reserved infoHash peerID1) };
print unpack("H*", $handShake), "\n";
# my $peerID2 = pack "A*", "x6kBPUAD4ugG";
my $peerID2 = RandomString(10);
$handShake .= $peerID2;
$handShake .= MakeExtension;
send( CLIENT_SOCK, $handShake, 0 )
or print "problem with send: $!\n";
my $extension = pack "H*", "00000003091b58";
send( CLIENT_SOCK, $extension, 0 )
or print "problem with send: $!\n";
# my $handShake = ."EvilHacker12";
# $cReserved = pack "a8";
}
sub RWSocket {
my ($fromWho, $data);
SetupSocket;
while ($fromWho=accept(CLIENT_SOCK, TCP_SOCK)) {
$fromWho =
recv(CLIENT_SOCK, $data, MAX_RECV_LEN, 0);
if ($fromWho eq ""

UnpackData($data);
PrintHash;
} else {
print "problem with recv: $!\n";
next;
}
print "Sending data to client... \n";
TalkToClient;
} continue {
# close CLIENT_SOCK
# or print "close failed: $!\n";
}
#close TCP_SOCK;
}
RWSocket;