fix: 修复RDP爆破高误报率问题 (#555)

- 移除 screen.go 中错误的认证结果覆盖逻辑
- 启用 NLA 协议的 ErrorCode 字段检测
- 添加 PubKeyAuth 验证确保认证真正成功
- 修复 io.go 中错误被静默忽略的问题
- 修复 socket.go/io.go 中可能导致 panic 的代码
- 修复 screen.go 中文件句柄泄漏和 log.Panic
This commit is contained in:
ZacharyZcR
2026-01-14 22:54:31 +08:00
parent 98c6ec3e2f
commit 90c7f9c27e
5 changed files with 47 additions and 32 deletions
+8 -5
View File
@@ -26,7 +26,10 @@ func ReadBytes(len int, r io.Reader) ([]byte, error) {
func ReadByte(r io.Reader) (byte, error) {
b, err := ReadBytes(1, r)
return b[0], err
if err != nil || len(b) == 0 {
return 0, err
}
return b[0], nil
}
func ReadUInt8(r io.Reader) (uint8, error) {
@@ -42,7 +45,7 @@ func ReadUint16LE(r io.Reader) (uint16, error) {
b := make([]byte, 2)
_, err := io.ReadFull(r, b)
if err != nil {
return 0, nil
return 0, err
}
return binary.LittleEndian.Uint16(b), nil
}
@@ -51,7 +54,7 @@ func ReadUint16BE(r io.Reader) (uint16, error) {
b := make([]byte, 2)
_, err := io.ReadFull(r, b)
if err != nil {
return 0, nil
return 0, err
}
return binary.BigEndian.Uint16(b), nil
}
@@ -60,7 +63,7 @@ func ReadUInt32LE(r io.Reader) (uint32, error) {
b := make([]byte, 4)
_, err := io.ReadFull(r, b)
if err != nil {
return 0, nil
return 0, err
}
return binary.LittleEndian.Uint32(b), nil
}
@@ -69,7 +72,7 @@ func ReadUInt32BE(r io.Reader) (uint32, error) {
b := make([]byte, 4)
_, err := io.ReadFull(r, b)
if err != nil {
return 0, nil
return 0, err
}
return binary.BigEndian.Uint32(b), nil
}
+8 -1
View File
@@ -69,6 +69,13 @@ func (s *SocketLayer) TlsPubKey() ([]byte, error) {
if s.tlsConn == nil {
return nil, errors.New("TLS conn does not exist")
}
pub := s.tlsConn.ConnectionState().PeerCertificates[0].PublicKey.(*rsa.PublicKey)
certs := s.tlsConn.ConnectionState().PeerCertificates
if len(certs) == 0 {
return nil, errors.New("no peer certificates")
}
pub, ok := certs[0].PublicKey.(*rsa.PublicKey)
if !ok {
return nil, errors.New("invalid public key type")
}
return asn1ber.Marshal(*pub)
}
+9 -23
View File
@@ -323,7 +323,6 @@ func (g *Client) ScreenShot(domain, user, pwd string, timeout int64, rdpProtocol
now := start
screenImage := image.NewRGBA(image.Rect(0, 0, pic_length, pic_width))
index := 1
targetSlice := strings.Split(g.Host, ":")
ip := targetSlice[0]
port := targetSlice[1]
@@ -489,11 +488,7 @@ loop:
}
glog.Debug("循环结束,总时间过去了:", time.Since(start))
if g.x224.ServerChooseProtocol() == x224.PROTOCOL_HYBRID && g.x224.ServerChooseProtocol() == x224.PROTOCOL_HYBRID {
if err == nil {
status = true
}
}
// 认证结果由 success 事件回调设置,不在此处覆盖
if needReconnect {
return status, err, reconnProtocol
@@ -501,26 +496,27 @@ loop:
glog.Info("get screen ok")
// Encode to jpeg.
var imageBuf bytes.Buffer
err = jpeg.Encode(&imageBuf, screenImage, nil)
if err != nil {
log.Panic(err)
encodeErr := jpeg.Encode(&imageBuf, screenImage, nil)
if encodeErr != nil {
glog.Error("Failed to encode screenshot:", encodeErr)
return status, err, reconnProtocol
}
// Write to file.
saveDate := time.Now().Format("2006_01_02_15_04_05")
fo, writeErr := os.Create(fmt.Sprintf("%s/%s_%s_%s.jpg", OutputDir, ip, port, saveDate))
index += 1
if writeErr != nil {
glog.Error("Can not create rdp screenshot file:", writeErr)
} else {
defer fo.Close()
fw := bufio.NewWriter(fo)
_, writeErr := fw.Write(imageBuf.Bytes())
if writeErr != nil {
glog.Error("Can not write rdp screenshot file:", writeErr)
} else {
fw.Flush()
}
}
}
return status, err, reconnProtocol
@@ -529,7 +525,6 @@ loop:
func (g *Client) Crack(domain, user, pwd string, timeout int64, rdpProtocol uint32) (status bool, err error, reconnProtocol uint32) {
//glog.SetLevel(glog.ERROR)
reconnProtocol = rdpProtocol
needReconnect := false
refresh := make(chan bool)
exitFlag := make(chan bool)
start := time.Now()
@@ -570,7 +565,6 @@ func (g *Client) Crack(domain, user, pwd string, timeout int64, rdpProtocol uint
g.x224.SetRequestedProtocol(rdpProtocol) //x224.PROTOCOL_SSL , x224.PROTOCOL_RDP , x224.PROTOCOL_HYBRID , x224.PROTOCOL_HYBRID_EX
g.x224.On("reconnect", func(protocol uint32) {
needReconnect = true
reconnProtocol = protocol
glog.Info("need reconnect with protocol:", protocol)
g.pdu.Emit("close")
@@ -649,14 +643,6 @@ loop:
}
glog.Debug("循环结束,总时间过去了:", time.Since(start))
if g.x224.ServerChooseProtocol() == x224.PROTOCOL_HYBRID && g.x224.ServerChooseProtocol() == x224.PROTOCOL_HYBRID {
if err == nil {
status = true
}
}
if needReconnect {
return status, err, reconnProtocol
}
// 认证结果由 success 事件回调设置,不在此处覆盖
return status, err, reconnProtocol
}
+1 -1
View File
@@ -15,7 +15,7 @@ type TSRequest struct {
NegoTokens []NegoToken `asn1:"optional,explicit,tag:1"`
AuthInfo []byte `asn1:"optional,explicit,tag:2"`
PubKeyAuth []byte `asn1:"optional,explicit,tag:3"`
//ErrorCode int `asn1:"optional,explicit,tag:4"`
ErrorCode int `asn1:"optional,explicit,tag:4"`
}
type TSCredentials struct {
+21 -2
View File
@@ -317,9 +317,28 @@ func (t *TPKT) recvPubKeyInc(data []byte) error {
glog.Info("DecodeDERTRequest", err)
return err
}
// 检查服务器是否返回错误码(认证失败)
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)
}
// 验证 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)
//ignore
//pubkey := t.ntlmSec.GssDecrypt([]byte(tsreq.PubKeyAuth))
// 验证服务器返回的公钥(可选但推荐)
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")
}
domain, username, password := t.ntlm.GetEncodedCredentials()
credentials := nla.EncodeDERTCredentials(domain, username, password)
authInfo := t.ntlmSec.GssEncrypt(credentials)