Files
fscan/plugins/services/rmi.go
T
ZacharyZcR bc2a8f653a fix: 修复新服务扫描插件的健壮性问题
- ipmi: 删除未使用的 encoding/binary 导入
- rmi: TCP读取改用 io.ReadFull 避免分片导致的解析错误
- jdwp: handshake响应读取改用 io.ReadFull 避免分片误判
- nfs: v4协议回退时使用新连接避免残留数据污染
- snmp: 修正timeout计算与其他插件保持一致
2026-05-19 20:46:22 +08:00

94 lines
2.3 KiB
Go

//go:build plugin_rmi || !plugin_selective
package services
import (
"context"
"fmt"
"io"
"time"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/plugins"
)
// Java RMI protocol magic: "JRMI" + version 2 + StreamProtocol
var rmiHandshake = []byte{0x4a, 0x52, 0x4d, 0x49, 0x00, 0x02, 0x4b}
type RMIPlugin struct {
plugins.BasePlugin
}
func NewRMIPlugin() *RMIPlugin {
return &RMIPlugin{BasePlugin: plugins.NewBasePlugin("rmi")}
}
func (p *RMIPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *ScanResult {
timeout := session.Config.Timeout
if timeout <= 0 {
timeout = 3 * time.Second
}
addr := fmt.Sprintf("%s:%d", info.Host, info.Port)
conn, err := session.DialTCP(ctx, "tcp", addr, timeout)
if err != nil {
return &ScanResult{Success: false, Service: "rmi"}
}
defer conn.Close()
_ = conn.SetDeadline(time.Now().Add(timeout))
if _, err := conn.Write(rmiHandshake); err != nil {
return &ScanResult{Success: false, Service: "rmi"}
}
// RMI server responds with ProtocolAck (0x4e) followed by endpoint info.
// Read at least 1 byte for the ack; io.ReadFull guarantees it.
ack := make([]byte, 1)
if _, err := io.ReadFull(conn, ack); err != nil {
return &ScanResult{Success: false, Service: "rmi"}
}
if ack[0] != 0x4e {
return &ScanResult{Success: false, Service: "rmi"}
}
buf := make([]byte, 255)
n, err := conn.Read(buf)
if err != nil && n == 0 {
return &ScanResult{Success: false, Service: "rmi"}
}
endpoint := parseRMIEndpoint(buf[:n])
return &ScanResult{
Success: true,
Type: plugins.ResultTypeVuln,
Service: "rmi",
VulInfo: "Java RMI/JMX Service Exposed",
Banner: endpoint,
}
}
func parseRMIEndpoint(data []byte) string {
if len(data) < 4 {
return "Java RMI"
}
// Skip 2 bytes (host length big-endian)
hostLen := int(data[0])<<8 | int(data[1])
if hostLen <= 0 || hostLen+2 > len(data) {
return "Java RMI"
}
host := string(data[2 : 2+hostLen])
offset := 2 + hostLen
if offset+4 > len(data) {
return fmt.Sprintf("Java RMI endpoint=%s", host)
}
port := int(data[offset])<<24 | int(data[offset+1])<<16 | int(data[offset+2])<<8 | int(data[offset+3])
return fmt.Sprintf("Java RMI endpoint=%s:%d", host, port)
}
func init() {
RegisterPluginWithPorts("rmi", func() Plugin {
return NewRMIPlugin()
}, []int{1099, 1098, 9999, 4444})
}