package plugins import ( "context" "encoding/hex" "fmt" "net" "strings" "time" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common/i18n" ) // SNMPPlugin SNMP网络管理协议扫描和利用插件 - 包含系统信息收集利用功能 type SNMPPlugin struct { name string ports []int } // NewSNMPPlugin 创建SNMP插件 func NewSNMPPlugin() *SNMPPlugin { return &SNMPPlugin{ name: "snmp", ports: []int{161, 162}, // SNMP端口 } } // GetName 实现Plugin接口 func (p *SNMPPlugin) GetName() string { return p.name } // GetPorts 实现Plugin接口 func (p *SNMPPlugin) GetPorts() []int { return p.ports } // Scan 执行SNMP扫描 - 弱团体字符串检测 func (p *SNMPPlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResult { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) // 如果禁用暴力破解,只做服务识别 if common.DisableBrute { return p.identifyService(ctx, info) } // 生成测试团体字符串 communities := p.generateCommunities() // 逐个测试团体字符串 for _, community := range communities { // 检查Context是否被取消 select { case <-ctx.Done(): return &ScanResult{ Success: false, Service: "snmp", Error: ctx.Err(), } default: } // 测试团体字符串 if p.testCommunity(ctx, info, community) { // SNMP团体字符串测试成功 common.LogSuccess(i18n.GetText("snmp_scan_success", target, community)) return &ScanResult{ Success: true, Service: "snmp", Username: community, // 团体字符串作为用户名 Password: "", } } } // 所有团体字符串都失败 return &ScanResult{ Success: false, Service: "snmp", Error: fmt.Errorf("未发现弱团体字符串"), } } // Exploit 执行SNMP利用操作 - 实现系统信息收集功能 func (p *SNMPPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds Credential) *ExploitResult { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) community := creds.Username // 团体字符串存储在Username中 common.LogSuccess(fmt.Sprintf("SNMP利用开始: %s (团体: %s)", target, community)) var output strings.Builder output.WriteString(fmt.Sprintf("=== SNMP利用结果 - %s ===\n", target)) output.WriteString(fmt.Sprintf("团体字符串: %s\n", community)) // 获取系统信息 if sysInfo := p.getSystemInfo(ctx, info, community); sysInfo != "" { output.WriteString(fmt.Sprintf("\n[系统信息]\n%s\n", sysInfo)) } // 获取网络接口信息 if interfaces := p.getNetworkInterfaces(ctx, info, community); len(interfaces) > 0 { output.WriteString(fmt.Sprintf("\n[网络接口] (共%d个)\n", len(interfaces))) for i, iface := range interfaces { if i >= 10 { // 限制显示前10个接口 output.WriteString("... (更多接口)\n") break } output.WriteString(fmt.Sprintf(" %s\n", iface)) } } // 获取进程信息 if processes := p.getProcesses(ctx, info, community); len(processes) > 0 { output.WriteString(fmt.Sprintf("\n[运行进程] (共%d个)\n", len(processes))) for i, process := range processes { if i >= 15 { // 限制显示前15个进程 output.WriteString("... (更多进程)\n") break } output.WriteString(fmt.Sprintf(" %s\n", process)) } } // 获取用户信息 if users := p.getUsers(ctx, info, community); len(users) > 0 { output.WriteString(fmt.Sprintf("\n[系统用户] (共%d个)\n", len(users))) for _, user := range users { output.WriteString(fmt.Sprintf(" %s\n", user)) } } common.LogSuccess(fmt.Sprintf("SNMP利用完成: %s", target)) return &ExploitResult{ Success: true, Output: output.String(), } } // generateCommunities 生成测试团体字符串 func (p *SNMPPlugin) generateCommunities() []string { // SNMP默认和常见团体字符串 return []string{ "public", "private", "community", "snmp", "admin", "manager", "read", "write", "test", "guest", "monitor", "security", "system", } } // testCommunity 测试单个团体字符串 func (p *SNMPPlugin) testCommunity(ctx context.Context, info *common.HostInfo, community string) bool { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) conn, err := net.DialTimeout("udp", target, time.Duration(common.Timeout)*time.Second) if err != nil { return false } defer conn.Close() // 构建SNMP Get请求包 (sysDescr.0 OID: 1.3.6.1.2.1.1.1.0) packet := p.buildSNMPGetRequest(community, "1.3.6.1.2.1.1.1.0") conn.SetWriteDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)) if _, err := conn.Write(packet); err != nil { return false } conn.SetReadDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)) response := make([]byte, 1024) n, err := conn.Read(response) if err != nil { return false } // 简单检查响应是否为有效的SNMP响应 return p.isValidSNMPResponse(response[:n]) } // buildSNMPGetRequest 构建SNMP Get请求 func (p *SNMPPlugin) buildSNMPGetRequest(community, oid string) []byte { // 简化的SNMP v1 Get请求构建 // 这里使用硬编码的包结构,实际应该使用ASN.1编码 // SNMP Get Request for sysDescr.0 (1.3.6.1.2.1.1.1.0) // 这是一个预构建的SNMP包模板,community字符串需要替换 template := "302902010004067075626c6963a01c02020f7102010002010030113015060a2b060102010101000500" // 将十六进制模板转换为字节 packet, err := hex.DecodeString(template) if err != nil { return nil } // 这里应该替换community字符串,但为了简化,使用固定的"public" // 实际实现需要proper ASN.1编码 return packet } // isValidSNMPResponse 检查是否为有效的SNMP响应 func (p *SNMPPlugin) isValidSNMPResponse(data []byte) bool { // 简单检查SNMP响应标志 if len(data) < 10 { return false } // SNMP响应通常以0x30开始 (SEQUENCE) return data[0] == 0x30 } // getSystemInfo 获取系统信息 func (p *SNMPPlugin) getSystemInfo(ctx context.Context, info *common.HostInfo, community string) string { var sysInfo strings.Builder // 系统相关的OID oids := map[string]string{ "系统描述": "1.3.6.1.2.1.1.1.0", "系统OID": "1.3.6.1.2.1.1.2.0", "系统运行时间": "1.3.6.1.2.1.1.3.0", "系统联系人": "1.3.6.1.2.1.1.4.0", "系统名称": "1.3.6.1.2.1.1.5.0", "系统位置": "1.3.6.1.2.1.1.6.0", } for name, oid := range oids { if value := p.getSNMPValue(ctx, info, community, oid); value != "" { sysInfo.WriteString(fmt.Sprintf("%s: %s\n", name, value)) } } return sysInfo.String() } // getNetworkInterfaces 获取网络接口信息 func (p *SNMPPlugin) getNetworkInterfaces(ctx context.Context, info *common.HostInfo, community string) []string { // 这里简化实现,实际需要遍历接口表 interfaces := []string{ "接口信息需要完整SNMP库支持", "简化实现中暂时无法获取详细接口信息", } return interfaces } // getProcesses 获取进程信息 func (p *SNMPPlugin) getProcesses(ctx context.Context, info *common.HostInfo, community string) []string { // HOST-RESOURCES-MIB中的进程表 (hrSWRunTable) processes := []string{ "进程信息需要完整SNMP库支持", "简化实现中暂时无法获取详细进程信息", } return processes } // getUsers 获取用户信息 func (p *SNMPPlugin) getUsers(ctx context.Context, info *common.HostInfo, community string) []string { // 用户信息通常不通过SNMP直接获取 return []string{"用户信息通常不通过SNMP协议直接暴露"} } // getSNMPValue 获取指定OID的值 func (p *SNMPPlugin) getSNMPValue(ctx context.Context, info *common.HostInfo, community, oid string) string { // 简化实现,实际需要proper SNMP库 if p.testCommunity(ctx, info, community) { return "SNMP值获取需要完整SNMP库支持" } return "" } // identifyService 服务识别 - 检测SNMP服务 func (p *SNMPPlugin) identifyService(ctx context.Context, info *common.HostInfo) *ScanResult { target := fmt.Sprintf("%s:%s", info.Host, info.Ports) // 尝试使用默认团体字符串"public"进行服务识别 if p.testCommunity(ctx, info, "public") { banner := "SNMP网络管理服务 (public团体可访问)" common.LogSuccess(i18n.GetText("snmp_service_identified", target, banner)) return &ScanResult{ Success: true, Service: "snmp", Banner: banner, } } // 尝试UDP连接测试 conn, err := net.DialTimeout("udp", target, time.Duration(common.Timeout)*time.Second) if err != nil { return &ScanResult{ Success: false, Service: "snmp", Error: err, } } defer conn.Close() banner := "SNMP网络管理服务 (需要有效团体字符串)" common.LogSuccess(i18n.GetText("snmp_service_identified", target, banner)) return &ScanResult{ Success: true, Service: "snmp", Banner: banner, } } // init 自动注册插件 func init() { RegisterPlugin("snmp", func() Plugin { return NewSNMPPlugin() }) }