Windows安全入门技能--调试lsass
2021-10-19 16:15:59 Author: mp.weixin.qq.com(查看原文) 阅读量:37 收藏

创建: 2021-10-19 14:01
http://scz.617.cn:8/windows/202110191401.txt
目录:

☆ 简介
☆ 环境
☆ 调试lsass
    1) 用kd调试lsass
    2) 用dbgsrv调试lsass
    3) 用"ntsd+kd"调试lsass
    4) 简单对比几种方案
☆ 结语

☆ 简介

有个Windows认证的逆向需求出现,涉及调试lsass。这项技术本身没有什么难度,属于Windows安全入门技能,值得初学者掌握。最近我又开始善良起来,索性做个好人好事,给尚未掌握该技能的小白们科普一下。

☆ 环境

测试环境Win10企业版2016 LTSB,winver显示1607(OS Build 14393.4704)

$ ver
Microsoft Windows [Version 10.0.14393]

NtlmShared.dll

    size    38904
    ver     10.0.14393.3269 (rs1_release.190929-1234)
    SHA256  a95e5823b68182c4e32cb783ad23bc4ff60690001c70e6b5e920c12740c4c37c

msv1_0.dll

    size    401152
    ver     10.0.14393.3866 (rs1_release.200805-1327)
    SHA25   46ad1ac8c7db7d21e8f41efc734b855cee566cb58f8fb825775490dc5de89c94

lsasrv.dll

    size    1501184
    ver     10.0.14393.4704 (rs1_release.211004-1917)
    SHA25   726a3441e46f2b2a109a3fce3002b2108c168d708e43ca23a3819f458964c4cf

SspiSrv.dll

    size    28672
    ver     10.0.14393.4704 (rs1_release.211004-1917)
    SHA25   89365a7ceade64504f15978c93e2bf61dab299327ea2002886bbc5269cad158e

环境不同无所谓,只是为了陈述的严谨性,重在实验过程,理解原理后自行适配。

上面说的是VMware Guest,至于VMware Host那完全无所谓。

☆ 调试lsass

1) 用kd调试lsass

假设Guest停留在登录界面,尚未登录,kd接入Guest

kd.exe -b -s -k com:pipe,port=\\.\pipe\com_136,resets=0

一点准备工作

.prompt_allow +reg +ea +dis

查看当前进程,此刻一般是System

kd> !process -1 0
PROCESS ffffda883e4a36c0
    SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 001ab000  ObjectTable: ffffa08d0bc01280  HandleCount: <Data Not Accessible>
    Image: System

寻找lsass的EPROCESS

kd> !process 0 0 lsass.exe
PROCESS ffffda884002a800
    SessionId: 0  Cid: 0314    Pebe6548a7000  ParentCid: 02ac
    DirBase: 214800000  ObjectTableffffa08d0c9362c0  HandleCount: <Data Not Accessible>
    Imagelsass.exe

切换进程空间到lsass

.process /i <EPROCESS>;g
.process /i ffffda884002a800;g

尽量不在这步用".process /p /r",而是用".process /i;g"。后面要在lsass进程空间直接用bp设用户态断点,本例断点位于系统dll中,用".process /p /r"切换也成,但最靠谱的还是用".process /i;g"切换。

再次检查当前进程(第二种方式)

kd> !thread -p -1 0
PROCESS ffffda884002a800
    SessionId: 0  Cid: 0314    Pebe6548a7000  ParentCid: 02ac
    DirBase: 214800000  ObjectTableffffa08d0c9362c0  HandleCount: <Data Not Accessible>
    Imagelsass.exe

THREAD ffffda883ebbb040  Cid 0004.0148  Teb: 0000000000000000 Win32Thread: 0000000000000000 RUNNING on processor 0

刷新kd维护的用户态加载模块列表使之匹配当前进程,这一步有些耗时,耐心等待

.reload /f /user

在kd查看用户态进程(本例是lsass)加载的dll

lmuf

确保相关模块符号就位,顺便获取模块基址

lmu m msv1_0
lmu m NtlmShared

还可以这样看基址

!lmi NtlmShared // 假设是0x7fff84a10000
!lmi msv1_0     // 假设是0x7fff84a20000
!lmi lsasrv     // 假设是0x7fff84e60000
!lmi SspiSrv    // 假设是0x7fff84520000

设置断点,g起来

kd> dt nt!_EPROCESS UniqueProcessId ImageFileName @$proc
   +0x2e8 UniqueProcessId : 0x00000000`00000314 Void
   +0x450 ImageFileName   : [15]  "lsass.exe"
kd> bp /p @$proc NtlmShared!MsvpPasswordValidate
kd> g

在kd中设置位于系统dll的用户态断点,尽量指定"/p @$proc"以减少干挠,系统dll可能出现在多个不同进程空间,且加载基址一样。

去Guest的登录界面随便输入啥口令,不需要输正确口令,断点命中。再设另一个一次性断点,g起来,会立即命中,查看调用栈回溯

kd> bp /1 /p @$proc ntdll!RtlCompareMemory
kd> g
Breakpoint 1 hit

kd> kpn
 # Child-SP          RetAddr           Call Site
00 000000e6`5557bff8 00007fff`84a136f5 ntdll!RtlCompareMemory
01 000000e6`5557c000 00007fff`84a4ff40 NtlmShared!MsvpPasswordValidate+0x5c5
02 000000e6`5557c100 00007fff`84a4ec3f msv1_0!MsvpValidateLogonWithUserPassword+0x1f0
03 000000e6`5557c210 00007fff`84a4d52d msv1_0!MsvpSamValidate+0xab3
04 000000e6`5557c900 00007fff`84a519d9 msv1_0!MsvSamValidate+0x1ad
05 000000e6`5557cb40 00007fff`84a3ae07 msv1_0!MsvpSamValidateAtLogon+0xd9
06 000000e6`5557cbf0 00007fff`84e844be msv1_0!LsaApLogonUserEx2+0x2067
07 000000e6`5557dde0 00007fff`84e839e6 lsasrv!NegLogonUserEx2Worker+0x7f6
08 000000e6`5557df70 00007fff`84e835b5 lsasrv!NegLogonUserEx2+0x2b6
09 000000e6`5557e250 00007fff`84e7b241 lsasrv!LsapCallAuthPackageForLogon+0x101
0000000e6`5557e300 00007fff`84e87f0b lsasrv!LsapAuApiDispatchLogonUser+0x391
0000000e6`5557e6d0 00007fff`84521467 lsasrv!SspiExLogonUser+0x79b
0000000e6`5557eac0 00007fff`8817a583 SspiSrv!SspirLogonUser+0x247
0d 000000e6`5557ec40 00007fff`881d9f41 RPCRT4!Invoke+0x73
0000000e6`5557ed00 00007fff`88162d3c RPCRT4!Ndr64StubWorker+0xbb1
0f 000000e6`5557f3d0 00007fff`8814a284 RPCRT4!NdrServerCallAll+0x3c
10 000000e6`5557f420 00007fff`8814919d RPCRT4!DispatchToStubInCNoAvrf+0x24
11 000000e6`5557f470 00007fff`88149a4b RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1bd
12 000000e6`5557f540 00007fff`881310ac RPCRT4!RPC_INTERFACE::DispatchToStub+0xcb
13 000000e6`5557f5a0 00007fff`8813152c RPCRT4!LRPC_SCALL::DispatchRequest+0x34c
14 000000e6`5557f680 00007fff`8811ae1c RPCRT4!LRPC_SCALL::HandleRequest+0x2bc
15 000000e6`5557f7a0 00007fff`8811c67b RPCRT4!LRPC_ADDRESS::HandleRequest+0x36c
16 000000e6`5557f850 00007fff`88143a2a RPCRT4!LRPC_ADDRESS::ProcessIO+0x91b
17 000000e6`5557f990 00007fff`88d0d35e RPCRT4!LrpcIoComplete+0xaa
18 000000e6`5557fa30 00007fff`88d0ecc9 ntdll!TppAlpcpExecuteCallback+0x25e
19 000000e6`5557fae0 00007fff`885b84d4 ntdll!TppWorkerThread+0x8d9
1000000e6`5557fee0 00007fff`88d41791 KERNEL32!BaseThreadInitThunk+0x14
1000000e6`5557ff10 00000000`00000000 ntdll!RtlUserThreadStart+0x21

简单整理一下上述调用栈回溯,换个形式展现

SspiSrv!SspirLogonUser
SspiSrv!SspirLogonUser+0x241                                        // call qword ptr [SspiSrv!_guard_dispatch_icall_fptr]
  ntdll!LdrpDispatchUserCallTarget                                  // "u @rax l 1"检查目标函数
                                                                    // "jmp rax"进行流程转移
  lsasrv!SspiExLogonUser
  lsasrv!SspiExLogonUser+0x796
    lsasrv!LsapAuApiDispatchLogonUser
    lsasrv!LsapAuApiDispatchLogonUser+0x38c
      lsasrv!LsapCallAuthPackageForLogon
      lsasrv!LsapCallAuthPackageForLogon+0xfc
        lsasrv!NegLogonUserEx2                                      // IDA中符号带形参,C++符号
                                                                    // 0x7fff84e83730
        lsasrv!NegLogonUserEx2+0x2b1                                // 0x7fff84e83730+0x2b1=0x7fff84e839e1
          lsasrv!NegLogonUserEx2Worker                              // IDA中符号带形参,C++符号
          lsasrv!NegLogonUserEx2Worker+0x7f0                        // 0x7fff84e844b8
                                                                    // call qword ptr [lsasrv!_guard_dispatch_icall_fptr]
            ntdll!LdrpDispatchUserCallTarget                        // "jmp rax"进行流程转移
            msv1_0!LsaApLogonUserEx2
            msv1_0!LsaApLogonUserEx2+0x2062
              msv1_0!MsvpSamValidateAtLogon                         // IDA中符号带形参,C++符号
              msv1_0!MsvpSamValidateAtLogon+0xd4                    // 0x7fff84a519d4
                msv1_0!MsvSamValidate
                msv1_0!MsvSamValidate+0x1a8
                  msv1_0!MsvpSamValidate
                  msv1_0!MsvpSamValidate+0xaae
                    msv1_0!MsvpValidateLogonWithUserPassword
                    msv1_0!MsvpValidateLogonWithUserPassword+0x1ea
                      msv1_0!_imp_MsvpPasswordValidate
                      NtlmShared!MsvpPasswordValidate
                      NtlmShared!MsvpPasswordValidate+0x5bf
                        NtlmShared!_imp_RtlCompareMemory
                        ntdll!RtlCompareMemory
                      NtlmShared!MsvpPasswordValidate+0x5c5         // cmp rax, r14

绝大多数函数名是自解释的,函数内相对偏移版本强相关,不用太在意。过去MsvpPasswordValidate由msv1_0直接提供,Win10将该函数移入NtlmShared。

kd> u NtlmShared!MsvpPasswordValidate+0x5bf l 3
00007fff`84a136ef ff15c31b0000    call    qword ptr [NtlmShared!_imp_RtlCompareMemory (00007fff`84a152b8)]
00007fff`84a136f5 493bc6          cmp     rax,r14
00007fff`
84a136f8 0f8518fbffff    jne     NtlmShared!MsvpPasswordValidate+0xe6 (00007fff`84a13216)

就该版本NtlmShared而言,设置如下Patch型断点,g起来

kd> bc *
kd> bp /p @$proc NtlmShared!MsvpPasswordValidate+0x5c5 "r @rax=@r14;gc"
kd> g

就是让NtlmShared!MsvpPasswordValidate+0x5bf处的内存比较始终认为相等,可在IDA中F5查看更多细节。

你会发现无论之前登录界面输了啥错误口令,都登录成功。这个实验对控制台登录、锁屏登录有效,不适用于SMB、RDP登录。

为了加快可能出现的其他实验进度,小结一下kd操作

.prompt_allow +reg +ea +dis
!process -1 0
!process 0 0 lsass.exe
.process /i <EPROCESS>;g
!thread -p -1 0
.reload /f /user
lmuf
lmu m msv1_0
lmu m NtlmShared
u NtlmShared!MsvpPasswordValidate

2) 用dbgsrv调试lsass

在Guest中

net use Z: "\\vmware-host\Shared Folders"
set _NT_SYMBOL_PATH=srv*z:\sym*http://msdl.microsoft.com/download/symbols

dir "Z:\Green\Windows Kits\10\x64\Debuggers\x64\"

DbgModel.dll
dbgeng.dll
dbghelp.dll
dbgsrv.exe

复制上述几个文件到Guest中,这种远程用户态调试只要求Guest中有这些文件。

dbgsrv.exe -t tcp:port=8765,password=8765

并不需要提前查找lsass的PID

tasklist | find "lsass"
tasklist | findstr lsass

在Host中

cdb.exe -noinh -snul -hd -o -premote tcp:server=192.168.65.136,port=8765,password=8765 -pn lsass.exe

全用户态操作

.prompt_allow +reg +ea +dis
u NtlmShared!MsvpPasswordValidate+0x5bf l 3
bc *
bp NtlmShared!MsvpPasswordValidate+0x5c5 "r @rax=@r14;gc"
g

用runas触发Patch型断点

runas /noprofile /user:DESKTOP-TEST\test cmd

输入任意口令都行。runas也是交互式登录的一种。

Administrator、Guest有其他缺省安全限制,只是Patch口令认证,仍然runas失败。

3) 用"ntsd+kd"调试lsass

参看windbg帮助

Debugger Operation
  Remote Debugging
    Controlling the User-Mode Debugger from the Kernel Debugger
      Starting the Debugging Session
      Switching Modes
      When to Use This Technique
        Debugging CSRSS
        Debugging WinLogon

这种技术将用户态ntsd的I/O重定向到内核态kd,通过kd操作ntsd。

在Guest中

net use Z: "\\vmware-host\Shared Folders"
set _NT_SYMBOL_PATH=srv*z:\sym*http://msdl.microsoft.com/download/symbols

dir "Z:\Green\Windows Kits\10\x64\Debuggers\x64\"

DbgModel.dll
dbgeng.dll
dbghelp.dll
dbgsrv.exe
symsrv.dll
cdb.exe
ntsd.exe

复制上述几个文件到Guest中。从Vista开始,系统缺省不带ntsd。

普通测试

ntsd.exe -noinh -snul -hd -o -g -G "C:\Windows\System32\notepad.exe"
bu notepad!SaveFile

"ntsd+kd"联动测试,主要是指定"-d"参数,让ntsd的I/O重定向到kd

ntsd.exe -d -noinh -snul -hd -o -g -G "C:\Windows\System32\notepad.exe"

本例刻意演示一种复杂情况,未直接断在ntsd中,notepad跑起来了,想让"ntsd+kd"重获控制。

此时需要提前查找notepad的PID

tasklist | find "notepad"
tasklist | findstr notepad

在kd中

!bpid <notepad pid>

指定PID时注意进制,tasklist输出的是10进制,kd中最好加上0n前缀

kd> !bpid 0n4944
Finding wininit.exe (1)...
Finding winlogon.exe (1)...
Waiting for winlogon.exe to break.  This can take a couple of minutes...
...
ntdll!DbgBreakPoint:
00007fff`88d994f0 cc              int     3
0:001>

!bpid向目标进程注入一个线程,通过该线程中断在ntsd中,这可能会耗较长时间。若因系统资源不足或其他原因使得创建、注入线程失败,!bpid就会失败。!bpid成功后可以看出内核态kd提示符已经变成用户态ntsd提示符。

获知被调试进程映像文件绝对路径,以防调错了目标进程

0:001> ?? @$peb->ProcessParameters->ImagePathName
struct _UNICODE_STRING
 "C:\Windows\System32\notepad.exe"
   +0x000 Length           : 0x3e
   +0x002 MaximumLength    : 0x40
   +0x008 Buffer           : 0x000001f4`e8402168  "C:\Windows\System32\notepad.exe"

通过"ntsd+kd"设置用户态断点

0:001.prompt_allow +reg +ea +dis
0:001bu notepad!SaveFile
0:001g

操作notepad触发断点,不再调试时可以正常detach

0:000> .detach
NoTarget> q

从ntsd detach不影响kd的存在。

有些介绍"ntsd+kd"的文章,上来就让你搞Image File Execution Options(IFEO)。"ntsd+kd"与IFEO没有必然联系,动用IFEO只是想尽早attach目标进程,调试其早期启动阶段。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe]
"Debugger"="C:\\temp\\ntsd.exe -d -noinh -snul -hd -o -g -G"

reg.exe add "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe" /v "Debugger" /t REG_SZ /d "C:\temp\ntsd.exe -d -noinh -snul -hd -o -g -G" /f
reg.exe query "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe" /v "Debugger"
reg.exe delete "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\notepad.exe" /f

前面演示目标是notepad.exe,理解原理后将目标换成lsass.exe没毛病。

tasklist | findstr lsass
ntsd.exe -d -noinh -snul -hd -o -g -G -p <lsass pid>

kd> !bpid 0n788
Finding wininit.exe (0)...
Waiting for wininit.exe to break.  This can take a couple of minutes...
...
ntdll!DbgBreakPoint:
00007fff`88d994f0 cc              int     3
0:007> ?? @$peb->ProcessParameters->ImagePathName
struct _UNICODE_STRING
 "C:\Windows\system32\lsass.exe"
   +0x000 Length           : 0x3a
   +0x002 MaximumLength    : 0x3c
   +0x008 Buffer           : 0x00000251`84403598  "C:\Windows\system32\lsass.exe"

在"ntsd+kd"中

.prompt_allow +reg +ea +dis
u NtlmShared!MsvpPasswordValidate+0x5bf l 3
bc *
bp NtlmShared!MsvpPasswordValidate+0x5c5 "r @rax=@r14;gc"
g

用runas触发Patch型断点

runas /noprofile /user:DESKTOP-TEST\test cmd

不想调试lsass时,在kd中Ctrl-C,这将断在kd中,而不是ntsd中,必须再次!bpid

kd> !bpid 0n788
0:007bl
 0 e 00007fff`84a136f5     0001 (0001)  0:**** NtlmShared!MsvpPasswordValidate+0x5c5 "r @rax=@r14;gc"
0:007.detach
NoTargetq

若需尽早调试lsass,动用IFEO比较省事,但不是非此不可,直接用kd完全可以尽早调试lsass,不在此讨论。

4) 简单对比几种方案

前述几种调试方案各有利弊,视需求不同而定。若只是关心登录认证过程,用dbgsrv调试lsass最佳。

调试环境是VMware时,符号目录不是问题,Guest、Host通过"Shared Folders"共用符号目录即可。只有"ntsd+kd"方案面临此问题。

☆ 结语

本文只是演示调试lsass的入门技能,断点、Patch都选用最简单的那种,示例与我的原始需求无关。


文章来源: http://mp.weixin.qq.com/s?__biz=MzUzMjQyMDE3Ng==&mid=2247484842&idx=1&sn=bc5d6a0138f30ed575ff520b22bc0990&chksm=fab2c695cdc54f8313182fbe4eac7f5237e86d0900adc6e4dc827978da84d3d2b5fa136b78c0#rd
如有侵权请联系:admin#unsafe.sh