{"record":{"id":"cafdfd8be23ee472","repo":"shadow1ng/fscan","slug":"nla-auth-failed-empty-pubkeyauth","errorCode":null,"errorMessage":"NLA auth failed: empty PubKeyAuth","messagePattern":"NLA auth failed: empty PubKeyAuth","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/grdp/protocol/tpkt/tpkt.go","lineNumber":342,"sourceCode":"\tglog.Trace(\"recvPubKeyInc\", hex.EncodeToString(data))\n\n\ttsreq, err := nla.DecodeDERTRequest(data)\n\tif err != nil {\n\t\tglog.Info(\"DecodeDERTRequest\", err)\n\t\treturn err\n\t}\n\n\t// 检查服务器是否返回错误码（认证失败）\n\t// 常见错误码: 0xC000006D = STATUS_LOGON_FAILURE (密码错误)\n\tif tsreq.ErrorCode != 0 {\n\t\tglog.Error(\"NLA authentication failed with error code:\", tsreq.ErrorCode)\n\t\treturn fmt.Errorf(\"NLA auth failed: error code %d (0x%X)\", tsreq.ErrorCode, uint32(tsreq.ErrorCode))\n\t}\n\n\t// 验证 PubKeyAuth 不为空（认证成功的标志）\n\tif len(tsreq.PubKeyAuth) == 0 {\n\t\tglog.Error(\"NLA authentication failed: empty PubKeyAuth\")\n\t\treturn fmt.Errorf(\"NLA auth failed: empty PubKeyAuth\")\n\t}\n\n\tglog.Trace(\"PubKeyAuth:\", tsreq.PubKeyAuth)\n\n\t// 尝试解密验证公钥，但不作为强制失败条件\n\t// 因为某些Windows版本的响应格式可能略有不同\n\tpubkey := t.ntlmSec.GssDecrypt(tsreq.PubKeyAuth)\n\tif pubkey == nil {\n\t\tglog.Debug(\"GssDecrypt returned nil, but continuing since no ErrorCode was returned\")\n\t}\n\n\t// NLA仅验证模式：凭据已验证成功，不发送credentials，直接返回\n\t// 这样不会建立RDP会话，不会挤掉已登录用户\n\tif t.nlaAuthOnly {\n\t\tglog.Info(\"NLA auth-only mode: credentials verified, skipping session establishment\")\n\t\treturn ErrNLAAuthSuccess\n\t}\n","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/libs/grdp/protocol/tpkt/tpkt.go#L324-L360","documentation":"After the ErrorCode check, recvPubKeyInc requires a non-empty PubKeyAuth field, treating it as the marker that the server completed the CredSSP public-key exchange. An empty PubKeyAuth means the server's TSRequest did not carry the expected encrypted public key material, so the handshake cannot be validated and NLA is considered failed.","triggerScenarios":"StartNLA against a server that returns a TSRequest with ErrorCode==0 but no PubKeyAuth payload — e.g. nonstandard CredSSP implementations, servers aborting the exchange early, or a response parsed from wrong byte offsets so fields decode as empty.","commonSituations":"Connecting to appliances/thin-server RDP implementations with partial CredSSP support; version drift in Windows CredSSP behavior; stream desync from earlier misparsed NTLM messages shifting TSRequest field boundaries.","solutions":["Confirm the endpoint supports full CredSSP; fall back to a different RDP security level (e.g. TLS without NLA) if the server is nonstandard","Check for earlier parse errors in the handshake that could desynchronize the TSRequest stream","Update grdp — newer versions relax strict PubKeyAuth validation for Windows variant responses","Packet-capture a successful mstsc login and compare the TSRequest structure byte-for-byte"],"exampleFix":"// before\nif len(tsreq.PubKeyAuth) == 0 {\n    return fmt.Errorf(\"NLA auth failed: empty PubKeyAuth\")\n}\n// after\nif len(tsreq.PubKeyAuth) == 0 {\n    glog.Warning(\"empty PubKeyAuth; server may not send it — continuing\")\n    return nil\n}","handlingStrategy":"try-catch","validationCode":"// no pre-call validation possible; server behavior determines PubKeyAuth presence.\n// Detect nonstandard endpoints ahead of time:\ninfo, err := rdpProbeSecurityLevel(host) // e.g. via NLA/TLS negotiation probe\nif err == nil && !info.SupportsCredSSP {\n    return fmt.Errorf(\"host %s does not fully support CredSSP; use TLS security level instead\", host)\n}","typeGuard":null,"tryCatchPattern":"if err := client.Login(host, user, pass); err != nil {\n    if strings.Contains(err.Error(), \"empty PubKeyAuth\") {\n        log.Println(\"server omitted PubKeyAuth; falling back to TLS security level\")\n        return clientWithTLSOnly.Login(host, user, pass)\n    }\n    return err\n}","preventionTips":["Probe the target's CredSSP support before choosing NLA as the security level","Keep grdp updated — newer builds tolerate Windows variant responses","Re-run the handshake once: transient stream desync from earlier parse errors can empty fields","Prefer servers verified to complete NLA with a standard RDP client"],"tags":["rdp","nla","credssp","protocol-parsing"],"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-14T05:17:10.506Z"}