兼职做ps网站,六六seo基础运营第三讲,贸易公司网址大全,wordpress 翻页404impacket工具解析之Kerberos认证协议
上一期我们介绍了impacket中ntlm协议的实现#xff0c;在Windows认证中除了使用ntlm认证#xff0c;还支持Kerberos认证协议#xff0c;Kerberos认证也是Windows 活动目录中占比最高的认证方式。 什么是Kerberos协议#xff1f;
Kerb…impacket工具解析之Kerberos认证协议
上一期我们介绍了impacket中ntlm协议的实现在Windows认证中除了使用ntlm认证还支持Kerberos认证协议Kerberos认证也是Windows 活动目录中占比最高的认证方式。 什么是Kerberos协议
Kerberos协议是一种网络身份验证协议用于在计算机网络环境中进行安全的身份验证和授权最初由麻省理工学院MIT开发。微软在Windows 操作系统中实现了 Kerberos 协议作为Windows Active DirectoryAD默认的身份验证协议。
ASN.1
我们在Kerberos协议RFC文档可以看到使用类似于以下格式的语句来描述Kerberos的消息结构。 PrincipalName:: SEQUENCE {name-type [0] Int32,name-string [1] SEQUENCE OF KerberosString}这种表示方法和ntlm协议直接使用字节排列的方式来描述有所不同该描述方式被称作ASN.1。
Kerberos 协议使用 ASN.1Abstract Syntax Notation One来定义和描述其消息格式和数据结构。ASN.1 是一种通用的数据表示和编码规范用于描述结构化数据和交换格式。
在 Kerberos 中ASN.1 用于定义各种消息类型、字段和数据结构。每个消息类型都有其自己的 ASN.1 定义包括消息的字段、类型、长度和顺序等信息。这些 ASN.1 定义规定了 Kerberos 消息的结构和编码规则。
01类型表示
ASN.1 定义了一套抽象的数据类型和结构包括简单类型如整数、字符串和复合类型如序列、集合。这些抽象数据类型提供了一种独立于具体实现的方式来描述数据结构。
简单类型
ASN.1定义了多种简单类型用于描述基本的数据类型。
INTEGERINTEGER 类型用于表示整数。它可以表示正整数、负整数或零。根据需要可以指定整数的取值范围和约束条件。BOOLEANBOOLEAN 类型用于表示布尔值即真或假。它只有两个取值TRUE 和 FALSE。ENUMERATEDENUMERATED 类型用于表示枚举类型其中每个枚举值都有一个对应的整数值。ENUMERATED 类型可以定义一组具体的枚举值并为每个值分配一个整数。NULLNULL 类型用于表示一个空值不包含任何数据。它通常用于表示缺少值或占位符。OCTET STRINGOCTET STRING 类型用于表示二进制数据通常是字节序列。它可以表示任意长度的字节流用于传输和存储二进制数据。BIT STRINGBIT STRING 类型用于表示位串即位的序列。它可以表示定长或变长的位序列用于表示比特字段和位标志。OBJECT IDENTIFIEROBJECT IDENTIFIER 类型用于表示对象标识符OID它是一个全局唯一的标识符用于标识实体、算法、属性等。UTF8StringUTF8String 类型用于表示以 UTF-8 编码的字符串。它可以包含任意 Unicode 字符并支持国际化字符集。
这些简单类型提供了一种通用的方式来表示和传输基本数据类型。ASN.1 还支持其他一些简单类型如实数、日期时间等以满足更具体的数据需求。
复合类型
除了简单类型ASN.1还提供了多种复合类型用于描述和组织结构化数据。以下是一些常见的 ASN.1 复合类型:
SEQUENCESEQUENCE 是最常用的复合类型之一它定义了一组按顺序排列的成员。每个成员可以是简单类型或其他复合类型。成员的顺序在序列化和解析数据时必须保持一致。SETSET 类型与 SEQUENCE 类似但是它不要求成员的顺序可以无序地存储和表示。在序列化和解析数据时SET 类型会根据成员的标签进行匹配。CHOICECHOICE 类型允许在一组成员中选择一个进行存储和表示。每个成员都有一个唯一的标签用于标识所选择的成员。CHOICE 类型只能存储和表示选择的成员而其他成员则被忽略。SEQUENCE OF 和 SET OFSEQUENCE OF 和 SET OF 类型允许定义一个成员的序列或集合。它们表示成员的重复出现可以包含零个或多个相同类型的成员。ARRAYARRAY 类型定义了一个固定长度的数组其中每个元素可以是简单类型或其他复合类型。ARRAY 类型在某些编程语言中可能与 SEQUENCE OF 或 SET OF 类型实现相似。
这些 ASN.1 复合类型提供了组织和描述结构化数据的灵活性和可扩展性。通过组合和嵌套这些复合类型可以构建复杂的数据结构以满足特定应用或协议的需求。
02编码
ASN.1 定义了不同的编码规则用于将抽象的数据类型转换为二进制格式进行传输和存储。常见的编码规则包括基于长度的编码BERBasic Encoding Rules、基于标记的编码DERDistinguished Encoding Rules、基于CER的编码CERCanonical Encoding Rules等。
其中Kerberos协议规定使用DER编码方式。
示例
我们可以通过impacket中使用的asn.1开源实现pyasn1来演示一些基本的asn1数据类型表示及数据编码。首先使用asn.1定义一个复合类型。
Record :: SEQUENCE { id INTEGER,room [0] INTEGER OPTIONAL,house [1] INTEGER DEFAULT 0}根据pyasn1提供的类描述该类型
from pyasn1.type.univ import *
from pyasn1.type.tag import *
from pyasn1.type.namedtype import *class Record(Sequence):componentType NamedTypes(NamedType(id, Integer()),OptionalNamedType(room, Integer().subtype(implicitTagTag(tagClassContext, tagFormatSimple, 0))),DefaultedNamedType(house, Integer(0).subtype(implicitTagTag(tagClassContext, tagFormatSimple, 1))))直接输出 record Record()
record[id] 123
record[room] 321
print(record)-----------------------------------
Record:
id123
room321pyasn1也实现了ber、der、cer编码方式例如使用der对该消息进行编码。 from pyasn1.codec.der import encoderencodedRecord encoder.encode(record)
print(encodedRecord.hex())----------------------------------------
300702017b80020141Kerberos数据类型
kerberos大部分的数据类型定义位于impacket/krb5/asn1.py中文件中实现了Kerberos协议中定义的简单类型如Int32、UInt32、Microseconds、KerberosString
class Int32(univ.Integer):subtypeSpec univ.Integer.subtypeSpec constraint.ValueRangeConstraint(-2147483648, 2147483647)class UInt32(univ.Integer):pass
subtypeSpec univ.Integer.subtypeSpec constraint.ValueRangeConstraint(0, 4294967295)class Microseconds(univ.Integer):subtypeSpec univ.Integer.subtypeSpec constraint.ValueRangeConstraint(0, 999999)class KerberosString(char.GeneralString):# TODO marc: Im not sure how to express this constraint in the API.# For now, we will be liberal in what we accept.# subtypeSpec constraint.PermittedAlphabetConstraint(char.IA5String())pass在简单类型的基础上通过序列组合形成多个复合类型如PrincipalName、HostAddress、AuthorizationData、PA_DATA等。
class PrincipalName(univ.Sequence):componentType namedtype.NamedTypes(_sequence_component(name-type, 0, Int32()),_sequence_component(name-string, 1,univ.SequenceOf(componentTypeKerberosString())))class HostAddress(univ.Sequence):componentType namedtype.NamedTypes(_sequence_component(addr-type, 0, Int32()),_sequence_component(address, 1, univ.OctetString()))class AuthorizationData(univ.SequenceOf):componentType univ.Sequence(componentTypenamedtype.NamedTypes(_sequence_component(ad-type, 0, Int32()),_sequence_component(ad-data, 1, univ.OctetString())))class PA_DATA(univ.Sequence):componentType namedtype.NamedTypes(_sequence_component(padata-type, 1, Int32()),_sequence_component(padata-value, 2, univ.OctetString()))01KDC_REQ
KDC_REQ用于客户端向KDC发送请求impacket中用于认证的请求AS_REQ和票据请求的TGS_REQ都通过继承KDC_REQ来实现自身结构该结构定义如下 KDC-REQ :: SEQUENCE {pvno [0] INTEGER (5),msg-type [1] INTEGER (10 -- AS --),padata [2] SEQUENCE OF PA-DATA OPTIONAL,req-body [3] KDC-REQ-BODY
}KDC-REQ中的字段有4种
pvno协议版本号表示 Kerberos 协议的版本。msg-type消息类型表示消息的类型对于 AS_REQ 结构它的值为 10表示 AS 请求。padata可选字段表示附加的认证数据Pre-Authentication Data。req-bodyKDC-REQ-BODY 结构包含了请求的主体信息如客户端的身份信息、请求的服务等。
KDC-REQ-BODY :: SEQUENCE {kdc-options [0] KDCOptions,cname [1] PrincipalName OPTIONAL,realm [2] Realm,sname [3] PrincipalName OPTIONAL,from [4] KerberosTime OPTIONAL,till [5] KerberosTime,rtime [6] KerberosTime OPTIONAL,nonce [7] UInt32,etype [8] SEQUENCE OF Int32 -- EncryptionTypeaddresses [9] HostAddresses OPTIONAL,enc-authorization-data [10] EncryptedData OPTIONAL,additional-tickets [11] SEQUENCE OF Ticket OPTIONAL
}KDC-REQ-BODY 结构中的字段包括
kdc-optionsKDC 选项表示请求的选项如是否要求可续期票据、是否要求使用预认证等。cname客户端名称表示客户端的主体名称。realm域表示域的名称。sname服务名称表示请求的服务的主体名称。from可选字段表示票据的有效起始时间。till票据的有效截止时间。rtime可选字段表示票据的可续期时间。nonce随机数用于防止重放攻击。etype加密类型表示请求的票据的加密类型。addresses可选字段表示客户端的网络地址。enc-authorization-data可选字段表示加密的授权数据。additional-tickets可选字段表示附加的票据。
02 KDC_REP
KDC_REP用于表示KDC向客户端发送的响应impacket中用于认证的请求AS_REP和票据请求的TGS_REP通过继承KDC_REP来实现自身结构该结构定义如下
KDC-REP :: SEQUENCE {pvno [0] INTEGER (5),msg-type [1] INTEGER (11 -- AS -- | 13 -- TGS --),padata [2] SEQUENCE OF PA-DATA OPTIONAL,crealm [3] Realm,cname [4] PrincipalName,ticket [5] Ticket,enc-part [6] EncryptedData
}KDC_REP 结构中的字段包括
pvno协议版本号表示 Kerberos 协议的版本。msg-type消息类型表示消息的类型。对于 KDC_REP 结构它的值可以是 11AS 响应或 13TGS 响应取决于是 AS 请求还是 TGS 请求的响应。padata可选字段表示附加的认证数据Pre-Authentication Data。crealm域realm表示响应的域的名称。cname客户端名称表示客户端的主体名称。ticket票据表示请求的票据Ticket Granting TicketTGT或服务票据Service Ticket。enc-part加密的部分表示加密的票据部分或认证数据。
Kerberos函数
在impacket库中用于Kerberos认证最常用的一些顶层函数位于impacket/krb5/kerberosv5.py中在文件中主要通过以下3个函数来实现Kerberos认证。
01 sendReceive
sendReceive是一个Kerberos消息发送和接收函数该函数除了与KDC的88端口建立通信连接外还对KDC的消息进行了一些处理我们可以看到在25行对接收到的数据包进行了解码在39行的判断中如果出现KDC_ERR_PREAUTH_REQUIRED以外的错误都会抛出异常。同时我们也可以看到这个函数会优先使用kdcHost参数作为连接目标只有kdcHost为None的情况下才会使用host作为连接目标kdcHost和host分别代表了目标ip和目标主机名。 在impacket中通常会将域名设置为hostkdcHost作为一个可选参数通过Kerberos认证时如果没有指定kdc地址会尝试通过解析域名来获取kdc地址所以如果在一台无法解析域名的机器上在没有指定kdcHost(一般是-dc-ip参数)的话就会导致连接失败这也是使用impacket一个经常出现的问题。
def sendReceive(data, host, kdcHost):if kdcHost is None:targetHost hostelse:targetHost kdcHostmessageLen struct.pack(!i, len(data))LOG.debug(Trying to connect to KDC at %s % targetHost)try:af, socktype, proto, canonname, sa socket.getaddrinfo(targetHost, 88, 0, socket.SOCK_STREAM)[0]s socket.socket(af, socktype, proto)s.connect(sa)except socket.error as e:raise socket.error(Connection error (%s:%s) % (targetHost, 88), e)s.sendall(messageLen data)recvDataLen struct.unpack(!i, s.recv(4))[0]r s.recv(recvDataLen)while len(r) recvDataLen:r s.recv(recvDataLen-len(r))try:krbError KerberosError(packet decoder.decode(r, asn1Spec KRB_ERROR())[0])except:return rif krbError.getErrorCode() ! constants.ErrorCodes.KDC_ERR_PREAUTH_REQUIRED.value:try:for i in decoder.decode(r):if type(i) Sequence:for k in vars(i)[_componentValues]:if type(k) GeneralizedTime:server_time datetime.datetime.strptime(k.asOctets().decode(utf-8), %Y%m%d%H%M%SZ)LOG.debug(Server time (UTC): %s % server_time)except:# Couldnt get server time for some reasonpassraise krbErrorreturn r02 getKerberosTGT
getKerberosTGT实现的部分比较长可以从其名称可以看出来是用于获取TGT。 def getKerberosTGT(clientName, password, domain, lmhash, nthash, aesKey, kdcHostNone, requestPACTrue):# Convert to binary form, just in case were receiving stringsif isinstance(lmhash, str):try:lmhash unhexlify(lmhash)except TypeError:passif isinstance(nthash, str):try:nthash unhexlify(nthash)except TypeError:passif isinstance(aesKey, str):try:aesKey unhexlify(aesKey)except TypeError:passasReq AS_REQ()...从这个函数里面我们也能看到一些重要的信息首先通过参数我们可以看到支持的凭据包括password、lmhash、nthash、aesKeylmhash实际没有参与运算。 在156行至174行这里是预认证过程发送的第一个AS_REQ加密方式的初始化可以看出来客户端支持的加密方式是由提供的凭据类型决定的输入的凭据对应的加密方式如下优先级从到下逐渐降低。 从加密方式的设置上也能看出来不管提供的是什么凭据AS_REQ中的加密方式始终只有一种但是在实际的Windows客户端一般都支持多种加密方式这也是一个比较明显的特征。 244行至268行是预身份验证中的第二个AS_REQ进行初始化在impacket中只实现了加密时间戳的认证方式这里也是对时间戳进行加密的核心部分可以看到使用的凭据优先级与上一个AS_REQ相同根据不同的凭据类型转换成对应的加密key加密时间戳使用的也是经过编码后的KerberosTime结构。 311至320行实现了加密方式的回滚在前面的代码中我们可以知道当指定了password也就是明文密码时会使用aes256加密方式而这里捕获了KDC_ERR_ETYPE_NOSUPP异常也就是KDC不支持aes256在这里的操作是将明文密码转换为hash再进行请求。
332-352行是对AS_REP的enc-part进行解密这里的enc-part是KDC使用客户端的密钥进行加密的这里所说的客户端就是AS_REQ中设置的cname在cname对应的用户UAC设置了DONT_REQ_PREAUTH的情况下没有设置预认证信息kdc也会返回使用该用户凭据加密的enc-part这时候就可以通过离线破解enc-part来获取用户的密钥这也就是aesreproating攻击的原理。在这个部分的代码作用主要就是获取sessionKey sessionKey作为下阶段TGS_REQ的认证加密密钥。最后返回的tgt实际上并不是协议描述的TGT而是整个AS_REP的数据包。
03 getKerberosTGS
getKerberosTGS函数用于获取服务票据也就是Kerberos认证的第二个阶段我们从函数的参数可以看到参数中一些名称正好是和getKerberosTGT的返回值相吻合的getKerberosTGT通过传参的设计实现了pthPass-the-Hash而在getKerberosTGS参数中的tgt实现pttPass-the-Ticket。 def getKerberosTGS(serverName, domain, kdcHost, tgt, cipher, sessionKey):# Decode the TGTtry:decodedTGT decoder.decode(tgt, asn1Spec AS_REP())[0]except:decodedTGT decoder.decode(tgt, asn1Spec TGS_REP())[0]domain domain.upper()# Extract the ticket from the TGTticket Ticket()ticket.from_asn1(decodedTGT[ticket])apReq AP_REQ()apReq[pvno] 5apReq[msg-type] int(constants.ApplicationTagNumbers.AP_REQ.value)在函数的开头部分对tgt进行了解码注意这里使用的结构并不是TGT而是AS_REP或者TGS_REP这是因为两种数据类型虽然不一样但是结构是完全相同的并且在AS_REP中的ticket与请求服务为krbtgt返回的TGS_REP中的ticket都可以当作TGT来使用。
367-400行涉及到两个重要结构的初始化AP_REQ和Authenticator在AP_REQ的ticket设置的是AS_REQ中获取的tgt而在Authenticator中设置了客户端主体、请求的spn、域名、时间戳等信息整个Authenticator使用上一步的sessionKey进行加密。 402-438行对TGS_REQ进行了初始化设置好票据选项、认证数据、域名、时间戳等信息后发送到KDC 442-468行中处理与getKerberosTGT类似首先是对TGS_REP进行解码在利用sessionKey对enc-part进行解密获取下一阶段的sessionKey以及服务票据等信息。在这里还进行了返回票据的判断如果返回票据的spn和请求中设置的spn不一致的话再进行递归调用。请求服务票据。 服务端根据请求的spn来确定服务主体如果spn存在的话就会返回TGS票据并且TGS票据中的ticket部分使用该服务主体的密钥进行加密绝大部分情况下服务主体都是计算机账户如果一个用户账户存在spn就可以利用TGS请求用户的服务票据这时候返回的TGS票据就会包含使用该用户的密钥进行加密的ticket部分此时就可以通过离线破解获取用户的密钥这种攻击手法就是kerberoasting攻击 以上就是impacket中kerberos实现部分主要包含了Kerberos消息编码、基础数据结构、消息类型及两个常用的助手函数。在Windows实际的认证中smb、rpc、ldap、http等服务都支持使用ntlm协议和Kerberos协议两种协议的认证信息如何嵌入到上述协议的数据包中以及认证数据的交互我们将在后续的应用层协议解析中再进行具体介绍。本文转自 公众号 网星安全 https://mp.weixin.qq.com/s/9biFHMHnw37xFG3fryFBZA