标题: Windows安全入门技能--调试lsass 创建: 2021-10-19 14:01 更新: 2021-10-25 12:21 链接: https://scz.617.cn/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: Image: System 寻找lsass的EPROCESS kd> !process 0 0 lsass.exe PROCESS ffffda884002a800 SessionId: 0 Cid: 0314 Peb: e6548a7000 ParentCid: 02ac DirBase: 214800000 ObjectTable: ffffa08d0c9362c0 HandleCount: Image: lsass.exe 切换进程空间到lsass .process /i ;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 Peb: e6548a7000 ParentCid: 02ac DirBase: 214800000 ObjectTable: ffffa08d0c9362c0 HandleCount: Image: lsass.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 0a 000000e6`5557e300 00007fff`84e87f0b lsasrv!LsapAuApiDispatchLogonUser+0x391 0b 000000e6`5557e6d0 00007fff`84521467 lsasrv!SspiExLogonUser+0x79b 0c 000000e6`5557eac0 00007fff`8817a583 SspiSrv!SspirLogonUser+0x247 0d 000000e6`5557ec40 00007fff`881d9f41 RPCRT4!Invoke+0x73 0e 000000e6`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 1a 000000e6`5557fee0 00007fff`88d41791 KERNEL32!BaseThreadInitThunk+0x14 1b 000000e6`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 ;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 指定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:001> bu notepad!SaveFile 0:001> g 操作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 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:007> bl 0 e 00007fff`84a136f5 0001 (0001) 0:**** NtlmShared!MsvpPasswordValidate+0x5c5 "r @rax=@r14;gc" 0:007> .detach NoTarget> q 若需尽早调试lsass,动用IFEO比较省事,但不是非此不可,直接用kd完全可以尽早 调试lsass,不在此讨论。 "ntsd+kd"时!号开头的扩展命令用的是Guest中的扩展dll,比如ext.dll,而前述简 易版ntsd环境缺失绝大多数扩展dll,所以!ustr、!peb等命令不可用;这不是问题, 可以改善环境。 $ tree /f /a . C:\TEMP | cdb.exe | dbgeng.dll | dbghelp.dll | DbgModel.dll | dbgsrv.exe | ntsd.exe | symsrv.dll | +---winext | ext.dll | uext.dll | \---winxp exts.dll ntsdexts.dll kd> !bpid 0n788 检查扩展dll就位 0:008> .extcmds .load ntsdexts .load uext .load exts .load ext .load dbghelp 0:008> .extmatch /e * * (略) 0:008> dt -c ntdll!_PEB ProcessParameters->ImagePathName +0x020 ProcessParameters +0x060 ImagePathName _UNICODE_STRING 0:008> !ustr poi(@$peb+0x20)+0x60 String(58,60) at 0000025184402fe0: C:\Windows\system32\lsass.exe 4) 简单对比几种方案 前述几种调试方案各有利弊,视需求不同而定。若只是关心登录认证过程,用dbgsrv 调试lsass最佳。kd中无法使用wt,只能在用户态调试中使用wt。据说x86内核态可以 用wt,但现在还有x86内核态吗? 调试环境是VMware时,符号目录不是问题,Guest、Host通过"Shared Folders"共用 符号目录即可。只有"ntsd+kd"方案面临此问题。 ☆ 结语 本文只是演示调试lsass的入门技能,断点、Patch都选用最简单的那种,示例与我的 原始需求无关。