mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
feat: 实现SNMP网络管理协议专业扫描插件
- 新增SNMP服务插件,支持UDP协议和community字符串认证 - 实现SNMP连接器、利用器和主插件的完整架构 - 添加UDP端口161的特殊处理机制,解决UDP端口扫描问题 - 支持默认community字符串爆破(public, private, cisco, community) - 集成SNMP系统信息获取和服务识别功能 - 完善国际化消息支持和Docker测试环境配置
This commit is contained in:
parent
fbc75bb709
commit
8da185257b
@ -864,4 +864,22 @@ var PluginMessages = map[string]map[string]string{
|
||||
LangZH: "SMTP认证失败: %v",
|
||||
LangEN: "SMTP authentication failed: %v",
|
||||
},
|
||||
|
||||
// ========================= SNMP插件消息 =========================
|
||||
"snmp_weak_community_success": {
|
||||
LangZH: "SNMP弱community: %s [%s]",
|
||||
LangEN: "SNMP weak community: %s [%s]",
|
||||
},
|
||||
"snmp_service_identified": {
|
||||
LangZH: "SNMP服务识别成功: %s - %s",
|
||||
LangEN: "SNMP service identified: %s - %s",
|
||||
},
|
||||
"snmp_connection_failed": {
|
||||
LangZH: "SNMP连接失败: %v",
|
||||
LangEN: "SNMP connection failed: %v",
|
||||
},
|
||||
"snmp_auth_failed": {
|
||||
LangZH: "SNMP认证失败: %v",
|
||||
LangEN: "SNMP authentication failed: %v",
|
||||
},
|
||||
}
|
@ -62,6 +62,10 @@ var ScanMessages = map[string]map[string]string{
|
||||
LangZH: "存活端口数量: %d",
|
||||
LangEN: "Alive ports count: %d",
|
||||
},
|
||||
"scan_snmp_udp_ports_added": {
|
||||
LangZH: "检测到SNMP端口161,添加UDP端口到扫描目标",
|
||||
LangEN: "Detected SNMP port 161, adding UDP ports to scan targets",
|
||||
},
|
||||
"scan_task_complete": {
|
||||
LangZH: "扫描已完成: %d/%d",
|
||||
LangEN: "Scan completed: %d/%d",
|
||||
|
@ -66,6 +66,13 @@ func (p *PortDiscoveryService) discoverAlivePorts(hosts []string) []string {
|
||||
common.LogBase(i18n.GetText("scan_alive_ports_count", len(alivePorts)))
|
||||
}
|
||||
|
||||
// UDP端口特殊处理(当前仅支持SNMP的161端口)
|
||||
udpPorts := p.handleUDPPorts(hosts)
|
||||
if len(udpPorts) > 0 {
|
||||
alivePorts = append(alivePorts, udpPorts...)
|
||||
common.LogBase(i18n.GetText("scan_alive_ports_count", len(alivePorts)))
|
||||
}
|
||||
|
||||
// 合并额外指定的端口
|
||||
if len(common.HostPort) > 0 {
|
||||
alivePorts = append(alivePorts, common.HostPort...)
|
||||
@ -95,4 +102,31 @@ func (p *PortDiscoveryService) convertToTargetInfos(ports []string, baseInfo com
|
||||
}
|
||||
|
||||
return infos
|
||||
}
|
||||
|
||||
// handleUDPPorts 处理UDP端口的特殊逻辑
|
||||
func (p *PortDiscoveryService) handleUDPPorts(hosts []string) []string {
|
||||
var udpPorts []string
|
||||
|
||||
// 检查是否包含SNMP端口161
|
||||
portList := parsers.ParsePort(common.Ports)
|
||||
hasPort161 := false
|
||||
for _, port := range portList {
|
||||
if port == 161 {
|
||||
hasPort161 = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 如果端口列表包含161,则为每个主机添加UDP 161端口
|
||||
if hasPort161 {
|
||||
for _, host := range hosts {
|
||||
udpPorts = append(udpPorts, fmt.Sprintf("%s:161", host))
|
||||
}
|
||||
if len(udpPorts) > 0 {
|
||||
common.LogBase(i18n.GetText("scan_snmp_udp_ports_added"))
|
||||
}
|
||||
}
|
||||
|
||||
return udpPorts
|
||||
}
|
@ -25,6 +25,7 @@ import (
|
||||
_ "github.com/shadow1ng/fscan/plugins/services/redis"
|
||||
_ "github.com/shadow1ng/fscan/plugins/services/rsync"
|
||||
_ "github.com/shadow1ng/fscan/plugins/services/smtp"
|
||||
_ "github.com/shadow1ng/fscan/plugins/services/snmp"
|
||||
_ "github.com/shadow1ng/fscan/plugins/services/ssh"
|
||||
)
|
||||
|
||||
|
271
Plugins/services/snmp/connector.go
Normal file
271
Plugins/services/snmp/connector.go
Normal file
@ -0,0 +1,271 @@
|
||||
package snmp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gosnmp/gosnmp"
|
||||
"github.com/shadow1ng/fscan/common"
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
"github.com/shadow1ng/fscan/plugins/base"
|
||||
)
|
||||
|
||||
// SNMPConnector SNMP连接器实现
|
||||
type SNMPConnector struct {
|
||||
host string
|
||||
port int
|
||||
}
|
||||
|
||||
// SNMPConnection SNMP连接结构
|
||||
type SNMPConnection struct {
|
||||
client *gosnmp.GoSNMP
|
||||
community string
|
||||
sysDesc string
|
||||
info string
|
||||
}
|
||||
|
||||
// NewSNMPConnector 创建SNMP连接器
|
||||
func NewSNMPConnector() *SNMPConnector {
|
||||
return &SNMPConnector{}
|
||||
}
|
||||
|
||||
// Connect 建立SNMP连接
|
||||
func (c *SNMPConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) {
|
||||
// 解析端口
|
||||
port, err := strconv.Atoi(info.Ports)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("无效的端口号: %s", info.Ports)
|
||||
}
|
||||
|
||||
c.host = info.Host
|
||||
c.port = port
|
||||
|
||||
timeout := time.Duration(common.Timeout) * time.Second
|
||||
|
||||
// 结果通道
|
||||
type connResult struct {
|
||||
conn *SNMPConnection
|
||||
err error
|
||||
banner string
|
||||
}
|
||||
resultChan := make(chan connResult, 1)
|
||||
|
||||
// 在协程中尝试连接
|
||||
go func() {
|
||||
// 尝试使用默认的public community进行连接
|
||||
client := &gosnmp.GoSNMP{
|
||||
Target: info.Host,
|
||||
Port: uint16(port),
|
||||
Community: "public",
|
||||
Version: gosnmp.Version2c,
|
||||
Timeout: timeout,
|
||||
Retries: 1,
|
||||
}
|
||||
|
||||
err := client.Connect()
|
||||
if err != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case resultChan <- connResult{nil, err, ""}:
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 尝试获取系统描述信息
|
||||
oids := []string{"1.3.6.1.2.1.1.1.0"} // sysDescr OID
|
||||
result, err := client.Get(oids)
|
||||
|
||||
var sysDesc string
|
||||
var banner string
|
||||
|
||||
if err == nil && len(result.Variables) > 0 {
|
||||
if result.Variables[0].Type != gosnmp.NoSuchObject {
|
||||
switch v := result.Variables[0].Value.(type) {
|
||||
case []byte:
|
||||
sysDesc = strings.TrimSpace(string(v))
|
||||
case string:
|
||||
sysDesc = strings.TrimSpace(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if sysDesc != "" {
|
||||
banner = fmt.Sprintf("SNMP Service (Version: %s, System: %s)", client.Version.String(), sysDesc)
|
||||
} else {
|
||||
banner = fmt.Sprintf("SNMP Service (Version: %s)", client.Version.String())
|
||||
}
|
||||
|
||||
// 创建连接对象
|
||||
snmpConn := &SNMPConnection{
|
||||
client: client,
|
||||
community: "public",
|
||||
sysDesc: sysDesc,
|
||||
info: banner,
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
client.Conn.Close()
|
||||
case resultChan <- connResult{snmpConn, nil, banner}:
|
||||
}
|
||||
}()
|
||||
|
||||
// 等待连接结果
|
||||
select {
|
||||
case result := <-resultChan:
|
||||
if result.err != nil {
|
||||
return nil, result.err
|
||||
}
|
||||
return result.conn, nil
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// Authenticate 进行SNMP认证(通过community字符串)
|
||||
func (c *SNMPConnector) Authenticate(ctx context.Context, conn interface{}, cred *base.Credential) error {
|
||||
snmpConn, ok := conn.(*SNMPConnection)
|
||||
if !ok {
|
||||
return fmt.Errorf("无效的SNMP连接类型")
|
||||
}
|
||||
|
||||
// 对于SNMP,将用户名作为community字符串使用
|
||||
community := cred.Username
|
||||
if community == "" {
|
||||
community = cred.Password // 如果用户名为空,尝试使用密码作为community
|
||||
}
|
||||
|
||||
timeout := time.Duration(common.Timeout) * time.Second
|
||||
|
||||
// 结果通道
|
||||
type authResult struct {
|
||||
client *gosnmp.GoSNMP
|
||||
sysDesc string
|
||||
err error
|
||||
}
|
||||
resultChan := make(chan authResult, 1)
|
||||
|
||||
// 在协程中尝试认证
|
||||
go func() {
|
||||
// 关闭旧连接
|
||||
if snmpConn.client != nil && snmpConn.client.Conn != nil {
|
||||
snmpConn.client.Conn.Close()
|
||||
}
|
||||
|
||||
// 创建新的SNMP客户端
|
||||
client := &gosnmp.GoSNMP{
|
||||
Target: c.host,
|
||||
Port: uint16(c.port),
|
||||
Community: community,
|
||||
Version: gosnmp.Version2c,
|
||||
Timeout: timeout,
|
||||
Retries: 1,
|
||||
}
|
||||
|
||||
err := client.Connect()
|
||||
if err != nil {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case resultChan <- authResult{nil, "", err}:
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 尝试获取系统描述信息验证认证
|
||||
oids := []string{"1.3.6.1.2.1.1.1.0"} // sysDescr OID
|
||||
result, err := client.Get(oids)
|
||||
if err != nil {
|
||||
client.Conn.Close()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case resultChan <- authResult{nil, "", err}:
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var sysDesc string
|
||||
if len(result.Variables) > 0 && result.Variables[0].Type != gosnmp.NoSuchObject {
|
||||
switch v := result.Variables[0].Value.(type) {
|
||||
case []byte:
|
||||
sysDesc = strings.TrimSpace(string(v))
|
||||
case string:
|
||||
sysDesc = strings.TrimSpace(v)
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
client.Conn.Close()
|
||||
case resultChan <- authResult{client, sysDesc, nil}:
|
||||
}
|
||||
}()
|
||||
|
||||
// 等待认证结果
|
||||
select {
|
||||
case result := <-resultChan:
|
||||
if result.err != nil {
|
||||
return fmt.Errorf(i18n.GetText("snmp_auth_failed"), result.err)
|
||||
}
|
||||
|
||||
// 更新连接信息
|
||||
snmpConn.client = result.client
|
||||
snmpConn.community = community
|
||||
snmpConn.sysDesc = result.sysDesc
|
||||
if result.sysDesc != "" {
|
||||
snmpConn.info = fmt.Sprintf("SNMP Service (Community: %s, System: %s)", community, result.sysDesc)
|
||||
} else {
|
||||
snmpConn.info = fmt.Sprintf("SNMP Service (Community: %s)", community)
|
||||
}
|
||||
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// Close 关闭SNMP连接
|
||||
func (c *SNMPConnector) Close(conn interface{}) error {
|
||||
if snmpConn, ok := conn.(*SNMPConnection); ok {
|
||||
if snmpConn.client != nil && snmpConn.client.Conn != nil {
|
||||
snmpConn.client.Conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("无效的SNMP连接类型")
|
||||
}
|
||||
|
||||
// GetConnectionInfo 获取连接信息
|
||||
func (conn *SNMPConnection) GetConnectionInfo() map[string]interface{} {
|
||||
info := map[string]interface{}{
|
||||
"protocol": "SNMP",
|
||||
"service": "SNMP",
|
||||
"info": conn.info,
|
||||
"community": conn.community,
|
||||
}
|
||||
|
||||
if conn.sysDesc != "" {
|
||||
info["system"] = conn.sysDesc
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
// IsAlive 检查连接是否仍然有效
|
||||
func (conn *SNMPConnection) IsAlive() bool {
|
||||
if conn.client == nil || conn.client.Conn == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// 简单的SNMP GET测试连接
|
||||
oids := []string{"1.3.6.1.2.1.1.1.0"}
|
||||
_, err := conn.client.Get(oids)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// GetServerInfo 获取服务器信息
|
||||
func (conn *SNMPConnection) GetServerInfo() string {
|
||||
return conn.info
|
||||
}
|
31
Plugins/services/snmp/exploiter.go
Normal file
31
Plugins/services/snmp/exploiter.go
Normal file
@ -0,0 +1,31 @@
|
||||
package snmp
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/shadow1ng/fscan/common"
|
||||
"github.com/shadow1ng/fscan/plugins/base"
|
||||
)
|
||||
|
||||
// SNMPExploiter SNMP利用器实现
|
||||
type SNMPExploiter struct{}
|
||||
|
||||
// NewSNMPExploiter 创建SNMP利用器
|
||||
func NewSNMPExploiter() *SNMPExploiter {
|
||||
return &SNMPExploiter{}
|
||||
}
|
||||
|
||||
// Exploit 执行SNMP利用
|
||||
func (e *SNMPExploiter) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetExploitMethods 获取可用的利用方法
|
||||
func (e *SNMPExploiter) GetExploitMethods() []base.ExploitMethod {
|
||||
return []base.ExploitMethod{}
|
||||
}
|
||||
|
||||
// IsExploitSupported 检查是否支持特定的利用类型
|
||||
func (e *SNMPExploiter) IsExploitSupported(method base.ExploitType) bool {
|
||||
return false
|
||||
}
|
240
Plugins/services/snmp/plugin.go
Normal file
240
Plugins/services/snmp/plugin.go
Normal file
@ -0,0 +1,240 @@
|
||||
package snmp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/shadow1ng/fscan/common"
|
||||
"github.com/shadow1ng/fscan/common/i18n"
|
||||
"github.com/shadow1ng/fscan/plugins/base"
|
||||
)
|
||||
|
||||
// SNMPPlugin SNMP插件实现
|
||||
type SNMPPlugin struct {
|
||||
*base.ServicePlugin
|
||||
exploiter *SNMPExploiter
|
||||
}
|
||||
|
||||
// NewSNMPPlugin 创建SNMP插件
|
||||
func NewSNMPPlugin() *SNMPPlugin {
|
||||
// 插件元数据
|
||||
metadata := &base.PluginMetadata{
|
||||
Name: "snmp",
|
||||
Version: "2.0.0",
|
||||
Author: "fscan-team",
|
||||
Description: "SNMP网络管理协议服务扫描插件",
|
||||
Category: "service",
|
||||
Ports: []int{161}, // SNMP默认端口
|
||||
Protocols: []string{"udp"},
|
||||
Tags: []string{"snmp", "network-management", "weak-community", "information-disclosure"},
|
||||
}
|
||||
|
||||
// 创建连接器和服务插件
|
||||
connector := NewSNMPConnector()
|
||||
servicePlugin := base.NewServicePlugin(metadata, connector)
|
||||
|
||||
// 创建SNMP插件
|
||||
plugin := &SNMPPlugin{
|
||||
ServicePlugin: servicePlugin,
|
||||
exploiter: NewSNMPExploiter(),
|
||||
}
|
||||
|
||||
// 设置能力
|
||||
plugin.SetCapabilities([]base.Capability{
|
||||
base.CapWeakPassword,
|
||||
base.CapInformationLeak,
|
||||
})
|
||||
|
||||
return plugin
|
||||
}
|
||||
|
||||
// Scan 重写扫描方法,进行SNMP服务扫描
|
||||
func (p *SNMPPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
||||
// 如果禁用了暴力破解,只进行服务识别
|
||||
if common.DisableBrute {
|
||||
return p.performServiceIdentification(ctx, info)
|
||||
}
|
||||
|
||||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||||
|
||||
// 优先尝试默认community字符串
|
||||
defaultCommunities := []*base.Credential{
|
||||
{Username: "public", Password: ""},
|
||||
{Username: "private", Password: ""},
|
||||
{Username: "cisco", Password: ""},
|
||||
{Username: "community", Password: ""},
|
||||
}
|
||||
|
||||
// 先测试默认community
|
||||
for _, cred := range defaultCommunities {
|
||||
result, err := p.ScanCredential(ctx, info, cred)
|
||||
if err == nil && result.Success {
|
||||
// 认证成功
|
||||
common.LogSuccess(i18n.GetText("snmp_weak_community_success", target, cred.Username))
|
||||
|
||||
return &base.ScanResult{
|
||||
Success: true,
|
||||
Service: "SNMP",
|
||||
Credentials: []*base.Credential{cred},
|
||||
Banner: result.Banner,
|
||||
Extra: map[string]interface{}{
|
||||
"service": "SNMP",
|
||||
"port": info.Ports,
|
||||
"community": cred.Username,
|
||||
"type": "weak-community",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 生成其他凭据进行暴力破解
|
||||
credentials := p.generateCredentials()
|
||||
|
||||
// 遍历凭据进行测试
|
||||
for _, cred := range credentials {
|
||||
result, err := p.ScanCredential(ctx, info, cred)
|
||||
if err == nil && result.Success {
|
||||
// 认证成功
|
||||
common.LogSuccess(i18n.GetText("snmp_weak_community_success", target, cred.Username))
|
||||
|
||||
return &base.ScanResult{
|
||||
Success: true,
|
||||
Service: "SNMP",
|
||||
Credentials: []*base.Credential{cred},
|
||||
Banner: result.Banner,
|
||||
Extra: map[string]interface{}{
|
||||
"service": "SNMP",
|
||||
"port": info.Ports,
|
||||
"community": cred.Username,
|
||||
"type": "weak-community",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 所有凭据都失败,但可能识别到了SNMP服务
|
||||
return p.performServiceIdentification(ctx, info)
|
||||
}
|
||||
|
||||
// generateCredentials 生成SNMP凭据(community字符串)
|
||||
func (p *SNMPPlugin) generateCredentials() []*base.Credential {
|
||||
var credentials []*base.Credential
|
||||
|
||||
// 获取SNMP community字典
|
||||
communities := common.Userdict["snmp"]
|
||||
if len(communities) == 0 {
|
||||
// 常见的SNMP community字符串
|
||||
communities = []string{
|
||||
"admin", "manager", "secret", "read", "write", "test",
|
||||
"monitor", "guest", "default", "root", "snmp", "router",
|
||||
"switch", "network", "public1", "private1", "v1", "v2c",
|
||||
}
|
||||
}
|
||||
|
||||
// 生成community凭据
|
||||
for _, community := range communities {
|
||||
credentials = append(credentials, &base.Credential{
|
||||
Username: community,
|
||||
Password: "",
|
||||
})
|
||||
}
|
||||
|
||||
// 如果有密码字典,也作为community使用
|
||||
passwords := common.Passwords
|
||||
if len(passwords) > 0 {
|
||||
for _, password := range passwords {
|
||||
// 替换密码中的占位符
|
||||
actualPassword := strings.Replace(password, "{user}", "snmp", -1)
|
||||
if actualPassword != "" && actualPassword != "snmp" {
|
||||
credentials = append(credentials, &base.Credential{
|
||||
Username: actualPassword,
|
||||
Password: "",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return credentials
|
||||
}
|
||||
|
||||
// Exploit 使用exploiter执行利用
|
||||
func (p *SNMPPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
|
||||
return p.exploiter.Exploit(ctx, info, creds)
|
||||
}
|
||||
|
||||
// GetExploitMethods 获取利用方法
|
||||
func (p *SNMPPlugin) GetExploitMethods() []base.ExploitMethod {
|
||||
return p.exploiter.GetExploitMethods()
|
||||
}
|
||||
|
||||
// IsExploitSupported 检查利用支持
|
||||
func (p *SNMPPlugin) IsExploitSupported(method base.ExploitType) bool {
|
||||
return p.exploiter.IsExploitSupported(method)
|
||||
}
|
||||
|
||||
// performServiceIdentification 执行SNMP服务识别(-nobr模式)
|
||||
func (p *SNMPPlugin) performServiceIdentification(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
||||
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
|
||||
|
||||
// 尝试识别SNMP服务
|
||||
connector := NewSNMPConnector()
|
||||
conn, err := connector.Connect(ctx, info)
|
||||
|
||||
if err == nil && conn != nil {
|
||||
if snmpConn, ok := conn.(*SNMPConnection); ok {
|
||||
// 记录服务识别成功
|
||||
common.LogSuccess(i18n.GetText("snmp_service_identified", target, snmpConn.info))
|
||||
|
||||
connector.Close(conn)
|
||||
return &base.ScanResult{
|
||||
Success: true,
|
||||
Service: "SNMP",
|
||||
Banner: snmpConn.info,
|
||||
Extra: map[string]interface{}{
|
||||
"service": "SNMP",
|
||||
"port": info.Ports,
|
||||
"info": snmpConn.info,
|
||||
"protocol": "SNMP",
|
||||
"community": snmpConn.community,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 如果无法识别为SNMP,返回失败
|
||||
return &base.ScanResult{
|
||||
Success: false,
|
||||
Error: fmt.Errorf("无法识别为SNMP服务"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 插件注册
|
||||
// =============================================================================
|
||||
|
||||
// RegisterSNMPPlugin 注册SNMP插件
|
||||
func RegisterSNMPPlugin() {
|
||||
factory := base.NewSimplePluginFactory(
|
||||
&base.PluginMetadata{
|
||||
Name: "snmp",
|
||||
Version: "2.0.0",
|
||||
Author: "fscan-team",
|
||||
Description: "SNMP网络管理协议服务扫描插件",
|
||||
Category: "service",
|
||||
Ports: []int{161}, // SNMP默认端口
|
||||
Protocols: []string{"udp"},
|
||||
Tags: []string{"snmp", "network-management", "weak-community", "information-disclosure"},
|
||||
},
|
||||
func() base.Plugin {
|
||||
return NewSNMPPlugin()
|
||||
},
|
||||
)
|
||||
|
||||
base.GlobalPluginRegistry.Register("snmp", factory)
|
||||
}
|
||||
|
||||
// 自动注册
|
||||
func init() {
|
||||
RegisterSNMPPlugin()
|
||||
}
|
9
TestDocker/SNMP/docker-compose.yml
Normal file
9
TestDocker/SNMP/docker-compose.yml
Normal file
@ -0,0 +1,9 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
snmp:
|
||||
build: .
|
||||
ports:
|
||||
- "161:161/udp"
|
||||
container_name: snmp_test
|
||||
restart: unless-stopped
|
Loading…
Reference in New Issue
Block a user