package fileinfo import ( "context" "fmt" "os" "path/filepath" "runtime" "strings" "github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/plugins/base" "github.com/shadow1ng/fscan/plugins/local" ) // FileInfoPlugin 文件信息收集插件 type FileInfoPlugin struct { *local.BaseLocalPlugin connector *FileInfoConnector // 配置选项 blacklist []string whitelist []string } // FileInfoConnector 文件信息连接器 type FileInfoConnector struct { *local.BaseLocalConnector sensitiveFiles []string searchDirs []string } // NewFileInfoPlugin 创建文件信息收集插件 func NewFileInfoPlugin() *FileInfoPlugin { metadata := &base.PluginMetadata{ Name: "fileinfo", Version: "1.0.0", Author: "fscan-team", Description: "本地敏感文件信息收集插件", Category: "local", Tags: []string{"local", "fileinfo", "sensitive"}, Protocols: []string{"local"}, } connector := NewFileInfoConnector() plugin := &FileInfoPlugin{ BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector), connector: connector, blacklist: []string{ ".exe", ".dll", ".png", ".jpg", ".bmp", ".xml", ".bin", ".dat", ".manifest", "locale", "winsxs", "windows\\sys", }, whitelist: []string{ "密码", "账号", "账户", "配置", "服务器", "数据库", "备忘", "常用", "通讯录", "password", "config", "credential", "key", "secret", }, } // 设置平台支持 plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"}) return plugin } // Scan 重写扫描方法以确保调用正确的ScanLocal实现 func (p *FileInfoPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { 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() { switch runtime.GOOS { case "windows": c.sensitiveFiles = []string{ "C:\\boot.ini", "C:\\windows\\systems32\\inetsrv\\MetaBase.xml", "C:\\windows\\repair\\sam", "C:\\windows\\system32\\config\\sam", } if homeDir := c.GetCommonDirectories()[0]; homeDir != "" { c.sensitiveFiles = append(c.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"), }...) } case "linux", "darwin": c.sensitiveFiles = []string{ "/etc/apache/httpd.conf", "/etc/httpd/conf/httpd.conf", "/etc/nginx/nginx.conf", "/etc/hosts.deny", "/etc/ssh/ssh_config", "/etc/resolv.conf", "/root/.ssh/authorized_keys", "/root/.ssh/id_rsa", "/root/.bash_history", } } c.searchDirs = c.GetCommonDirectories() } // 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) foundFiles := make([]string, 0) // 扫描固定位置的敏感文件 common.LogDebug("扫描固定敏感文件位置...") for _, file := range p.connector.sensitiveFiles { if p.checkFile(file) { foundFiles = append(foundFiles, file) common.LogSuccess(fmt.Sprintf("发现敏感文件: %s", file)) } } // 根据规则搜索敏感文件 common.LogDebug("按规则搜索敏感文件...") searchFiles := p.searchSensitiveFiles() foundFiles = append(foundFiles, searchFiles...) result := &base.ScanResult{ Success: len(foundFiles) > 0, Service: "FileInfo", Banner: fmt.Sprintf("发现 %d 个敏感文件", len(foundFiles)), Extra: map[string]interface{}{ "files": foundFiles, "total_count": len(foundFiles), "platform": runtime.GOOS, }, } if len(foundFiles) > 0 { common.LogSuccess(fmt.Sprintf("本地文件扫描完成,共发现 %d 个敏感文件", len(foundFiles))) } else { common.LogDebug("未发现敏感文件") } return result, nil } // GetLocalData 获取本地文件数据 func (p *FileInfoPlugin) GetLocalData(ctx context.Context) (map[string]interface{}, error) { data := make(map[string]interface{}) // 获取系统信息 data["platform"] = runtime.GOOS data["arch"] = runtime.GOARCH if homeDir, err := os.UserHomeDir(); err == nil { data["home_dir"] = homeDir } if workDir, err := os.Getwd(); err == nil { data["work_dir"] = workDir } return data, nil } // ExtractData 提取敏感文件数据 func (p *FileInfoPlugin) ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error) { // 文件信息收集插件主要是扫描,不进行深度利用 return &base.ExploitResult{ Success: true, Output: "文件信息收集完成", Data: data, }, nil } // checkFile 检查文件是否存在 func (p *FileInfoPlugin) checkFile(path string) bool { if _, err := os.Stat(path); err == nil { return true } return false } // searchSensitiveFiles 搜索敏感文件 func (p *FileInfoPlugin) searchSensitiveFiles() []string { var foundFiles []string for _, searchPath := range p.connector.searchDirs { filepath.Walk(searchPath, func(path string, info os.FileInfo, err error) error { if err != nil { return nil } // 跳过黑名单文件 if p.isBlacklisted(path) { if info.IsDir() { return filepath.SkipDir } return nil } // 检查白名单关键词 if p.isWhitelisted(info.Name()) { foundFiles = append(foundFiles, path) common.LogSuccess(fmt.Sprintf("发现潜在敏感文件: %s", path)) } return nil }) } return foundFiles } // isBlacklisted 检查是否在黑名单中 func (p *FileInfoPlugin) isBlacklisted(path string) bool { pathLower := strings.ToLower(path) for _, black := range p.blacklist { if strings.Contains(pathLower, black) { return true } } return false } // isWhitelisted 检查是否匹配白名单 func (p *FileInfoPlugin) isWhitelisted(filename string) bool { filenameLower := strings.ToLower(filename) for _, white := range p.whitelist { if strings.Contains(filenameLower, white) { return true } } return false } // 插件注册函数 func init() { base.GlobalPluginRegistry.Register("fileinfo", base.NewSimplePluginFactory( &base.PluginMetadata{ Name: "fileinfo", Version: "1.0.0", Author: "fscan-team", Description: "本地敏感文件信息收集插件", Category: "local", Tags: []string{"local", "fileinfo", "sensitive"}, Protocols: []string{"local"}, }, func() base.Plugin { return NewFileInfoPlugin() }, )) }