shadow1ng/fscan · error
NLA auth failed: empty PubKeyAuth
Error message
NLA auth failed: empty PubKeyAuth
What it means
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.
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
Example fix
// before
if len(tsreq.PubKeyAuth) == 0 {
return fmt.Errorf("NLA auth failed: empty PubKeyAuth")
}
// after
if len(tsreq.PubKeyAuth) == 0 {
glog.Warning("empty PubKeyAuth; server may not send it — continuing")
return nil
} Defensive patterns
Strategy: try-catch
Validate before calling
// no pre-call validation possible; server behavior determines PubKeyAuth presence.
// Detect nonstandard endpoints ahead of time:
info, err := rdpProbeSecurityLevel(host) // e.g. via NLA/TLS negotiation probe
if err == nil && !info.SupportsCredSSP {
return fmt.Errorf("host %s does not fully support CredSSP; use TLS security level instead", host)
} Try / catch
if err := client.Login(host, user, pass); err != nil {
if strings.Contains(err.Error(), "empty PubKeyAuth") {
log.Println("server omitted PubKeyAuth; falling back to TLS security level")
return clientWithTLSOnly.Login(host, user, pass)
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/cafdfd8be23ee472.
Report an issue: GitHub.
Appendix: source
Thrown at libs/grdp/protocol/tpkt/tpkt.go:342
glog.Trace("recvPubKeyInc", hex.EncodeToString(data))
tsreq, err := nla.DecodeDERTRequest(data)
if err != nil {
glog.Info("DecodeDERTRequest", err)
return err
}
// 检查服务器是否返回错误码(认证失败)
// 常见错误码: 0xC000006D = STATUS_LOGON_FAILURE (密码错误)
if tsreq.ErrorCode != 0 {
glog.Error("NLA authentication failed with error code:", tsreq.ErrorCode)
return fmt.Errorf("NLA auth failed: error code %d (0x%X)", tsreq.ErrorCode, uint32(tsreq.ErrorCode))
}
// 验证 PubKeyAuth 不为空(认证成功的标志)
if len(tsreq.PubKeyAuth) == 0 {
glog.Error("NLA authentication failed: empty PubKeyAuth")
return fmt.Errorf("NLA auth failed: empty PubKeyAuth")
}
glog.Trace("PubKeyAuth:", tsreq.PubKeyAuth)
// 尝试解密验证公钥,但不作为强制失败条件
// 因为某些Windows版本的响应格式可能略有不同
pubkey := t.ntlmSec.GssDecrypt(tsreq.PubKeyAuth)
if pubkey == nil {
glog.Debug("GssDecrypt returned nil, but continuing since no ErrorCode was returned")
}
// NLA仅验证模式:凭据已验证成功,不发送credentials,直接返回
// 这样不会建立RDP会话,不会挤掉已登录用户
if t.nlaAuthOnly {
glog.Info("NLA auth-only mode: credentials verified, skipping session establishment")
return ErrNLAAuthSuccess
}
View on GitHub (pinned to 95cc12e753)