Win10主控台登录认证流程
2022-1-23 00:0:0 Author: mp.weixin.qq.com(查看原文) 阅读量:69 收藏

☆ 背景介绍

搜某些关键字时命中[1],作者在看雪论坛分享过Win7主控台登录认证流程中的部分环节。正好当时有深究Win10 RPC调试的动机,这篇也涉及一些,实验环境唾手可得,就在Win10上实践一番。

我对登录认证流程没有研究过,那个可能是黑产党的刚需。本文不涉及黑产党视角,就是简单看看[1]之作者所述内容在Win10上的变化,以调试视角呈现内容。阅读本文前,应该先看[1]。

本文在Win10企业版2016 LTSB 1607(OS Build 14393.4704)上测试。

☆ Win10交互式登录大框架

Win10主控台登录动态创建进程树

winlogon.exe
  LogonUI.exe

假设在主控台登录成功,则上述进程树被销毁;此时若锁屏,会创建新的进程树。每种交互式登录都有这样的进程树被创建,RDP登录时也会创建这样的进程树。

下面这个框架对应几件事

a) 出现密码输入界面
b) 输入密码点击确定后的RPC调用
c) 报告登录验证结果

winlogon!StateMachineWorkerCallback

  ntdll!LdrpDispatchUserCallTarget
  winlogon!WLGeneric_Request_Logon_Credz_Execute
    winlogon!RequestCredentials
      winlogon!WluiRequestCredentials               // 要求用户输入密码,出现新的密码输入界面
        logoncontroller!WluirRequestCredentials     // 从winlogon进入LogonUI
    winlogon!SignalManagerSetSignal                 // 错误密码触发

  ntdll!LdrpDispatchUserCallTarget
  winlogon!WLGeneric_Logon_ReportFailedResult_Execute
    winlogon!ReportResult
      winlogon!WluiReportResult                     // 报告登录验证结果
        logoncontroller!WluirReportResult
    winlogon!WluiDisplayRequestCredentialsError     // 提示"密码不正确"
      logoncontroller!WluirDisplayRequestCredentialsError
    winlogon!WlStateMachineSetSignal
      winlogon!SignalManagerSetSignal               // 提示"密码不正确",点击确定,触发

  winlogon!WLGeneric_Authenticating_Execute
    winlogon!WlDisplayStatus
      winlogon!WluiDisplayStatus
        logoncontroller!WluirDisplayStatus
    winlogon!AuthenticateUser                       // 输入密码,提交,触发
      usermgrcli!UMgrLogonUser
        usermgrcli!DoRpcCall<<lambda_39def54e3a6d242849c8646b9417fa1f> >
          usermgrcli!<lambda_39def54e3a6d242849c8646b9417fa1f>::operator()
            RPCRT4!NdrClientCall3
              RPCRT4!NdrpClientCall3
                usermgr!svcUMLogonUser              // 从winlogon进入UserManager服务
                  usermgr!UserMgrCli::UMLogonUser
                    sspicli!LsaLogonUser            // Win10在UserManager服务中调用此函数
                      sspicli!SspipLogonUser
                        RPCRT4!NdrClientCall3
                          SspiSrv!SspirLogonUser    // 从UserManager服务进入lsass
    winlogon!SignalManagerSetSignal                 // 错误密码触发

Win10在winlogon与lsass之间多了一个UserManager服务

winlogon->UserManager服务->lsass

过去可能由winlogon直接发起RPC请求sspicli!SspipLogonUser,Win10有变化,由UserManager服务发起RPC请求。

☆ winlogon!Wlui*

winlogon!WluiRequestCredentials负责展现新的密码输入界面,函数名中的"ui"暗示了这是用户界面相关的函数。winlogn中这类函数还有

> x /1 winlogon!Wlui*[a-z]
winlogon!WluiiReleaseLessPrivilegedToken
winlogon!WluiiStartupImpl
winlogon!WluiiWaitForServer
...
winlogon!WluiReleaseContext
winlogon!WluiClearUIState

直接"x /1 winlogon!Wlui*"会显示很多内部函数,比如

winlogon!WluiDisplayStatus$filt$0

参看windbg帮助中的"String Wildcard Syntax"小节。"x /1 winlogon!Wlui*[a-z]"大小写不敏感。

☆ logoncontroller!Wluir*

从框架图中看到winlogon!WluiRequestCredentials通过RPC调用LogonUI进程中的logoncontroller!WluirRequestCredentials,函数名多了一个字母"r",表示RPC。这一对函数分别对应RPC Client、RPC Server。类似的还有

winlogon!WluiReportResult
logoncontroller!WluirReportResult

winlogon!WluiDisplayRequestCredentialsError
logoncontroller!WluirDisplayRequestCredentialsError

winlogon!WluiDisplayStatus
logoncontroller!WluirDisplayStatus

调试LogonUI.exe,查看

> .shell -ci "x /1 logoncontroller!Wluir*" findstr Wluir
logoncontroller!WluirDisplayLocked
logoncontroller!WluirSecureDisplayLocked
logoncontroller!WluirDisplayTSReconnectUI
...
logoncontroller!WluirDisplayTSDisconnectUI
logoncontroller!WluirFinishOperation

由于x命令后面的模板大小写不敏感,直接"x /1 logoncontroller!Wluir*"会命中这种

logoncontroller!WluiRequestReasonToLogonReason

不是我们想要的,所以用了".shell -ci"技巧。上述25个函数都有相应的winlogon版本。

更精准的办法是,用IDA的findrpc插件看LogonController.dll的"f3f09ffd-fbcf-4291-944d-70ad6e0e73bb"接口,该接口提供的远程过程正是上述25个函数。关于findrpc,参看

《利用findrpc寻找RPC接口信息》
http://scz.617.cn:8/python/202111061555.txt

☆ SspiSrv!SspirLogonUser

主控台登录时流程会过lsass进程空间的SspiSrv!SspirLogonUser。对之设断,断下来时查看IID/ProcNum/RPC Client PID

bp SspiSrv!SspirLogonUser
r $t0=poi(@rsp+0xc8+0xe0);dt ntdll!_GUID poi(@$t0+0x28)+4;? dwo(@$t0+0x1c)
r $t1=poi(@$t0+0x68);? qwo(@$t1+8)

在SspiSrv!SspirLogonUser入口处获取RPC Client PID的Hacking方案不可用于产品。其他IID/ProcNum目标函数入口处未必如此,需要逆向确认,不可直接套用。假设获取信息如下

IID             4f32adc8-6052-4a04-8701-293ccf2096f0
ProcNum         SspiSrv!SspirLogonUser (12)
RPC Client PID  872

$
 tasklist /svc /fi "pid eq 872"

Image Name                     PID Services
========================= ======== ============================================
svchost.exe                    872 Appinfo, AppMgmt, BITS, CertPropSvc,
                                   DsmSvc, gpsvc, IKEEXT, iphlpsvc, lfsvc,
                                   ProfSvc, RasMan, Schedule, seclogon, SENS,
                                   SessionEnv, Themes, UserManager, UsoSvc,
                                   Winmgmt, WpnService

RPC客户端是svchost(872),不是winlogon.exe,具体说,是UserManager服务。

☆ sspicli!SspipLogonUser

调试svchost(872),调试UserManager服务

bp RPCRT4!NdrpClientCall3 "r $t0=poi(poi(@rdx));.if(@$t0){.if(qwo(@$t0+4)==0x4a0460524f32adc8 and qwo(@$t0+0xc)==0xf09620cf3c290187 and @r8==0n12){kpn}.else{gc}}.else{gc}"
dt -io ntdll!_GUID @$t0+4;? @r8

IID/ProcNum匹配时断下

 # Call Site
00 RPCRT4!NdrpClientCall3
01 RPCRT4!NdrClientCall3+0xf2
02 sspicli!SspipLogonUser+0x394
03 sspicli!LsaLogonUser+0x83
04 usermgr!UserMgrCli::UMLogonUser+0x2c6
05 usermgr!svcUMLogonUser+0xd7
06 RPCRT4!Invoke+0x73
07 RPCRT4!Ndr64StubWorker+0xbb1
08 RPCRT4!NdrServerCallAll+0x3c
09 RPCRT4!DispatchToStubInCNoAvrf+0x24
0a RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1bd
0b RPCRT4!RPC_INTERFACE::DispatchToStubWithObject+0x15e
0c RPCRT4!LRPC_SCALL::DispatchRequest+0x177
0d RPCRT4!LRPC_SCALL::HandleRequest+0x2bc
0e RPCRT4!LRPC_ADDRESS::HandleRequest+0x36c
0f RPCRT4!LRPC_ADDRESS::ProcessIO+0x91b
10 RPCRT4!LrpcIoComplete+0xaa
11 ntdll!TppAlpcpExecuteCallback+0x25e
12 ntdll!TppWorkerThread+0x8d9
13 KERNEL32!BaseThreadInitThunk+0x14
14 ntdll!RtlUserThreadStart+0x21

UserManager服务调用sspicli!SspipLogonUser,发起RPC,调用lsass的SspiSrv!SspirLogonUser。

☆ sspicli!LsaLogonUser

sspicli!SspipLogonUser的父函数是sspicli!LsaLogonUser,有个同名函数文档化过,参[2]

NTSTATUS LsaLogonUser
(
    [in]    HANDLE              LsaHandle,                          // rcx
    /*
     * dt UxTheme!LSA_STRING @rdx
     *
     * "Winlogon"
     */

    [in]    PLSA_STRING         OriginName,                         // rdx
    /*
     * dt UxTheme!_SECURITY_LOGON_TYPE
     *
     * Interactive(2)
     */

    [in]    SECURITY_LOGON_TYPE LogonType,                          // r8
    [in]    ULONG               AuthenticationPackage,              // r9
    /*
     * A pointer to an input buffer that contains authentication information,
     * such as user name and password. The format and content of this buffer
     * are determined by the authentication package.
     *
     * db poi(@rsp+0x28) l dwo(@rsp+0x30)
     */

    [in]    PVOID               AuthenticationInformation,          // poi(@rsp+0x28)
    [in]    ULONG               AuthenticationInformationLength,    // dwo(@rsp+0x30)
    [in]    PTOKEN_GROUPS       LocalGroups,                        // poi(@rsp+0x38)
    [in]    PTOKEN_SOURCE       SourceContext,                      // poi(@rsp+0x40)
    [out]   PVOID               *ProfileBuffer,
    [out]   PULONG              ProfileBufferLength,
    [out]   PLUID               LogonId,
    [out]   PHANDLE             Token,
    [out]   PQUOTA_LIMITS       Quotas,
    [out]   PNTSTATUS           SubStatus
)

我以为AuthenticationInformation指向下述结构

> dt UxTheme!_MSV1_0_INTERACTIVE_LOGON
   +0x000 MessageType      : _MSV1_0_LOGON_SUBMIT_TYPE
   +0x008 LogonDomainName  : _UNICODE_STRING
   +0x018 UserName         : _UNICODE_STRING
   +0x028 Password         : _UNICODE_STRING

bp sspicli!LsaLogonUser "db poi(@rsp+0x28) l dwo(@rsp+0x30)"

user:scz
pass:xxx

断在sspicli!LsaLogonUser时查看AuthenticationInformation

00000205`a41b4c40  01 80 00 00 06 00 00 00-ba 00 00 00 00 00 00 00  ................
00000205`
a41b4c50  40 42 0a 905 02 00 00-02 00 00 00 ff 700 00  @B..............
00000205`a41b4c60  18 00 18 00 ff 7f 00 00-40 00 00 00 00 00 00 00  [email protected]
00000205`
a41b4c70  06 00 06 00 00 00 00 00-58 00 00 00 00 00 00 00  ........X.......
00000205`a41b4c80  5c 00 5c 00 00 00 00 00-5e 00 00 00 00 00 00 00  \.\.....^.......
00000205`
a41b4c90  00 00 00 00 00 00 00 00-44 00 45 00 53 00 400  ........D.E.S.K.
00000205`a41b4ca0  54 00 4f 00 50 00 2d 00-54 00 45 00 53 00 54 00  T.O.P.-.T.E.S.T.
00000205`
a41b4cb0  73 00 63 00 700 40 00-40 00 44 00 07 00 08 00  [email protected]@.D.....
00000205`a41b4cc0  0c 00 0a 00 0d 00 59 00-41 00 41 00 41 00 41 00  ......Y.A.A.A.A.
00000205`
a41b4cd0  41 00 600 50 00 41 00-41 00 41 00 41 00 41 00  A.n.P.A.A.A.A.A.
00000205`a41b4ce0  41 00 41 00 41 00 41 00-53 00 48 00 58 00 7a 00  A.A.A.A.S.H.X.z.
00000205`
a41b4cf0  64 00 63 00 500 65 00-500 51 00 71 00 47 00  d.c.Z.e.Z.Q.q.G.
00000205`a41b4d00  6f 00 76 00 37 00 47 00-57 00 61 00 54 00 6d 00  o.v.7.G.W.a.T.m.
00000205`
a41b4d10  500                                            Z.

可能与winlogon通过RPC到UserManager服务有关,AuthenticationInformation不再直接对应MSV1_0_INTERACTIVE_LOGON结构。后来跟踪到usermgr!UserMgrCli::WrapWlAuthInfo中,AuthenticationInformation指向"struct UserMgrCli::_WL_AUTH_INFO"。手工解码如下

01 80 00 00             // +0x0 在usermgr!UserMgrCli::WrapWlAuthInfo+0x53处被设置
06 00 00 00             // +0x4
/*
 * buf/len均来自UserMgrCli::UMLogonUser的形参a7,其类型是"struct _AUTH_INFO_RPC *"
 */

ba 00 00 00             // +0x8 len=dwo()=0xba
00 00 00 00             // +0xc
40 42 0905 02 00 00 // +0x10 buf=0x2059c0a4240
                        //       db 0x2059c0a4240 l 0xba
                        //       跟后面的数据区相同
--------------------------------------------------------------------------
02 00 00 00             // +0x0 MessageType=dwo()=0x2
ff 7f 00 00             // +0x4 填充对齐
--------------------------------------------------------------------------
18 00                   // +0x8 LogonDomainNameLen=wo()=0x18
18 00                   // +0xa
ff 7f 00 00             // +0xc 填充对齐
40 00 00 00 00 00 00 00 // +0x10 LogonDomainNameOff=0x40
--------------------------------------------------------------------------
06 00                   // +0x18 UserNameLen=wo()=0x6
06 00                   // +0x1a
00 00 00 00             // +0x1c 填充对齐
58 00 00 00 00 00 00 00 // +0x20 UserNameOff=0x58
--------------------------------------------------------------------------
500                   // +0x28 PasswordLen=0x5c
500                   // +0x2a
00 00 00 00             // +0x2c 填充对齐
500 00 00 00 00 00 00 // +0x30 PasswordOff=0x5e
--------------------------------------------------------------------------
00 00 00 00 00 00 00 00 // +0x38 填充对齐
--------------------------------------------------------------------------
44 00 45 00 53 00 400 // +0x40 LogonDomainName="DESKTOP-TEST"
54 00 4f 00 50 00 2d 00
54 00 45 00 53 00 54 00
--------------------------------------------------------------------------
73 00 63 00 700       // +0x58 UserName="scz"
--------------------------------------------------------------------------
40 00 40 00 44 00 07 00 // +0x5e Password=(混淆过)
08 00 000 000 0d 00
59 00 41 00 41 00 41 00 // +0x6e "YAAAAAnPAAAAAAAAASHXzdcZeZQqGov7GWaTmZ"
41 00 41 00 600 50 00
41 00 41 00 41 00 41 00
41 00 41 00 41 00 41 00
41 00 53 00 48 00 58 00
700 64 00 63 00 500
65 00 500 51 00 71 00
47 00 6f 00 76 00 37 00
47 00 57 00 61 00 54 00
6d 00 500

☆ usermgrcli!UMgrLogonUser

看大框架图,winlogon会调用usermgrcli!UMgrLogonUser

usermgrcli!UMgrLogonUser
(
    a1,                                 // rcx
    a2,                                 // rdx
    AuthenticationPackage,              // r8
    /*
     * db @r9 l dwo(@rsp+0x28)
     */

    AuthenticationInformation,          // r9
    AuthenticationInformationLength,    // dwo(@rsp+0x28)
    ...
)

bp usermgrcli!UMgrLogonUser "db @r9 l dwo(@rsp+0x28)"

 # Call Site
00 usermgrcli!UMgrLogonUser
01 winlogon!AuthenticateUser+0x556
02 winlogon!WLGeneric_Authenticating_Execute+0x45e
03 winlogon!StateMachineWorkerCallback+0xc8
04 ntdll!TppWorkpExecuteCallback+0x35e
05 ntdll!TppWorkerThread+0x474
06 KERNEL32!BaseThreadInitThunk+0x14
07 ntdll!RtlUserThreadStart+0x21

000001e3`b4d70000  02 00 00 00 ff 7f 00 00-18 00 18 00 ff 7f 00 00  ................
000001e3`
b4d70010  40 00 00 00 00 00 00 00-06 00 06 00 00 00 00 00  @...............
000001e3`b4d70020  58 00 00 00 00 00 00 00-5c 00 5c 00 00 00 00 00  X.......\.\.....
000001e3`
b4d70030  500 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ^...............
000001e3`b4d70040  44 00 45 00 53 00 4b 00-54 00 4f 00 50 00 2d 00  D.E.S.K.T.O.P.-.
000001e3`
b4d70050  54 00 45 00 53 00 54 00-73 00 63 00 700 40 00  [email protected]
000001e3`b4d70060  40 00 44 00 07 00 08 00-0c 00 0a 00 0d 00 59 00  @.D...........Y.
000001e3`
b4d70070  41 00 41 00 41 00 41 00-41 00 600 50 00 41 00  A.A.A.A.A.n.P.A.
000001e3`b4d70080  41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  A.A.A.A.A.A.A.A.
000001e3`
b4d70090  53 00 48 00 58 00 700-64 00 63 00 500 65 00  S.H.X.z.d.c.Z.e.
000001e3`b4d700a0  5a 00 51 00 71 00 47 00-6f 00 76 00 37 00 47 00  Z.Q.q.G.o.v.7.G.
000001e3`
b4d700b0  57 00 61 00 54 00 600-500                    W.a.T.m.Z.

对混淆过的Password设置数据断点,逐步溯源至

 # Call Site
00 RPCRT4!Ndr64ComplexStructUnmarshall+0x503
01 RPCRT4!Ndr64pClientUnMarshal+0x28f
02 RPCRT4!NdrpClientCall3+0x1010
03 RPCRT4!NdrClientCall3+0xf2
04 winlogon!WluiRequestCredentials+0xa4
05 winlogon!RequestCredentials+0x9f
06 winlogon!WLGeneric_Request_Logon_Credz_Execute+0x190
07 winlogon!StateMachineWorkerCallback+0x7f
08 ntdll!TppWorkpExecuteCallback+0x35e
09 ntdll!TppWorkerThread+0x474
0a KERNEL32!BaseThreadInitThunk+0x14
0b ntdll!RtlUserThreadStart+0x21

长度为0xba的数据来自winlogon!WluiRequestCredentials,调用栈表明正在返回RPC结果,其服务端是logoncontroller!WluirRequestCredentials。这意味着对明文口令"xxx"的混淆工作很可能发生在LogonUI.exe进程空间。

☆ logoncontroller!WluirRequestCredentials

winlogon!WluiRequestCredentials
(
    a1,
    a2,
    a3,
    /*
     * out型形参,返回时会设置poi(@r9+0x10)、dwo(@r9+8)
     *
     * 对应logoncontroller!WluirRequestCredentials
     */

    sth,    // r9
    a5
)

logoncontroller!WluirRequestCredentials
(
    a1,
    a2,
    a3,
    a4,
    /*
     * 对应winlogon!WluiRequestCredentials的sth
     */

    sth,    // poi(@rsp+0x28)
    a6
)

sth就是长度为0xba的数据。对sth中混淆过的Password设置数据断点,异常艰难地逐步溯源,中途几欲放弃,这个时候特别怀念VMware 7之前的Record/Replay功能。后来溯源至credprovs!KerbInteractiveUnlockLogonPack。

☆ credprovs!KerbInteractiveUnlockLogonPack

credprovs!KerbInteractiveUnlockLogonPack
(
    /*
     * dt -r uxtheme!_KERB_INTERACTIVE_UNLOCK_LOGON @rcx
     */

    a1,
    ...
)

该函数第一形参指向下述结构

> dt -r uxtheme!_KERB_INTERACTIVE_UNLOCK_LOGON @rcx
   +0x000 Logon            : _KERB_INTERACTIVE_LOGON
      +0x000 MessageType      : 2 ( KerbInteractiveLogon )
      +0x008 LogonDomainName  : _UNICODE_STRING "DESKTOP-TEST"
         +0x000 Length           : 0x18
         +0x002 MaximumLength    : 0x1a
         +0x008 Buffer           : 0x000001cf`0b0d5b60  "DESKTOP-TEST"
      +0x018 UserName         : _UNICODE_STRING "scz"
         +0x000 Length           : 6
         +0x002 MaximumLength    : 8
         +0x008 Buffer           : 0x000001cf
`0679b020  "scz"
      +0x028 Password         : _UNICODE_STRING "@@D???"
         +0x000 Length           : 0x5c
         +0x002 MaximumLength    : 0x5e
         +0x008 Buffer           : 0x000001cf`0a86f980  "@@D???"
   +0x038 LogonId          : _LUID
      +0x000 LowPart          : 0
      +0x004 HighPart         : 0n0

如下断点可以查看混淆过的Password

bp credprovs!KerbInteractiveUnlockLogonPack "r $t0=@rcx+0x28;db poi(@$t0+8) l wo(@$t0)"

☆ sechost!CredProtectW

从credprovs!KerbInteractiveUnlockLogonPack开始,对混淆过的Password用数据断点溯源,这个时候就比较容易了。后来溯源至

 # Call Site
00 ntdll!memcpy+0xb9
01 sechost!CredProtectW+0xa6
02 credprovs!CPasswordCredential::_EncryptAndMarshal+0xfe
03 credprovs!CPasswordCredential::_KerbInteractiveUnlockLogonFill+0xf7
04 credprovs!CPasswordCredential::_GetSerializationUnlockLogon+0x7f
05 credprovs!CPasswordCredential::GetSerialization+0x1f4
06 credprovhost!CGetSerializationJob::Do+0x224
07 credprovhost!CJobQueue<CREDENTIAL_PROVIDER_THREAD_JOB_CONTEXT const & __ptr64>::DoJobInternal+0xec
08 credprovhost!CCredentialProviderThread::_vThreadProc+0x355
09 credprovhost!CCredentialProviderThread::_sThreadProc+0xc7
0a KERNEL32!BaseThreadInitThunk+0x14
0b ntdll!RtlUserThreadStart+0x21

sechost!CredProtectW是文档化的导出函数,参[3],大致流程如下

BOOL CredProtectW
(
    [in]        BOOL                    fAsSelf,
    /*
     * 明文口令
     */

    [in]        LPWSTR                  pszCredentials,             // rdx
    /*
     * 明文口令长度,是字符长度,不是字节长度
     */

    [in]        DWORD                   cchCredentials,             // r8
    /*
     * 返回混淆后的Password
     */

    [out]       LPWSTR                  pszProtectedCredentials,    // r9
    /*
     * 返回混淆后的Password长度,是字符长度,不是字节长度
     */

    [inout]   DWORD                  *pcchMaxChars,               // poi(@rsp+0x28)
    [out]       CRED_PROTECTION_TYPE   *ProtectionType
)
--------------------------------------------------------------------------
sechost!CredProtectW
  sechost!CredpEncryptAndMarshalBinaryBlobEx        // db @r8 l @r9
    sechost!CredpEncodeSecretEx                     // 对明文口令做混淆,混淆结果是16字节字节流
                                                    // db @rdx l @r8
      CRYPTBASE!SystemFunction040                   // db @rcx l @rdx
                                                    // 该函数就地混淆(加密)
        ntdll!NtDeviceIoControlFile                 // db poi(@rsp+0x38) l dwo(@rsp+0x40)
                                                    // 访问"\Device\KsecDD"
                                                    // ? qwo(CRYPTBASE!g_hKsecDD)
    sechost!CredMarshalCredentialW                  // 对混淆结果序列化

在LogonUI进程空间拦截sechost!CredProtectW,查看交互式登录的明文口令

bp sechost!CredProtectW "db @rdx l @r8*2"

有两次命中,第一次先计算结果长度,第二次才生成结果,非常熟悉的Win32 API套路。此外,下列断点都可以查看交互式登录的明文口令

bp sechost!CredpEncryptAndMarshalBinaryBlobEx "db @r8 l @r9"
bp sechost!CredpEncodeSecretEx "db @rdx l @r8"
bp CRYPTBASE!SystemFunction040 "db @rcx l @rdx"
bp ntdll!NtDeviceIoControlFile "db poi(@rsp+0x38) l dwo(@rsp+0x40)"

☆ sechost!CredUnprotectW

sechost!CredUnprotectW是逆函数,也是文档化的导出函数,参[3]

BOOL CredUnprotectW
(
    [in]        BOOL    fAsSelf,
    /*
     * 混淆过的Password
     */

    [in]        LPWSTR  pszProtectedCredentials,    // rdx
    /*
     * 混淆过的Password长度
     */

    [in]        DWORD   cchProtectedCredentials,    // r8
    /*
     * 返回明文口令
     */

    [out]       LPWSTR  pszCredentials,             // r9
    /*
     * 返回明文口令长度
     */

    [inout]   DWORD  *pcchMaxChars                // poi(@rsp+0x28)
)

调试lsass进程

bp sechost!CredUnprotectW "db @rdx l @r8*2"

00000251`8519b110  40 00 40 00 44 00 07 00-08 00 0c 00 0a 00 0d 00  @[email protected]
00000251`
8519b120  59 00 41 00 41 00 41 00-41 00 41 00 600 50 00  Y.A.A.A.A.A.n.P.
00000251`8519b130  41 00 41 00 41 00 41 00-41 00 41 00 41 00 41 00  A.A.A.A.A.A.A.A.
00000251`
8519b140  41 00 53 00 48 00 58 00-700 64 00 63 00 500  A.S.H.X.z.d.c.Z.
00000251`8519b150  65 00 5a 00 51 00 71 00-47 00 6f 00 76 00 37 00  e.Z.Q.q.G.o.v.7.
00000251`
8519b160  47 00 57 00 61 00 54 00-600 500 00 00        G.W.a.T.m.Z...

 # Call Site
00 sechost!CredUnprotectW
01 lsasrv!LsapDecodeSecret+0xe9
02 lsasrv!LsapConvertLogonAuthInfo+0x253
03 lsasrv!SspiExLogonUser+0x610
04 SspiSrv!SspirLogonUser+0x247
05 RPCRT4!Invoke+0x73
06 RPCRT4!Ndr64StubWorker+0xbb1
07 RPCRT4!NdrServerCallAll+0x3c
08 RPCRT4!DispatchToStubInCNoAvrf+0x24
09 RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1bd
0a RPCRT4!RPC_INTERFACE::DispatchToStub+0xcb
0b RPCRT4!LRPC_SCALL::DispatchRequest+0x34c
0c RPCRT4!LRPC_SCALL::HandleRequest+0x2bc
0d RPCRT4!LRPC_ADDRESS::HandleRequest+0x36c
0e RPCRT4!LRPC_ADDRESS::ProcessIO+0x91b
0f RPCRT4!LrpcIoComplete+0xaa
10 ntdll!TppAlpcpExecuteCallback+0x25e
11 ntdll!TppWorkerThread+0x8d9
12 KERNEL32!BaseThreadInitThunk+0x14
13 ntdll!RtlUserThreadStart+0x21

看到前面介绍过的SspiSrv!SspirLogonUser,它会间接调用sechost!CredUnprotectW获取明文口令。

sechost!CredUnprotectW大致流程如下

sechost!CredUnprotectW
  sechost!CredpUnmarshalandDecodeStringEx       // db @r8
                                                // du @r8
    sechost!CredUnmarshalCredentialW            // 反序列化
                                                // db @rcx
                                                // du @rcx
  sechost!CredpUnmarshalandDecodeStringEx+0xbc  // 查看明文口令
                                                // p;db poi(@rbp+0x60) l dwo(@rbp+38)
    sechost!CredpDecodeSecretEx                 // 反混淆,r8此时等于0x10
                                                // 查看密文口令
                                                // db @rdx l @r8
    sechost!CredpDecodeSecretEx+0x132
      CRYPTBASE!SystemFunction041               // db @rcx l @rdx
                                                // 该函数就地反混淆(解密)
      CRYPTBASE!SystemFunction041+0x55
        ntdll!NtDeviceIoControlFile             // db poi(@rsp+0x38) l dwo(@rsp+0x40)
                                                // 访问"\Device\KsecDD"
                                                // ? qwo(CRYPTBASE!g_hKsecDD)

在lsass进程空间查看交互式登录的明文口令

bp sechost!CredpUnmarshalandDecodeStringEx+0xc1 "db poi(@rbp+0x60) l dwo(@rbp+0x38)"
bp sechost!CredUnprotectW+0x6a "db poi(@rsp+0x48) l dwo(@rsp+0x40)"

☆ winlogon的状态与信号

因为[1]提到了winlogon的状态与信号,这里也说一下Win10的情况。Win10没有Win7的StateMachineSetSignal,只有SignalManagerSetSignal

winlogon!WlStateMachineSetSignal
  winlogon!SignalManagerSetSignal

Win10的winlogon共有100个状态,41个信号

> dps winlogon!g_rpWLGeneric_States l 0n100
winlogon!g_xWLGeneric_Start_State
winlogon!g_xWLGeneric_NotifyCreateSession_State
winlogon!g_xWLGeneric_Welcome_State
....
winlogon!g_xWLGeneric_PseudoLogging_Off3_Unlock_State
winlogon!g_xWLGeneric_NotifyTerminateSession_State

> dqs winlogon!g_rpWLGeneric_Signals l 0n41
winlogon!g_xAction_Succeeded_Signal
winlogon!g_xAction_Failed_Signal
winlogon!g_xLogoff_NtUserCompleted_Signal
...
winlogon!g_xWLGeneric_ChangePassword_Signal
winlogon!g_xWLGeneric_ChangeIsAlreadyDone_Signal

☆ 后记

本文只研究了[1]在Win10中的变化,没有进一步拓展。有兴趣继续深入研究这个方向的朋友,可以参照框架图设断、动态调试。

☆ 参考资源

[1] Windows7口令认证流程调试 - boywhp [2013-02-14]
    https://bbs.pediy.com/thread-162632-1.htm

[2] LsaLogonUser function (ntsecapi.h)
    https:
//docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-lsalogonuser

    SECURITY_LOGON_TYPE enumeration (ntsecapi.h)
    https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/ne-ntsecapi-security_logon_type

    MSV1_0_INTERACTIVE_LOGON structure (ntsecapi.h)
    https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/ns-ntsecapi-msv1_0_interactive_logon

[3] CredProtectW function (wincred.h)
    https:
//docs.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-credprotectw

    CredUnprotectW function (wincred.h)
    https:
//docs.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-credunprotectw

    CRED_PROTECTION_TYPE enumeration (wincred.h)
    https://docs.microsoft.com/en-us/windows/win32/api/wincred/ne-wincred-cred_protection_type


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