line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Net::Telnet::Netgear::Packet::Native;
|
2
|
3
|
|
|
3
|
|
1083
|
use strict;
|
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
106
|
|
3
|
3
|
|
|
3
|
|
14
|
use warnings;
|
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
86
|
|
4
|
3
|
|
|
3
|
|
12
|
use parent "Net::Telnet::Netgear::Packet";
|
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
16
|
|
5
|
3
|
|
|
3
|
|
152
|
use Carp;
|
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
160
|
|
6
|
3
|
|
|
3
|
|
1486
|
use Crypt::ECB;
|
|
3
|
|
|
|
|
5548
|
|
|
3
|
|
|
|
|
189
|
|
7
|
3
|
|
|
3
|
|
30
|
use Digest::MD5;
|
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
1100
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our @CARP_NOT = qw ( Net::Telnet::Netgear::Packet );
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
sub new
|
12
|
|
|
|
|
|
|
{
|
13
|
11
|
|
|
11
|
1
|
29
|
my ($self, %opts) = @_;
|
14
|
11
|
100
|
|
|
|
225
|
Carp::croak "Missing required parameter 'mac'"
|
15
|
|
|
|
|
|
|
unless exists $opts{mac};
|
16
|
10
|
|
|
|
|
49
|
$opts{mac} =~ s/\W//g;
|
17
|
|
|
|
|
|
|
# WARNING: "Gearguy" and "Geardog" are no longer valid since the new firmwares of Netgear
|
18
|
|
|
|
|
|
|
# routers. You need to specify the username/password combination you use to login in the
|
19
|
|
|
|
|
|
|
# control panel. (default: admin/password)
|
20
|
10
|
|
100
|
|
|
36
|
$opts{username} ||= "Gearguy";
|
21
|
10
|
|
100
|
|
|
31
|
$opts{password} ||= "Geardog";
|
22
|
10
|
100
|
100
|
|
|
256
|
Carp::croak "The MAC address and the username have to be shorter than 16 characters"
|
23
|
|
|
|
|
|
|
unless length $opts{mac} < 16
|
24
|
|
|
|
|
|
|
and length $opts{username} < 16;
|
25
|
|
|
|
|
|
|
# Increase the maximum password length to 33 characters.
|
26
|
|
|
|
|
|
|
# See the discussion here: https://github.com/insanid/netgear-telenetenable/commit/445c972
|
27
|
|
|
|
|
|
|
# Long story short: the original packet structure specifies 5 fields: username, password, mac,
|
28
|
|
|
|
|
|
|
# signature, reserved. Each field excluding 'reserved' is 0x10 (16) characters. However,
|
29
|
|
|
|
|
|
|
# it looks like the length of the 'password' field can be increased up to 33 characters, which
|
30
|
|
|
|
|
|
|
# is the maximum length allowed for the password by the Netgear interface. The analysis of what
|
31
|
|
|
|
|
|
|
# happens is in the GitHub link written before.
|
32
|
8
|
100
|
|
|
|
120
|
Carp::croak "The password must have a maximum length of 33 characters."
|
33
|
|
|
|
|
|
|
unless length $opts{password} <= 33;
|
34
|
7
|
|
|
|
|
29
|
bless \%opts, $self;
|
35
|
|
|
|
|
|
|
}
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# Generates the packet. Here's some documentation:
|
38
|
|
|
|
|
|
|
# http://wiki.openwrt.org/toh/netgear/telnet.console
|
39
|
|
|
|
|
|
|
# Special thanks to insanid@github, he's a nice guy.
|
40
|
|
|
|
|
|
|
sub get_packet
|
41
|
|
|
|
|
|
|
{
|
42
|
6
|
|
|
6
|
1
|
17
|
my $self = shift;
|
43
|
6
|
|
|
|
|
15
|
my ($mac, $usr, $pwd) = (
|
44
|
|
|
|
|
|
|
_left_justify ($self->{mac}, 0x10, "\x00"),
|
45
|
|
|
|
|
|
|
_left_justify ($self->{username}, 0x10, "\x00"),
|
46
|
|
|
|
|
|
|
_left_justify ($self->{password}, 0x21, "\x00")
|
47
|
|
|
|
|
|
|
);
|
48
|
6
|
|
|
|
|
13
|
my $text = _left_justify ($mac . $usr . $pwd, 0x70, "\x00");
|
49
|
6
|
|
|
|
|
38
|
my $payload = _swap_bytes (
|
50
|
|
|
|
|
|
|
_left_justify (Digest::MD5::md5 ($text) . $text, 0x80, "\x00")
|
51
|
|
|
|
|
|
|
);
|
52
|
6
|
|
|
|
|
24
|
my $cipher = Crypt::ECB->new;
|
53
|
6
|
|
|
|
|
72
|
$cipher->padding (PADDING_NONE);
|
54
|
6
|
50
|
|
|
|
34
|
$cipher->cipher ("Blowfish")
|
55
|
|
|
|
|
|
|
|| die "Blowfish not available: ", $cipher->errstring;
|
56
|
6
|
|
|
|
|
2028
|
$cipher->key ("AMBIT_TELNET_ENABLE+" . $self->{password});
|
57
|
6
|
|
|
|
|
44
|
_swap_bytes ($cipher->encrypt ($payload));
|
58
|
|
|
|
|
|
|
}
|
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# https://goo.gl/qYA1u5
|
61
|
|
|
|
|
|
|
# TL;DR: The Blowfish implementation of Netgear expects data in big endian
|
62
|
|
|
|
|
|
|
# order, but we are using the little endian order. This fixes the problem.
|
63
|
|
|
|
|
|
|
# Also, from perldoc -f pack:
|
64
|
|
|
|
|
|
|
# - V An unsigned long (32-bit) in "VAX" (little-endian) order.
|
65
|
|
|
|
|
|
|
# - N An unsigned long (32-bit) in "network" (big-endian) order.
|
66
|
|
|
|
|
|
|
sub _swap_bytes
|
67
|
|
|
|
|
|
|
{
|
68
|
12
|
|
|
12
|
|
1379
|
pack 'V*', unpack 'N*', shift;
|
69
|
|
|
|
|
|
|
}
|
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# See https://docs.python.org/2/library/string.html#string.ljust
|
72
|
|
|
|
|
|
|
# Left-justifies $str to $length characters, filling with $filler if necessary.
|
73
|
|
|
|
|
|
|
# Example:
|
74
|
|
|
|
|
|
|
# _left_justify 'meow', 6, 'A' -> 'meowAA'
|
75
|
|
|
|
|
|
|
sub _left_justify
|
76
|
|
|
|
|
|
|
{
|
77
|
30
|
|
|
30
|
|
33
|
my ($str, $length, $filler) = @_;
|
78
|
30
|
|
50
|
|
|
43
|
$filler ||= " ";
|
79
|
30
|
|
|
|
|
18
|
my $i = length $str;
|
80
|
30
|
100
|
|
|
|
53
|
return $str if $i >= $length;
|
81
|
22
|
|
|
|
|
344
|
$str .= $filler while ($i++ < $length);
|
82
|
22
|
|
|
|
|
35
|
$str;
|
83
|
|
|
|
|
|
|
}
|
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
1;
|