PDA

View Full Version : uTorrent [Fake] client detection and TCP checksums


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:mde1i70 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:mde1i5000e1:v15:";
$uTorrentExt .= pack "H*", "c2b5";
$uTorrentExt .= pack "A*", "Torrent 1.7.5e";
# $uTorrentExt .= pack "a3";

return $uTorrentExt;
# = 'd1:ei0e1:mde1i22029e1: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;

dELTA
October 23rd, 2007, 10:03
So, are you saying that your client's session data is identical to the one of the real uTorrent client, except for the TCP checksums? This sounds really strange, considering that ring 3 applications don't have access to the raw IP stack, and thus wouldn't be able to manipulate the TCP checksums itself. I would focus on the parts of the communication that are different otherwise, because the problem most likely lies in there (e.g. that random data stuff, what's that?).

Also, have you captured all communication data up until the point of the identifiction as fake (and the corresponding point in time for the non-fake client), including any possible communication on other sockets/ports? Maybe the identification isn't just based on this inital connection, but rather semi-out of band?

I would focus on these two alternatives in this case.

sabkabox
October 23rd, 2007, 10:59
Hi dELTA, Thanks for replying so quickly!

I'll do what you said and ditch the checksum thing for now and refocus on random and aggregate data Many thanks! Didn't occur to me that my assumed random data may not be random at all Will keep you guys posted though it may/will take some time. I got exams breathing down my neck Made some corrections to my code. PeerID = PeerID1 [10 bytes: -UT-1750-FA91] + PeerID2: [ 10 Bytes generated via unknown algorithm ]

(just for completeness)
http://www.bittorrent.org/protocol.html
http://wiki.theory.org/BitTorrentSpecification
http://www.rasterbar.com/products/libtorrent/extension_protocol.html

wtbw
October 23rd, 2007, 12:59
http://torrentfreak.com/oink-investigation-seeks-identities-and-activities-of-users-071023/

involving the police just because you can't reverse something seems a bit extreme!

dELTA
October 23rd, 2007, 13:01
Thanks for the links, keep us posted.

blurcode
October 24th, 2007, 04:26
Maybe he needs the server to patch it so it will always accept his client as genuine uTorrent

sabkabox
October 24th, 2007, 07:08
Re wtbw: wo! hehe. Still, they can't do much except hassle him. Poor chap, maybe he just has crummy admins.

A small update. Phooey! It worked! I just used a old peerID string. I must have mangled some bit because i tried that stunt before or maybe it's age based and tomorrow it will have borked. Oh well i am content

I still need to add the cheat crud, but Oink is down. If you want the code as it stands PM/mail me, or use that pasted stuff and change to:

sub TalkToClient {
my $handShake = join "",
@dataHash{ qw(strlen str reserved infoHash) };
print unpack("H*", $handShake), "\n";
$handShake .= pack "H*", "2d5554313735302dfa9110708948aaf8e4babf3e";
$handShake .= MakeExtension;

Thanks guys Oo! And check these tools out! They are awesome and should be part of any reversers toolkit: http://tripp.dynalias.org/tripp_docs.txt
Dynamic rewriting of transmitted packets You can also use Perl's Net:ivert module but you have to dance around naked first and then try to install some kernel recompile crap, so i just gave up after the dance routine.

dELTA
October 24th, 2007, 10:13
I'm glad you made it, and thanks for the "tool tips".

LLXX
October 27th, 2007, 00:43
Where's the disassembly?

Quote:
considering that ring 3 applications don't have access to the raw IP stack, and thus wouldn't be able to manipulate the TCP checksums itself.
Actually, "raw sockets" as they are called are quite feasible in both Windows and Linux; otherwise things like packet loggers, IP spoofers, DDoSers, etc. wouldn't work.

@sabkabox: PM me if you want some more information on this. I have a... resource you might find useful.

dELTA
October 27th, 2007, 07:15
Quote:
[Originally Posted by LLXX;69847]Actually, "raw sockets" as they are called are quite feasible in both Windows and Linux; otherwise things like packet loggers, IP spoofers, DDoSers, etc. wouldn't work.
I see, then please enlighten us all about how to get raw socket access on any non-antiquated Windows OS without using a driver?

And yes, DDoSers were indeed one of the major reasons behind Microsoft's decision to remove raw socket access from ring 3 (I think it was in Windows XP SP1 or SP2).

blurcode
October 28th, 2007, 13:03
Btw you can remove the TCP Checksum from your network card options.

LLXX
October 28th, 2007, 14:49
Quote:
[Originally Posted by dELTA;69859]I see, then please enlighten us all about how to get raw socket access on any non-antiquated Windows OS without using a driver?
I do not see any requirement to not use a driver in any of the previous posts here, and that is the way to do it.

dELTA
October 28th, 2007, 17:09
Quote:
[Originally Posted by dELTA]considering that ring 3 applications don't have access to the raw IP stack, and thus wouldn't be able to manipulate the TCP checksums itself.
Quote:
[Originally Posted by LLXX]Actually, "raw sockets" as they are called are quite feasible in both Windows and Linux; otherwise things like packet loggers, IP spoofers, DDoSers, etc. wouldn't work.
Quote:
[Originally Posted by dELTA]I see, then please enlighten us all about how to get raw socket access on any non-antiquated Windows OS without using a driver?
Quote:
[Originally Posted by LLXX]I do not see any requirement to not use a driver in any of the previous posts here, and that is the way to do it.
How about the "ring 3 applications" reference in the original post you commented, I think that pretty much sets this "requirement"? (and yes, it implied that the bittorrent application did not install its own network driver, which it doesn't)

LLXX
October 31st, 2007, 17:51
Windows has to use a driver for your network card, and that is likely how you'd get raw socket functionality -- utilising a driver that is already present.

But in any case, this is a good read:

http://www.codeproject.com/internet/rawsocket.asp

upb
November 1st, 2007, 02:04
Quote:
[Originally Posted by LLXX;69950]Windows has to use a driver for your network card, and that is likely how you'd get raw socket functionality -- utilising a driver that is already present.

But in any case, this is a good read:

http://www.codeproject.com/internet/rawsocket.asp


the network card drivers and raw sockets are on quite different 'layers'

dELTA
November 1st, 2007, 08:37
Quote:
[Originally Posted by upb;69966]the network card drivers and raw sockets are on quite different 'layers'
Indeed they are. But then again, LLXX is just trying to find an easy way out from accusing me of being wrong when I wasn't, so I wouldn't pay too much attention to the actual "factual contents" of her last posts in this thread.

Oh, and anyone who really wants to look into raw sockets access on Windows, check out the WinPcap driver package/library, which is the de facto way to do it.

0rp
November 4th, 2007, 16:45
Quote:
[Originally Posted by dELTA;69859]I see, then please enlighten us all about how to get raw socket access on any non-antiquated Windows OS without using a driver?



int s = socket(SOCK_RAW, ......) ?

prior to winsock 2.0 you needed pcap like ndis drivers to get raw socket functionality but with windows2000+ this requirement is gone

dELTA
November 4th, 2007, 17:32
Yes, except that they removed it again in a Windows XP service pack/hotfix...

http://www.grc.com/dos/intro.htm

disavowed
November 9th, 2007, 14:01
Quote:
[Originally Posted by sabkabox;69738]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.

blurcode alluded to it above, but AFAIK, most NIC cards from the past ~5 years calculate the checksums themselves on-the-fly, after wireshark sees the outgoing packet. that would probably explain the bogus checksums you saw.