fscan/Plugins/local/fileinfo/plugin.go
ZacharyZcR eeaa4c3b3a feat: 实现本地插件架构迁移与统一管理
- 新建本地插件统一架构,包含接口定义和基础类
- 实现三个本地插件:fileinfo(文件信息收集)、dcinfo(域控信息收集)、minidump(内存转储)
- 添加-localplugin参数,支持指定单个本地插件执行
- 完善参数验证机制,本地模式必须指定插件
- 集成新插件系统到核心扫描策略
- 修复Go方法调用机制导致的插件执行问题
- 支持跨平台和权限检查功能

支持的本地插件:
- fileinfo: 敏感文件扫描
- dcinfo: Windows域控信息收集
- minidump: lsass进程内存转储

使用方式: fscan -local -localplugin <plugin_name>
2025-08-09 22:43:28 +08:00

275 lines
7.1 KiB
Go

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()
},
))
}