{"record":{"id":"4d461feb4435b4eb","repo":"shadow1ng/fscan","slug":"empty-certificate-chain","errorCode":null,"errorMessage":"empty certificate chain","messagePattern":"empty certificate chain","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/grdp/protocol/t125/gcc/gcc.go","lineNumber":410,"sourceCode":"\tp.SignatureBlob, _ = core.ReadBytes(int(p.SignatureBlobLen)-8, r)\n\tp.Padding, _ = core.ReadBytes(8, r)\n\n\treturn nil\n}\n\ntype CertBlob struct {\n\tCbCert uint32 `struc:\"little,sizeof=AbCert\"`\n\tAbCert []byte `struc:\"little\"`\n}\ntype X509CertificateChain struct {\n\tNumCertBlobs  uint32     `struc:\"little,sizeof=CertBlobArray\"`\n\tCertBlobArray []CertBlob `struc:\"little\"`\n\tPadding       []byte     `struc:\"[12]byte\"`\n}\n\nfunc (x *X509CertificateChain) GetPublicKey() (*rsa.PublicKey, error) {\n\tif len(x.CertBlobArray) == 0 {\n\t\treturn nil, errors.New(\"empty certificate chain\")\n\t}\n\tdata := x.CertBlobArray[len(x.CertBlobArray)-1].AbCert\n\tcert, err := x509.ParseCertificate(data)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parse certificate: %w\", err)\n\t}\n\tif cert.PublicKey == nil {\n\t\tvar pubKeyInfo struct {\n\t\t\tAlgorithm        pkix.AlgorithmIdentifier\n\t\t\tSubjectPublicKey asn1.BitString\n\t\t}\n\t\t_, err = asn1.Unmarshal(cert.RawSubjectPublicKeyInfo, &pubKeyInfo)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unmarshal public key info: %w\", err)\n\t\t}\n\t\trsaPublicKey, err := x509.ParsePKCS1PublicKey(pubKeyInfo.SubjectPublicKey.Bytes)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"parse PKCS1 public key: %w\", err)","sourceCodeStart":392,"sourceCodeEnd":428,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/libs/grdp/protocol/t125/gcc/gcc.go#L392-L428","documentation":"X509CertificateChain.GetPublicKey extracts the RSA public key from the server certificate chain carried in the CONNECT_RESPONSE. If the CertBlobArray is empty there is no certificate to parse, so this error is returned. This typically means the server sent no certificate chain where one was required for the chosen security path.","triggerScenarios":"Calling GetPublicKey on an X509CertificateChain parsed from a server_proposals/certificate blob whose CertBlobArray decoded to zero entries — e.g. the server chose a security path without sending an X.509 chain, or the struc decoding silently produced an empty array.","commonSituations":"Servers configured for non-standard security (e.g. hybird/NLA-only) that omit the server certificate in the expected structure; decoding desync causing the array length field to read as 0; connecting to gateways that use different certificate PDU types (e.g. proprietary/bogus certs) this struct does not capture.","solutions":["Verify the server's selected security protocol actually returns an X.509 certificate chain; if the server uses NLA/CredSSP, this code path may not apply — use the matching security handler","Hex-dump the certificate PDU and check certCount: if nonzero, fix the struc decoding (alignment/endianness) of CertBlobArray","Handle the alternate certificate types (SSCert/ProprietaryCert) instead of assuming X509CertificateChain","Update grdp or the server config so standard TLS/RDP security with a real certificate chain is negotiated"],"exampleFix":"// before\nif len(x.CertBlobArray) == 0 {\n    return nil, errors.New(\"empty certificate chain\")\n}\n\n// after\nif len(x.CertBlobArray) == 0 {\n    return nil, fmt.Errorf(\"empty certificate chain: server sent certCount=0 (security proto %v may not use X.509 certs)\", selectedProto)\n}","handlingStrategy":"type-guard","validationCode":"// check the certificate chain before extracting the key\nfunc hasCertChain(x *gcc.X509CertificateChain) bool {\n    return x != nil && len(x.CertBlobArray) > 0 && len(x.CertBlobArray[len(x.CertBlobArray)-1].AbCert) > 0\n}","typeGuard":"func validCertChain(x *gcc.X509CertificateChain) bool {\n    return x != nil && len(x.CertBlobArray) > 0\n}","tryCatchPattern":"pub, err := certChain.GetPublicKey()\nif err != nil {\n    if strings.Contains(err.Error(), \"empty certificate chain\") {\n        // server did not send an X.509 chain; switch security handler or abort\n        return ErrNoServerCertificate\n    }\n    return err\n}","preventionTips":["Check the negotiated security protocol before assuming an X.509 chain is present","Handle the alternate certificate PDU types (SSCert/Proprietary) in your connect logic","Verify certCount in the raw PDU to distinguish encoding bugs from genuinely absent certificates"],"tags":["rdp","tls","certificate","x509"],"backgroundTag":"empty-required-field","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}