Files
ZacharyZcR 42092d8664 fix: 修复实机测试发现的可靠性问题 (v2.2.0-rc.1)
- UDP 插件在 -p 指定端口时被跳过
- Redis exploit 无超时保护 / readReply 吞没非超时错误
- service_probe 连接丢失后静默成功
- SNMP 探测成功但终端无输出
- SSH 爆破不稳定 (并发过高 + 自适应超时过短 + 限流误判)
- 进度条 isActive 竞态

新增 Config.ModuleTimeout() 协议级超时下限 (≥3s)
新增 ErrorTypeThrottle 限流错误分类
2026-06-14 22:14:02 +08:00

115 lines
2.5 KiB
Go

//go:build plugin_mqtt || !plugin_selective
package services
import (
"context"
"crypto/tls"
"fmt"
"io"
"net"
"time"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/plugins"
)
var mqttConnectPacket = []byte{
0x10, 0x0c,
0x00, 0x04, 'M', 'Q', 'T', 'T',
0x04,
0x02,
0x00, 0x00,
0x00, 0x00,
}
type MQTTPlugin struct {
plugins.BasePlugin
}
func NewMQTTPlugin() *MQTTPlugin {
return &MQTTPlugin{BasePlugin: plugins.NewBasePlugin("mqtt")}
}
func (p *MQTTPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
timeout := session.Config.ModuleTimeout()
if timeout <= 0 {
timeout = 3 * time.Second
}
addr := info.Target()
conn, err := session.DialTCP(ctx, "tcp", addr, timeout)
if err != nil {
return &ScanResult{Success: false, Service: "mqtt"}
}
defer conn.Close()
if info.Port == 8883 {
_ = conn.SetDeadline(time.Now().Add(timeout))
conn, err = p.wrapTLS(ctx, conn)
if err != nil {
return &ScanResult{Success: false, Service: "mqtt"}
}
defer conn.Close()
}
_ = conn.SetDeadline(time.Now().Add(timeout))
if _, err := conn.Write(mqttConnectPacket); err != nil {
return &ScanResult{Success: false, Service: "mqtt"}
}
header := make([]byte, 4)
if _, err := io.ReadFull(conn, header); err != nil {
return &ScanResult{Success: false, Service: "mqtt"}
}
banner, ok := parseMQTTConnack(header)
if !ok {
return &ScanResult{Success: false, Service: "mqtt"}
}
return &ScanResult{
Success: true,
Type: plugins.ResultTypeService,
Service: "mqtt",
Banner: banner,
}
}
func (p *MQTTPlugin) wrapTLS(ctx context.Context, conn net.Conn) (net.Conn, error) {
tlsConn := tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
if err := tlsConn.HandshakeContext(ctx); err != nil {
return nil, err
}
return tlsConn, nil
}
func parseMQTTConnack(data []byte) (string, bool) {
if len(data) < 4 || data[0] != 0x20 || data[1] != 0x02 {
return "", false
}
switch data[3] {
case 0x00:
return "MQTT CONNACK accepted", true
case 0x01:
return "MQTT CONNACK unacceptable protocol version", true
case 0x02:
return "MQTT CONNACK identifier rejected", true
case 0x03:
return "MQTT CONNACK server unavailable", true
case 0x04:
return "MQTT CONNACK bad username or password", true
case 0x05:
return "MQTT CONNACK not authorized", true
default:
return fmt.Sprintf("MQTT CONNACK return_code=%d", data[3]), true
}
}
func init() {
RegisterPluginWithPorts("mqtt", func() Plugin {
return NewMQTTPlugin()
}, []int{1883, 8883})
}