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.

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)

Solutions

  1. Confirm the endpoint supports full CredSSP; fall back to a different RDP security level (e.g. TLS without NLA) if the server is nonstandard
  2. Check for earlier parse errors in the handshake that could desynchronize the TSRequest stream
  3. Update grdp — newer versions relax strict PubKeyAuth validation for Windows variant responses
  4. 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

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.