解析mimikatz sekurlsa::pth实现原理

解析mimikatz  sekurlsa::pth实现原理

前言

Starcross

Pass-The-Hash(hash传递攻击)是内网渗透实战中一种常见且高效的横向移动方法,现有的实现 Pass-The-Hash 的工具繁多,例如 Impacket 从流量数据层面自定义协议得以实现,mimikatz sekurlsa::pth模块则是通过修改内存的方法从而实现。 


了解并掌握Pass-The-Hash的代码实现细节有助于从更深层次的角度来理解渗透。在这篇文章中,我们将从mimikatz源码中逐步解析其sekurlsa::pth模块实现原理。


背景知识

Starcross

通过了解NTLM身份认证步骤我们可知,当客户端向服务端发起协商消息后,将接收到由服务端返还的challenge,客户端使用用户hash与challenge进行加密得到respond后,再将challenge,respond以及username等信息发送至服务端完成身份认证。我们不难发现,身份认证过程中计算respond所需要的只有用户的hash,整个过程中均不需要用户明文密码的参与。Pass-The-Hash正是借助"无需明文参与"这一身份认证特性才得以实现。


解析mimikatz  sekurlsa::pth实现原理


我们打开mimikatz的源码,聚焦到sekulsa::pth模块。mimikatz sekulsa::pth模块实现思路是将lsass中缓存的当前用户凭证,通过内存修改替换成攻击者所控制的NTLM hash,使用替换后的用户凭证进行请求网络资源的身份验证。 


那么如何才能准确定位到lsass中所缓存hash的具体位置呢?


解析mimikatz  sekurlsa::pth实现原理


mimikatz 通过标识 Lsasrv.dll 里面的全局变量(

LogonSessionList 和 LogonSessionListCount

)并且通过扫描内存获取它们的具体地址。LogonSessionListCount为Windows logon session的数目,LogonSessionList为指向KIWI_MSV1_0_LIST结构链表(Windows logon session)的指针。


typedef struct _KIWI_MSV1_0_CREDENTIALS {
  struct _KIWI_MSV1_0_CREDENTIALS *next;
  DWORD AuthenticationPackageId;
  PKIWI_MSV1_0_PRIMARY_CREDENTIALS PrimaryCredentials;
} KIWI_MSV1_0_CREDENTIALS, *PKIWI_MSV1_0_CREDENTIALS;
 
typedef struct _KIWI_MSV1_0_PRIMARY_CREDENTIALS {
  struct _KIWI_MSV1_0_PRIMARY_CREDENTIALS *next;
  ANSI_STRING Primary;
  LSA_UNICODE_STRING Credentials;
} KIWI_MSV1_0_PRIMARY_CREDENTIALS,*PKIWI_MSV1_0_PRIMARY_CREDENTIALS;

KIWI_MSV1_0_LIST.Credentials 指向 KIWI_MSV1_0_CREDENTIALS 结构,再通过KIWI_MSV1_0_CREDENTIALS.PrimaryCredentials,
找到KIWI_MSV1_0_PRIMARY_CREDENTIALS 结构体,KIWI_MSV1_0_PRIMARY_CREDENTIALS.Credentials.Buffer 指向搜寻目标缓存凭证的地址。


解析mimikatz  sekurlsa::pth实现原理


所获取的凭证是经过加密处理的,那么这就意味着,如需成功替换NTLM hash,我们首先要清楚lsass加密凭证所用的加密方法以及获取加密密钥。


对凭证加密所用为lsasrv!LsaProtectMemory函数,然而LsaProtectMemory函数实际上是对 LsaEncryptMemory 函数的一个调用,并且LsaEncryptMemory函数又是对 BCryptEncrypt 函数的封装。BCryptEncrypt函数会根据待加密的数据块长度来选择加密算法,如果待加密数据长度能被8整除,那么就会使用AES算法。否则会使用3Des算法。


mimikatz使用kuhl_m_sekurlsa_nt6_LsaProtectMemory函数完成了上述的加密算法以及加密过程。



VOID WINAPI kuhl_m_sekurlsa_nt6_LsaProtectMemory(IN PVOID Buffer, IN ULONG BufferSize)
{
  kuhl_m_sekurlsa_nt6_LsaEncryptMemory((PUCHAR) Buffer, BufferSize, TRUE);
}

lsasrv!LsaInitializeProtectedMemory函数则是负责对密钥的生成,mimikatz同样是使用标识特征的方法内存搜寻LsaInitializeProtectedMemory函数从而获取密钥。


解析过程

Starcross

 mimikatz输入以下命令  


sekurlsa::pth /user:root /domain:workgroup 
/ntlm:86199d1d8f82957695a40fbe62d3fd8f


定位到 sekurlsa::pth 模块入口 NTSTATUS 

kuhl_m_sekurlsa_pth函数,解析user、ntlm、domain等参数后,调用kull_m_process_create函数,实际上是 CreateProcessWithLogonW  API 的调用(将user、domain作为参数传入CreateProcessWithLogonW),创建指定凭证的进程,并对进程进行挂起。


紧接着调用OpenProcessToken API获取所创建进程的句柄,通过调用 GetTokenInformation API 来获取进程的 AuthenticationId( 此处用于下文所提到的LogonId 的正确校验 ),接下来调用kuhl_m_sekurlsa_pth_luid函数。


if(kull_m_process_create(KULL_M_PROCESS_CREATE_LOGON, szRun, CREATE_SUSPENDED, NULL, LOGON_NETCREDENTIALS_ONLY, szUser, szDomain, L"", &processInfos, FALSE))
      {
        kprintf(L" | PID %u\n | TID %u\n",processInfos.dwProcessId, processInfos.dwThreadId);
        if(OpenProcessToken(processInfos.hProcess, TOKEN_READ | (isImpersonate ? TOKEN_DUPLICATE : 0), &hToken))
        {
          if(GetTokenInformation(hToken, TokenStatistics, &tokenStats, sizeof(tokenStats), &dwNeededSize))
          {
            kuhl_m_sekurlsa_pth_luid(&data);


kuhl_m_sekurlsa_pth_luid 函数首先调用 

kuhl_m_sekurlsa_acquireLSA函数获取lsass进程中的基本信息,如lsass进程句柄、下文所需的加密密钥以及LogonSessionList的地址等。接下调用kuhl_m_sekurlsa_enum函数。


kuhl_m_sekurlsa_enum(kuhl_m_sekurlsa_enum_callback_msv_pth, data);


kuhl_m_sekurlsa_enum 函数先后通过调用  kull_m_memory_copy函数读取lsass进程中LogonSessionListCount以及LogonSessionList的值。遍历LogonSessionList链表,调用回调函数kuhl_m_sekurlsa_enum_callback_msv_pth,通过校验LogonId确定是否找到正确的KIWI_MSV1_0_LIST。如果LogonId正确,则继续调用kuhl_m_sekurlsa_msv_enum_cred函数。


if(SecEqualLuid(pData->LogonId, pthData->LogonId))
  {
    kuhl_m_sekurlsa_msv_enum_cred(pData->cLsass, pData->pCredentials, kuhl_m_sekurlsa_msv_enum_cred_callback_pth, &credData);
    return FALSE;
  }
  else return TRUE;


kuhl_m_sekurlsa_msv_enum_cred函数则是同样先后调用kull_m_memory_copy函数继续读取lsass进程中KIWI_MSV1_0_CREDENTIALS以及KIWI_MSV1_0_PRIMARY_CREDENTIALS的值。紧接着调用

kuhl_m_sekurlsa_msv_enum_cred_callback_pth回调函数对所获取凭证进行解密,对解密后的凭证进行修改、替换,如LM、SHA以及DPAPI保护属性设置为FALSE,长度设置为0,表明凭证不包含这些hash值。isNtOwfPassword属性设置为TRUE,表明凭证中存在NTLM hash,NtOwfPassword设置为被替换的NTLM hash。接着使用kuhl_m_sekurlsa_nt6_LsaProtectMemory函数对修改、替换后的凭证做加密处理,再写入lsass进程中,覆盖其原有凭证。


*(PBOOLEAN) (msvCredentials + helper->offsetToisLmOwfPassword) = FALSE;
      if(helper->offsetToisIso)
        *(PBOOLEAN) (msvCredentials + helper->offsetToisIso) = FALSE;
      if(helper->offsetToisDPAPIProtected)
      {
        *(PBOOLEAN) (msvCredentials + helper->offsetToisDPAPIProtected) = FALSE;
        RtlZeroMemory(msvCredentials + helper->offsetToDPAPIProtected, LM_NTLM_HASH_LENGTH);
      }
      RtlZeroMemory(msvCredentials + helper->offsetToLmOwfPassword, LM_NTLM_HASH_LENGTH);
      RtlZeroMemory(msvCredentials + helper->offsetToShaOwPassword, SHA_DIGEST_LENGTH);
      if(pthDataCred->pthData->NtlmHash)
      {
        *(PBOOLEAN) (msvCredentials + helper->offsetToisNtOwfPassword) = TRUE;
        RtlCopyMemory(msvCredentials + helper->offsetToNtOwfPassword, pthDataCred->pthData->NtlmHash, LM_NTLM_HASH_LENGTH);
      }
      else
      {
        *(PBOOLEAN) (msvCredentials + helper->offsetToisNtOwfPassword) = FALSE;
        RtlZeroMemory(msvCredentials + helper->offsetToNtOwfPassword, LM_NTLM_HASH_LENGTH);
      }
      (*pthDataCred->pSecData->lsassLocalHelper->pLsaProtectMemory)(msvCredentials, pCredentials->Credentials.Length);

最后返回到 kuhl_m_sekurlsa_pth 函数,

NtResumeProcess(processInfos.hProcess)恢复开始时挂起的进程即完成凭证的修改,输入命令如dir,即成功完成一次hash传递攻击。


总结

Starcross

mimikatzt 作为渗透神兵,赫赫有名,却也因此被各大杀软围追堵截,导致其免杀性并不理想。了解了sekulsa::pth模块的代码细节以及实现原理后,借鉴神兵,我们可以根据场景的不同,灵活打造属于自己的Pass-The-Hash、明文获取自定义武器。

参考链接:

https://www.ampliasecurity.com/research/WCE_Internals_RootedCon2011_ampliasecurity.pdf


https://blog.xpnsec.com/exploring-mimikatz-part-1/


解析mimikatz  sekurlsa::pth实现原理

往期精彩

招聘 | 寻找发光的你(京东卡、机械键盘、小米手环等你来拿!)
论复杂方式解决简单问题——rwctf game 2048
记一次Postgresql从堆叠注入到RCE
Guess | 猜猜小阑在地铁站邂逅了什么?
CVE-2018-8440分析

解析mimikatz  sekurlsa::pth实现原理

星阑科技

微信号|StarCrossCN

知乎号 | 星阑科技


版权归原作者所有,如若转载,请注明出处:https://www.ciocso.com/article/518.html

发表评论

登录后才能评论
跳至工具栏