| 1 | #!/usr/bin/perl |
|---|
| 2 | use strict; |
|---|
| 3 | |
|---|
| 4 | my $target = shift; |
|---|
| 5 | die "Usage: $0 host\n" unless $target; |
|---|
| 6 | |
|---|
| 7 | my $scanner = VNCScanner->new; |
|---|
| 8 | $scanner->scan($target); |
|---|
| 9 | |
|---|
| 10 | package VNCScanner; |
|---|
| 11 | use IO::Socket::INET; |
|---|
| 12 | |
|---|
| 13 | sub new { |
|---|
| 14 | my ($class) = @_; |
|---|
| 15 | |
|---|
| 16 | my $self = {}; |
|---|
| 17 | return bless $self, $class; |
|---|
| 18 | } |
|---|
| 19 | |
|---|
| 20 | *socket = \&sock; |
|---|
| 21 | sub sock { $_[0]->{sock} } |
|---|
| 22 | |
|---|
| 23 | sub scan { |
|---|
| 24 | my ($self, $host) = @_; |
|---|
| 25 | |
|---|
| 26 | my $socket = IO::Socket::INET->new( |
|---|
| 27 | PeerAddr => $host, |
|---|
| 28 | PeerPort => '5900', |
|---|
| 29 | Proto => 'tcp', |
|---|
| 30 | ) or warn "Error connecting to $host: $!\n"; |
|---|
| 31 | |
|---|
| 32 | if ($socket && $socket->connected) { |
|---|
| 33 | $self->{sock} = $socket; |
|---|
| 34 | $self->connect; |
|---|
| 35 | } |
|---|
| 36 | } |
|---|
| 37 | |
|---|
| 38 | sub connect { |
|---|
| 39 | my $self = shift; |
|---|
| 40 | |
|---|
| 41 | my $socket = $self->sock; |
|---|
| 42 | |
|---|
| 43 | # read protocol version |
|---|
| 44 | my $proto_v; |
|---|
| 45 | $socket->read($proto_v, 12); |
|---|
| 46 | $proto_v ||= ''; |
|---|
| 47 | chomp $proto_v; |
|---|
| 48 | |
|---|
| 49 | my ($maj_v, $min_v) = $proto_v =~ /RFB (\d+)\.(\d+)/; |
|---|
| 50 | $self->{maj_v} = $maj_v; |
|---|
| 51 | $self->{min_v} = $min_v; |
|---|
| 52 | |
|---|
| 53 | $self->log("Found RFB server ($maj_v.$min_v)"); |
|---|
| 54 | |
|---|
| 55 | if ($maj_v == 3) { |
|---|
| 56 | $self->auth; |
|---|
| 57 | } elsif ($maj_v) { |
|---|
| 58 | $self->log("Unknown RFB version $maj_v"); |
|---|
| 59 | } else { |
|---|
| 60 | $self->log("Unknown RFB version response: $proto_v"); |
|---|
| 61 | } |
|---|
| 62 | } |
|---|
| 63 | |
|---|
| 64 | sub auth { |
|---|
| 65 | my $self = shift; |
|---|
| 66 | my $sock = $self->sock; |
|---|
| 67 | |
|---|
| 68 | if ($self->{min_v} >= 7) { |
|---|
| 69 | # do v. 7 auth |
|---|
| 70 | $sock->print("RFB 003.007\n"); |
|---|
| 71 | |
|---|
| 72 | # should receive 1 byte count of auth types |
|---|
| 73 | my $sec_t_cnt; |
|---|
| 74 | $sock->read($sec_t_cnt, 1); |
|---|
| 75 | |
|---|
| 76 | if ($sec_t_cnt) { |
|---|
| 77 | # read array of auth types |
|---|
| 78 | my $sec_t_array; |
|---|
| 79 | $sock->read($sec_t_array, int($sec_t_cnt)); |
|---|
| 80 | |
|---|
| 81 | # try auth bypass |
|---|
| 82 | $sock->print(pack('C', 0x01)); # auth type none |
|---|
| 83 | $self->check_auth_resp; |
|---|
| 84 | } else { |
|---|
| 85 | my $err_str_len; |
|---|
| 86 | $sock->read($err_str_len, 4); |
|---|
| 87 | $err_str_len = unpack('N', $err_str_len) + 0; |
|---|
| 88 | |
|---|
| 89 | my $err_str; |
|---|
| 90 | $sock->read($err_str, $err_str_len); |
|---|
| 91 | $self->log("Got authentication error: $err_str"); |
|---|
| 92 | } |
|---|
| 93 | } else { |
|---|
| 94 | # request version 3.3 auth |
|---|
| 95 | $sock->print("RFB 003.003\n"); |
|---|
| 96 | |
|---|
| 97 | # should get a type back |
|---|
| 98 | my $sec_t; |
|---|
| 99 | $sock->read($sec_t, 4); |
|---|
| 100 | $sec_t = unpack('N', $sec_t); |
|---|
| 101 | |
|---|
| 102 | unless ($sec_t) { |
|---|
| 103 | $self->log("Auth rejected"); |
|---|
| 104 | return; |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | if ($sec_t == 1) { |
|---|
| 108 | # no auth, yay |
|---|
| 109 | $self->log("No auth required!"); |
|---|
| 110 | } else { |
|---|
| 111 | $self->log("Auth type $sec_t requested. Giving up."); |
|---|
| 112 | } |
|---|
| 113 | } |
|---|
| 114 | } |
|---|
| 115 | |
|---|
| 116 | sub check_auth_resp { |
|---|
| 117 | my $self = shift; |
|---|
| 118 | my $sock = $self->sock; |
|---|
| 119 | |
|---|
| 120 | # read securityresult |
|---|
| 121 | my $sec_res; |
|---|
| 122 | $sock->read($sec_res, 4); |
|---|
| 123 | $sec_res = unpack('N', $sec_res); |
|---|
| 124 | |
|---|
| 125 | if ($sec_res == 0) { |
|---|
| 126 | # we're in! |
|---|
| 127 | $self->log("Connected successfully!"); |
|---|
| 128 | } else { |
|---|
| 129 | # read reason |
|---|
| 130 | my $err_str_len; |
|---|
| 131 | $sock->read($err_str_len, 4); |
|---|
| 132 | $err_str_len = unpack('N*', $err_str_len) + 0; |
|---|
| 133 | |
|---|
| 134 | my $err_str; |
|---|
| 135 | $sock->read($err_str, $err_str_len); |
|---|
| 136 | $self->log("Error in authentication: $err_str"); |
|---|
| 137 | |
|---|
| 138 | # server will disconnect |
|---|
| 139 | $sock->close; |
|---|
| 140 | } |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | sub log { |
|---|
| 144 | my ($self, ,$msg) = @_; |
|---|
| 145 | my $sock = $self->sock; |
|---|
| 146 | my $addr = $sock ? $sock->peerhost : ''; |
|---|
| 147 | print "[$addr] $msg\n"; |
|---|
| 148 | } |
|---|