{"record":{"id":"072c4ba9009bd373","repo":"shadow1ng/fscan","slug":"nla-auth-failed-error-code-d-0x-x","errorCode":null,"errorMessage":"NLA auth failed: error code %d (0x%X)","messagePattern":"NLA auth failed: error code (.+?) \\(0x%X\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libs/grdp/protocol/tpkt/tpkt.go","lineNumber":336,"sourceCode":"}\n\n// ErrNLAAuthSuccess 表示NLA仅验证模式下认证成功（非真正错误）\nvar ErrNLAAuthSuccess = fmt.Errorf(\"NLA_AUTH_SUCCESS\")\n\nfunc (t *TPKT) recvPubKeyInc(data []byte) error {\n\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，直接返回","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/libs/grdp/protocol/tpkt/tpkt.go#L318-L354","documentation":"recvPubKeyInc inspects the server's TSRequest during NLA authentication; a nonzero ErrorCode means the server rejected the NTLM credentials (e.g. 0xC000006D STATUS_LOGON_FAILURE for a bad password). The library surfaces the raw NTSTATUS code so the caller can distinguish credential rejection from protocol failures.","triggerScenarios":"StartNLA with username/password/domain that the server rejects: wrong password, expired/locked account, user lacking RDP rights, or domain mis specification causing the NTLM exchange to fail server-side.","commonSituations":"Typo'd or rotated passwords; account lockout policies; connecting with a local account when a domain account is required (or vice versa); user not in 'Remote Desktop Users'; stale cached credentials in automation config.","solutions":["Verify username/password/domain by logging in interactively on the target machine","Use DOMAIN\\user or user@domain format consistently; try '.\\user' for local accounts","Check the account is not locked/expired and has Remote Desktop Users membership","Read the 0x%X code: 0xC000006D/0xC000006A = bad password, 0xC0000072 = disabled account, 0xC0000234 = locked out"],"exampleFix":"// before\nerr := client.Login(\"10.0.0.5\", \"admin\", \"password\")\nif err != nil { panic(err) }\n// after\nvar authErr *NLAAuthError\nif err := client.Login(\"10.0.0.5\", \"admin\", password); err != nil {\n    if errors.As(err, &authErr) && authErr.Code == 0xC000006D {\n        // bad credentials: re-prompt instead of retrying\n        return rePromptForCredentials()\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"if user == \"\" || pass == \"\" {\n    return errors.New(\"NLA requires non-empty credentials; supply username and password before calling Login\")\n}\nif !strings.Contains(user, \"\\\\\") && !strings.Contains(user, \"@\") && domain == \"\" {\n    // ambiguous account: will likely fail with STATUS_LOGON_FAILURE\n    log.Println(\"warning: no domain qualifier on username\")\n}","typeGuard":"func isLogonFailure(err error) bool {\n    var nlaErr *NLAAuthError\n    return errors.As(err, &nlaErr) && nlaErr.Code == 0xC000006D\n}","tryCatchPattern":"err := client.Login(host, user, pass)\nvar nlaErr *NLAAuthError\nif errors.As(err, &nlaErr) {\n    switch nlaErr.Code {\n    case 0xC000006D, 0xC000006A:\n        return rePromptCredentials()\n    case 0xC0000072:\n        return errors.New(\"account disabled\")\n    case 0xC0000234:\n        return errors.New(\"account locked out\")\n    }\n    return err\n}","preventionTips":["Test credentials interactively (mstsc) before wiring them into automation","Store credentials in a secrets manager, not hardcoded config, to survive rotation","Use consistent DOMAIN\\user or UPN formats matching what the server expects","Check account lockout/expiration policies when auth suddenly starts failing"],"tags":["rdp","nla","authentication","credentials"],"backgroundTag":"authentication-failed","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"}