mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00

将复杂的三文件插件架构(connector/exploiter/plugin)重构为简化的单文件插件架构, 大幅减少代码重复和维护成本,提升插件开发效率。 主要改进: • 将每个服务插件从3个文件简化为1个文件 • 删除过度设计的工厂模式、适配器模式等抽象层 • 消除plugins/services/、plugins/adapters/、plugins/base/复杂目录结构 • 实现直接的插件注册机制,提升系统简洁性 • 保持完全向后兼容,所有扫描功能和输出格式不变 重构统计: • 删除文件:100+个复杂架构文件 • 新增文件:20个简化的单文件插件 • 代码减少:每个插件减少60-80%代码量 • 功能增强:所有插件包含完整扫描和利用功能 已重构插件: MySQL, SSH, Redis, MongoDB, PostgreSQL, MSSQL, Oracle, Neo4j, Memcached, RabbitMQ, ActiveMQ, Cassandra, FTP, Kafka, LDAP, Rsync, SMTP, SNMP, Telnet, VNC 验证通过: 新系统编译运行正常,所有插件功能验证通过
386 lines
11 KiB
Go
386 lines
11 KiB
Go
package cleaner
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/shadow1ng/fscan/common"
|
|
"github.com/shadow1ng/fscan/plugins/base"
|
|
"github.com/shadow1ng/fscan/plugins/local"
|
|
)
|
|
|
|
// CleanerPlugin 系统痕迹清理插件 - 跨平台支持
|
|
type CleanerPlugin struct {
|
|
*local.BaseLocalPlugin
|
|
|
|
// 配置选项
|
|
targetFiles []string // 要清理的文件列表
|
|
cleanDirectories []string // 要清理的目录列表
|
|
currentExecutable string // 当前执行文件路径
|
|
workingDirectory string // 当前工作目录
|
|
cleanupStats map[string]int // 清理统计
|
|
}
|
|
|
|
// NewCleanerPlugin 创建系统痕迹清理插件
|
|
func NewCleanerPlugin() *CleanerPlugin {
|
|
metadata := &base.PluginMetadata{
|
|
Name: "cleaner",
|
|
Version: "1.0.0",
|
|
Author: "fscan-team",
|
|
Description: "跨平台系统痕迹清理插件,清理扫描过程中产生的文件和系统痕迹",
|
|
Category: "local",
|
|
Tags: []string{"local", "cleaner", "forensics", "cross-platform"},
|
|
Protocols: []string{"local"},
|
|
}
|
|
|
|
plugin := &CleanerPlugin{
|
|
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
|
|
targetFiles: make([]string, 0),
|
|
cleanDirectories: make([]string, 0),
|
|
cleanupStats: make(map[string]int),
|
|
}
|
|
|
|
// 支持所有主要平台
|
|
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
|
|
// 需要系统权限进行清理操作
|
|
plugin.SetRequiresPrivileges(false) // 根据当前用户权限进行清理
|
|
|
|
return plugin
|
|
}
|
|
|
|
// Initialize 初始化插件
|
|
func (p *CleanerPlugin) Initialize() error {
|
|
common.LogInfo(fmt.Sprintf("初始化系统痕迹清理插件 - 平台: %s", runtime.GOOS))
|
|
|
|
// 获取当前执行文件路径
|
|
if exe, err := os.Executable(); err == nil {
|
|
p.currentExecutable = exe
|
|
common.LogDebug(fmt.Sprintf("当前执行文件: %s", exe))
|
|
}
|
|
|
|
// 获取当前工作目录
|
|
if wd, err := os.Getwd(); err == nil {
|
|
p.workingDirectory = wd
|
|
common.LogDebug(fmt.Sprintf("当前工作目录: %s", wd))
|
|
}
|
|
|
|
// 扫描要清理的文件
|
|
if err := p.scanCleanupTargets(); err != nil {
|
|
return fmt.Errorf("扫描清理目标失败: %v", err)
|
|
}
|
|
|
|
return p.BaseLocalPlugin.Initialize()
|
|
}
|
|
|
|
// Scan 重写扫描方法以确保调用正确的ScanLocal实现
|
|
func (p *CleanerPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
|
return p.ScanLocal(ctx, info)
|
|
}
|
|
|
|
// ScanLocal 执行系统痕迹清理任务
|
|
func (p *CleanerPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
|
common.LogInfo("开始系统痕迹清理...")
|
|
|
|
// 执行清理操作
|
|
cleanupReport, err := p.performCleanup(ctx)
|
|
if err != nil {
|
|
return &base.ScanResult{
|
|
Success: false,
|
|
Error: err,
|
|
}, nil
|
|
}
|
|
|
|
result := &base.ScanResult{
|
|
Success: true,
|
|
Service: "SystemCleaner",
|
|
Banner: fmt.Sprintf("痕迹清理完成: 清理了 %d 个文件, %d 个目录, %d 个系统条目",
|
|
p.cleanupStats["files"], p.cleanupStats["directories"], p.cleanupStats["system_entries"]),
|
|
Extra: map[string]interface{}{
|
|
"platform": runtime.GOOS,
|
|
"files_cleaned": p.cleanupStats["files"],
|
|
"directories_cleaned": p.cleanupStats["directories"],
|
|
"system_entries_cleaned": p.cleanupStats["system_entries"],
|
|
"cleanup_report": cleanupReport,
|
|
},
|
|
}
|
|
|
|
common.LogSuccess(fmt.Sprintf("系统痕迹清理完成: 文件(%d) 目录(%d) 系统条目(%d)",
|
|
p.cleanupStats["files"], p.cleanupStats["directories"], p.cleanupStats["system_entries"]))
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// scanCleanupTargets 扫描要清理的目标
|
|
func (p *CleanerPlugin) scanCleanupTargets() error {
|
|
common.LogInfo("扫描清理目标...")
|
|
|
|
// 扫描当前目录下的fscan相关文件
|
|
if err := filepath.Walk(p.workingDirectory, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return nil // 忽略访问错误
|
|
}
|
|
|
|
if info.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
filename := strings.ToLower(info.Name())
|
|
|
|
// 检查fscan相关文件
|
|
if p.isFscanRelatedFile(filename) {
|
|
p.targetFiles = append(p.targetFiles, path)
|
|
common.LogDebug(fmt.Sprintf("发现清理目标: %s", path))
|
|
}
|
|
|
|
return nil
|
|
}); err != nil {
|
|
common.LogError(fmt.Sprintf("扫描文件失败: %v", err))
|
|
}
|
|
|
|
common.LogInfo(fmt.Sprintf("发现 %d 个文件需要清理", len(p.targetFiles)))
|
|
return nil
|
|
}
|
|
|
|
// isFscanRelatedFile 判断是否为fscan相关文件 - 使用保守的策略
|
|
func (p *CleanerPlugin) isFscanRelatedFile(filename string) bool {
|
|
// 严格的项目文件排除列表 - 确保不误删项目文件
|
|
excludePatterns := []string{
|
|
".go", ".mod", ".sum", ".md", ".yml", ".yaml", // 源码和配置
|
|
".git", ".claude", ".idea", ".vscode", // 版本控制和IDE
|
|
"dockerfile", "makefile", "license", "readme", // 项目文件
|
|
"plugins", "common", "core", "webscan", // 核心目录
|
|
"testdocker", // 测试配置
|
|
".json", ".xml", // 配置文件
|
|
}
|
|
|
|
// 检查是否为需要排除的文件
|
|
for _, exclude := range excludePatterns {
|
|
if strings.Contains(filename, exclude) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// 只清理明确的输出和结果文件 - 非常保守的策略
|
|
cleanPatterns := []string{
|
|
"result.txt", // 默认扫描结果文件
|
|
"results.txt", // 可能的结果文件
|
|
"output.txt", // 输出文件
|
|
"scan_result.txt", // 扫描结果
|
|
"keylog.txt", // 键盘记录输出
|
|
"my_keylog.txt", // 自定义键盘记录
|
|
}
|
|
|
|
// 排除当前执行文件(稍后单独处理)
|
|
if p.currentExecutable != "" {
|
|
currentExeName := strings.ToLower(filepath.Base(p.currentExecutable))
|
|
if filename == currentExeName {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// 只清理明确匹配的输出文件
|
|
for _, pattern := range cleanPatterns {
|
|
if filename == pattern { // 精确匹配,不使用 Contains
|
|
return true
|
|
}
|
|
}
|
|
|
|
// 清理明确的测试生成可执行文件(但保留源码)
|
|
if strings.HasSuffix(filename, ".exe") {
|
|
// 只清理包含特定测试标识的exe文件
|
|
testPatterns := []string{"_test.exe", "_debug.exe", "fscan_test", "fscan_debug"}
|
|
for _, pattern := range testPatterns {
|
|
if strings.Contains(filename, pattern) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// performCleanup 执行清理操作
|
|
func (p *CleanerPlugin) performCleanup(ctx context.Context) (map[string]interface{}, error) {
|
|
report := make(map[string]interface{})
|
|
|
|
// 初始化统计
|
|
p.cleanupStats["files"] = 0
|
|
p.cleanupStats["directories"] = 0
|
|
p.cleanupStats["system_entries"] = 0
|
|
|
|
// 1. 清理文件
|
|
common.LogInfo("清理相关文件...")
|
|
fileReport := p.cleanTargetFiles()
|
|
report["file_cleanup"] = fileReport
|
|
|
|
// 2. 清理系统痕迹(平台特定)
|
|
common.LogInfo("清理系统痕迹...")
|
|
systemReport := p.cleanSystemTraces()
|
|
report["system_cleanup"] = systemReport
|
|
|
|
// 3. 清理网络痕迹
|
|
common.LogInfo("清理网络痕迹...")
|
|
networkReport := p.cleanNetworkTraces()
|
|
report["network_cleanup"] = networkReport
|
|
|
|
// 4. 最后清理自身(需要特殊处理)
|
|
if p.currentExecutable != "" {
|
|
common.LogInfo("准备清理自身...")
|
|
selfReport := p.prepareSelfDestruction()
|
|
report["self_cleanup"] = selfReport
|
|
}
|
|
|
|
return report, nil
|
|
}
|
|
|
|
// cleanTargetFiles 清理文件
|
|
func (p *CleanerPlugin) cleanTargetFiles() []string {
|
|
var cleaned []string
|
|
|
|
for _, file := range p.targetFiles {
|
|
if err := p.secureDelete(file); err != nil {
|
|
common.LogError(fmt.Sprintf("删除文件失败 %s: %v", file, err))
|
|
} else {
|
|
cleaned = append(cleaned, file)
|
|
p.cleanupStats["files"]++
|
|
common.LogSuccess(fmt.Sprintf("已删除: %s", file))
|
|
}
|
|
}
|
|
|
|
return cleaned
|
|
}
|
|
|
|
// secureDelete 安全删除文件(覆盖后删除)
|
|
func (p *CleanerPlugin) secureDelete(filepath string) error {
|
|
// 获取文件信息
|
|
info, err := os.Stat(filepath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 小文件进行覆盖删除
|
|
if info.Size() < 10*1024*1024 { // 10MB以下
|
|
if err := p.overwriteFile(filepath); err != nil {
|
|
common.LogDebug(fmt.Sprintf("覆盖文件失败: %v", err))
|
|
}
|
|
}
|
|
|
|
// 删除文件
|
|
return os.Remove(filepath)
|
|
}
|
|
|
|
// overwriteFile 覆盖文件内容
|
|
func (p *CleanerPlugin) overwriteFile(filepath string) error {
|
|
info, err := os.Stat(filepath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
file, err := os.OpenFile(filepath, os.O_WRONLY, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
// 用随机数据覆盖
|
|
size := info.Size()
|
|
buffer := make([]byte, 4096)
|
|
for i := range buffer {
|
|
buffer[i] = byte(time.Now().UnixNano() % 256)
|
|
}
|
|
|
|
for size > 0 {
|
|
writeSize := int64(len(buffer))
|
|
if size < writeSize {
|
|
writeSize = size
|
|
}
|
|
|
|
if _, err := file.Write(buffer[:writeSize]); err != nil {
|
|
return err
|
|
}
|
|
size -= writeSize
|
|
}
|
|
|
|
return file.Sync()
|
|
}
|
|
|
|
// prepareSelfDestruction 准备自毁 - 平台特定实现在各自的文件中
|
|
|
|
// GetLocalData 获取清理器本地数据
|
|
func (p *CleanerPlugin) GetLocalData(ctx context.Context) (map[string]interface{}, error) {
|
|
data := make(map[string]interface{})
|
|
|
|
data["plugin_type"] = "cleaner"
|
|
data["platform"] = runtime.GOOS
|
|
data["current_executable"] = p.currentExecutable
|
|
data["working_directory"] = p.workingDirectory
|
|
data["cleanup_targets"] = len(p.targetFiles)
|
|
|
|
if hostname, err := os.Hostname(); err == nil {
|
|
data["hostname"] = hostname
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
// ExtractData 提取数据
|
|
func (p *CleanerPlugin) ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error) {
|
|
return &base.ExploitResult{
|
|
Success: true,
|
|
Output: fmt.Sprintf("系统痕迹清理完成,清理了 %d 个项目", p.cleanupStats["files"]+p.cleanupStats["directories"]+p.cleanupStats["system_entries"]),
|
|
Data: data,
|
|
Extra: map[string]interface{}{
|
|
"platform": runtime.GOOS,
|
|
"cleanup_stats": p.cleanupStats,
|
|
"status": "completed",
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// GetInfo 获取插件信息
|
|
func (p *CleanerPlugin) GetInfo() string {
|
|
var info strings.Builder
|
|
|
|
info.WriteString("跨平台系统痕迹清理插件\n")
|
|
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
|
|
info.WriteString("功能:\n")
|
|
info.WriteString(" - 清理fscan相关输出文件\n")
|
|
info.WriteString(" - 清理系统日志痕迹\n")
|
|
info.WriteString(" - 清理网络连接痕迹\n")
|
|
info.WriteString(" - 清理Shell历史记录\n")
|
|
info.WriteString(" - 安全删除敏感文件\n")
|
|
info.WriteString(" - 自毁功能\n")
|
|
info.WriteString("注意: 根据当前用户权限执行清理操作\n")
|
|
|
|
return info.String()
|
|
}
|
|
|
|
// RegisterCleanerPlugin 注册系统痕迹清理插件
|
|
func RegisterCleanerPlugin() {
|
|
factory := base.NewSimplePluginFactory(
|
|
&base.PluginMetadata{
|
|
Name: "cleaner",
|
|
Version: "1.0.0",
|
|
Author: "fscan-team",
|
|
Description: "跨平台系统痕迹清理插件,清理扫描过程中产生的文件和系统痕迹",
|
|
Category: "local",
|
|
Tags: []string{"cleaner", "local", "forensics", "cross-platform"},
|
|
Protocols: []string{"local"},
|
|
},
|
|
func() base.Plugin {
|
|
return NewCleanerPlugin()
|
|
},
|
|
)
|
|
|
|
base.GlobalPluginRegistry.Register("cleaner", factory)
|
|
}
|
|
|
|
// init 插件注册函数
|
|
func init() {
|
|
RegisterCleanerPlugin()
|
|
} |