mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-26 13:11:53 +08:00
fix: 修复RDP爆破高误报率问题 (#555)
- 移除 screen.go 中错误的认证结果覆盖逻辑 - 启用 NLA 协议的 ErrorCode 字段检测 - 添加 PubKeyAuth 验证确保认证真正成功 - 修复 io.go 中错误被静默忽略的问题 - 修复 socket.go/io.go 中可能导致 panic 的代码 - 修复 screen.go 中文件句柄泄漏和 log.Panic
This commit is contained in:
@@ -26,7 +26,10 @@ func ReadBytes(len int, r io.Reader) ([]byte, error) {
|
|||||||
|
|
||||||
func ReadByte(r io.Reader) (byte, error) {
|
func ReadByte(r io.Reader) (byte, error) {
|
||||||
b, err := ReadBytes(1, r)
|
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) {
|
func ReadUInt8(r io.Reader) (uint8, error) {
|
||||||
@@ -42,7 +45,7 @@ func ReadUint16LE(r io.Reader) (uint16, error) {
|
|||||||
b := make([]byte, 2)
|
b := make([]byte, 2)
|
||||||
_, err := io.ReadFull(r, b)
|
_, err := io.ReadFull(r, b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil
|
return 0, err
|
||||||
}
|
}
|
||||||
return binary.LittleEndian.Uint16(b), nil
|
return binary.LittleEndian.Uint16(b), nil
|
||||||
}
|
}
|
||||||
@@ -51,7 +54,7 @@ func ReadUint16BE(r io.Reader) (uint16, error) {
|
|||||||
b := make([]byte, 2)
|
b := make([]byte, 2)
|
||||||
_, err := io.ReadFull(r, b)
|
_, err := io.ReadFull(r, b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil
|
return 0, err
|
||||||
}
|
}
|
||||||
return binary.BigEndian.Uint16(b), nil
|
return binary.BigEndian.Uint16(b), nil
|
||||||
}
|
}
|
||||||
@@ -60,7 +63,7 @@ func ReadUInt32LE(r io.Reader) (uint32, error) {
|
|||||||
b := make([]byte, 4)
|
b := make([]byte, 4)
|
||||||
_, err := io.ReadFull(r, b)
|
_, err := io.ReadFull(r, b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil
|
return 0, err
|
||||||
}
|
}
|
||||||
return binary.LittleEndian.Uint32(b), nil
|
return binary.LittleEndian.Uint32(b), nil
|
||||||
}
|
}
|
||||||
@@ -69,7 +72,7 @@ func ReadUInt32BE(r io.Reader) (uint32, error) {
|
|||||||
b := make([]byte, 4)
|
b := make([]byte, 4)
|
||||||
_, err := io.ReadFull(r, b)
|
_, err := io.ReadFull(r, b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil
|
return 0, err
|
||||||
}
|
}
|
||||||
return binary.BigEndian.Uint32(b), nil
|
return binary.BigEndian.Uint32(b), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,13 @@ func (s *SocketLayer) TlsPubKey() ([]byte, error) {
|
|||||||
if s.tlsConn == nil {
|
if s.tlsConn == nil {
|
||||||
return nil, errors.New("TLS conn does not exist")
|
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)
|
return asn1ber.Marshal(*pub)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -323,7 +323,6 @@ func (g *Client) ScreenShot(domain, user, pwd string, timeout int64, rdpProtocol
|
|||||||
now := start
|
now := start
|
||||||
screenImage := image.NewRGBA(image.Rect(0, 0, pic_length, pic_width))
|
screenImage := image.NewRGBA(image.Rect(0, 0, pic_length, pic_width))
|
||||||
|
|
||||||
index := 1
|
|
||||||
targetSlice := strings.Split(g.Host, ":")
|
targetSlice := strings.Split(g.Host, ":")
|
||||||
ip := targetSlice[0]
|
ip := targetSlice[0]
|
||||||
port := targetSlice[1]
|
port := targetSlice[1]
|
||||||
@@ -489,11 +488,7 @@ loop:
|
|||||||
}
|
}
|
||||||
glog.Debug("循环结束,总时间过去了:", time.Since(start))
|
glog.Debug("循环结束,总时间过去了:", time.Since(start))
|
||||||
|
|
||||||
if g.x224.ServerChooseProtocol() == x224.PROTOCOL_HYBRID && g.x224.ServerChooseProtocol() == x224.PROTOCOL_HYBRID {
|
// 认证结果由 success 事件回调设置,不在此处覆盖
|
||||||
if err == nil {
|
|
||||||
status = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if needReconnect {
|
if needReconnect {
|
||||||
return status, err, reconnProtocol
|
return status, err, reconnProtocol
|
||||||
@@ -501,26 +496,27 @@ loop:
|
|||||||
glog.Info("get screen ok")
|
glog.Info("get screen ok")
|
||||||
// Encode to jpeg.
|
// Encode to jpeg.
|
||||||
var imageBuf bytes.Buffer
|
var imageBuf bytes.Buffer
|
||||||
err = jpeg.Encode(&imageBuf, screenImage, nil)
|
encodeErr := jpeg.Encode(&imageBuf, screenImage, nil)
|
||||||
|
if encodeErr != nil {
|
||||||
if err != nil {
|
glog.Error("Failed to encode screenshot:", encodeErr)
|
||||||
log.Panic(err)
|
return status, err, reconnProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write to file.
|
// Write to file.
|
||||||
saveDate := time.Now().Format("2006_01_02_15_04_05")
|
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))
|
fo, writeErr := os.Create(fmt.Sprintf("%s/%s_%s_%s.jpg", OutputDir, ip, port, saveDate))
|
||||||
index += 1
|
|
||||||
if writeErr != nil {
|
if writeErr != nil {
|
||||||
glog.Error("Can not create rdp screenshot file:", writeErr)
|
glog.Error("Can not create rdp screenshot file:", writeErr)
|
||||||
} else {
|
} else {
|
||||||
|
defer fo.Close()
|
||||||
fw := bufio.NewWriter(fo)
|
fw := bufio.NewWriter(fo)
|
||||||
_, writeErr := fw.Write(imageBuf.Bytes())
|
_, writeErr := fw.Write(imageBuf.Bytes())
|
||||||
if writeErr != nil {
|
if writeErr != nil {
|
||||||
glog.Error("Can not write rdp screenshot file:", writeErr)
|
glog.Error("Can not write rdp screenshot file:", writeErr)
|
||||||
|
} else {
|
||||||
|
fw.Flush()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return status, err, reconnProtocol
|
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) {
|
func (g *Client) Crack(domain, user, pwd string, timeout int64, rdpProtocol uint32) (status bool, err error, reconnProtocol uint32) {
|
||||||
//glog.SetLevel(glog.ERROR)
|
//glog.SetLevel(glog.ERROR)
|
||||||
reconnProtocol = rdpProtocol
|
reconnProtocol = rdpProtocol
|
||||||
needReconnect := false
|
|
||||||
refresh := make(chan bool)
|
refresh := make(chan bool)
|
||||||
exitFlag := make(chan bool)
|
exitFlag := make(chan bool)
|
||||||
start := time.Now()
|
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.SetRequestedProtocol(rdpProtocol) //x224.PROTOCOL_SSL , x224.PROTOCOL_RDP , x224.PROTOCOL_HYBRID , x224.PROTOCOL_HYBRID_EX
|
||||||
g.x224.On("reconnect", func(protocol uint32) {
|
g.x224.On("reconnect", func(protocol uint32) {
|
||||||
needReconnect = true
|
|
||||||
reconnProtocol = protocol
|
reconnProtocol = protocol
|
||||||
glog.Info("need reconnect with protocol:", protocol)
|
glog.Info("need reconnect with protocol:", protocol)
|
||||||
g.pdu.Emit("close")
|
g.pdu.Emit("close")
|
||||||
@@ -649,14 +643,6 @@ loop:
|
|||||||
}
|
}
|
||||||
glog.Debug("循环结束,总时间过去了:", time.Since(start))
|
glog.Debug("循环结束,总时间过去了:", time.Since(start))
|
||||||
|
|
||||||
if g.x224.ServerChooseProtocol() == x224.PROTOCOL_HYBRID && g.x224.ServerChooseProtocol() == x224.PROTOCOL_HYBRID {
|
// 认证结果由 success 事件回调设置,不在此处覆盖
|
||||||
if err == nil {
|
|
||||||
status = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if needReconnect {
|
|
||||||
return status, err, reconnProtocol
|
|
||||||
}
|
|
||||||
return status, err, reconnProtocol
|
return status, err, reconnProtocol
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type TSRequest struct {
|
|||||||
NegoTokens []NegoToken `asn1:"optional,explicit,tag:1"`
|
NegoTokens []NegoToken `asn1:"optional,explicit,tag:1"`
|
||||||
AuthInfo []byte `asn1:"optional,explicit,tag:2"`
|
AuthInfo []byte `asn1:"optional,explicit,tag:2"`
|
||||||
PubKeyAuth []byte `asn1:"optional,explicit,tag:3"`
|
PubKeyAuth []byte `asn1:"optional,explicit,tag:3"`
|
||||||
//ErrorCode int `asn1:"optional,explicit,tag:4"`
|
ErrorCode int `asn1:"optional,explicit,tag:4"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TSCredentials struct {
|
type TSCredentials struct {
|
||||||
|
|||||||
@@ -317,9 +317,28 @@ func (t *TPKT) recvPubKeyInc(data []byte) error {
|
|||||||
glog.Info("DecodeDERTRequest", err)
|
glog.Info("DecodeDERTRequest", err)
|
||||||
return 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)
|
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()
|
domain, username, password := t.ntlm.GetEncodedCredentials()
|
||||||
credentials := nla.EncodeDERTCredentials(domain, username, password)
|
credentials := nla.EncodeDERTCredentials(domain, username, password)
|
||||||
authInfo := t.ntlmSec.GssEncrypt(credentials)
|
authInfo := t.ntlmSec.GssEncrypt(credentials)
|
||||||
|
|||||||
Reference in New Issue
Block a user