mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-24 20:21:52 +08:00
feat: RDP使用NLA仅验证模式,避免挤掉已登录用户
- 添加ErrNLAAuthSuccess标志用于NLA验证成功信号 - tpkt层支持nlaAuthOnly模式,验证成功后不建立完整会话 - x224层正确传播NLA验证结果 - rdpCrack改用NlaAuth进行凭据验证
This commit is contained in:
@@ -39,6 +39,7 @@ type TPKT struct {
|
||||
lastShortLength int
|
||||
fastPathListener core.FastPathListener
|
||||
ntlmSec *nla.NTLMv2Security
|
||||
nlaAuthOnly bool // NLA仅验证模式:验证成功后立即断开,不建立会话
|
||||
}
|
||||
|
||||
var OsVersion = map[string]string{
|
||||
@@ -98,6 +99,12 @@ func (t *TPKT) StartTLS() error {
|
||||
return t.Conn.StartTLS()
|
||||
}
|
||||
|
||||
// SetNLAAuthOnly 设置NLA仅验证模式
|
||||
// 启用后,NLA认证成功即返回,不发送credentials建立会话,不会挤掉已登录用户
|
||||
func (t *TPKT) SetNLAAuthOnly(authOnly bool) {
|
||||
t.nlaAuthOnly = authOnly
|
||||
}
|
||||
|
||||
func (t *TPKT) StartNLA() error {
|
||||
err := t.StartTLS()
|
||||
if err != nil {
|
||||
@@ -310,8 +317,12 @@ func (t *TPKT) recvChallenge(data []byte) error {
|
||||
return t.recvPubKeyInc(resp[:n])
|
||||
}
|
||||
|
||||
// ErrNLAAuthSuccess 表示NLA仅验证模式下认证成功(非真正错误)
|
||||
var ErrNLAAuthSuccess = fmt.Errorf("NLA_AUTH_SUCCESS")
|
||||
|
||||
func (t *TPKT) recvPubKeyInc(data []byte) error {
|
||||
glog.Trace("recvPubKeyInc", hex.EncodeToString(data))
|
||||
|
||||
tsreq, err := nla.DecodeDERTRequest(data)
|
||||
if err != nil {
|
||||
glog.Info("DecodeDERTRequest", err)
|
||||
@@ -319,9 +330,10 @@ func (t *TPKT) recvPubKeyInc(data []byte) error {
|
||||
}
|
||||
|
||||
// 检查服务器是否返回错误码(认证失败)
|
||||
// 常见错误码: 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", tsreq.ErrorCode)
|
||||
return fmt.Errorf("NLA auth failed: error code %d (0x%X)", tsreq.ErrorCode, uint32(tsreq.ErrorCode))
|
||||
}
|
||||
|
||||
// 验证 PubKeyAuth 不为空(认证成功的标志)
|
||||
@@ -332,11 +344,18 @@ func (t *TPKT) recvPubKeyInc(data []byte) error {
|
||||
|
||||
glog.Trace("PubKeyAuth:", tsreq.PubKeyAuth)
|
||||
|
||||
// 验证服务器返回的公钥(可选但推荐)
|
||||
// 尝试解密验证公钥,但不作为强制失败条件
|
||||
// 因为某些Windows版本的响应格式可能略有不同
|
||||
pubkey := t.ntlmSec.GssDecrypt(tsreq.PubKeyAuth)
|
||||
if pubkey == nil {
|
||||
glog.Error("NLA authentication failed: invalid PubKeyAuth signature")
|
||||
return fmt.Errorf("NLA auth failed: invalid PubKeyAuth")
|
||||
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
|
||||
}
|
||||
|
||||
domain, username, password := t.ntlm.GetEncodedCredentials()
|
||||
|
||||
@@ -409,7 +409,14 @@ func (x *X224) recvConnectionConfirm(s []byte) {
|
||||
err := x.transport.(*tpkt.TPKT).StartNLA()
|
||||
glog.Debug("nla end, err?:", err)
|
||||
if err != nil {
|
||||
// 检查是否是NLA仅验证模式的成功返回
|
||||
if err == tpkt.ErrNLAAuthSuccess {
|
||||
glog.Info("NLA auth-only mode: credentials verified successfully")
|
||||
x.Emit("error", err) // 通过 error 事件传播成功信号
|
||||
return
|
||||
}
|
||||
glog.Error("start NLA failed:", err)
|
||||
x.Emit("error", err)
|
||||
return
|
||||
}
|
||||
x.Emit("connect", x.selectedProtocol)
|
||||
|
||||
Reference in New Issue
Block a user