diff --git a/Common/Flag.go b/Common/Flag.go index 5b5b417..8ce0fb3 100644 --- a/Common/Flag.go +++ b/Common/Flag.go @@ -35,7 +35,7 @@ func Flag(Info *HostInfo) { flag.StringVar(&SshKeyPath, "sshkey", "", "SSH密钥文件(id_rsa)") // 扫描配置 - flag.StringVar(&ScanMode, "m", "all", "扫描类型,例如: -m ssh") + flag.StringVar(&ScanMode, "m", "All", "扫描类型,例如: -m ssh") flag.IntVar(&ThreadNum, "t", 600, "线程数量") flag.Int64Var(&Timeout, "time", 3, "超时时间(秒)") flag.IntVar(&LiveTop, "top", 10, "显示存活主机数量") diff --git a/Common/ParseScanMode.go b/Common/ParseScanMode.go new file mode 100644 index 0000000..f10905d --- /dev/null +++ b/Common/ParseScanMode.go @@ -0,0 +1,98 @@ +package Common + +import "fmt" + +// 扫描模式常量 - 使用大写开头表示这是一个预设的扫描模式 +const ( + ModeAll = "All" // 全量扫描 + ModeBasic = "Basic" // 基础扫描 + ModeDatabase = "Database" // 数据库扫描 + ModeWeb = "Web" // Web扫描 + ModeService = "Service" // 服务扫描 + ModeVul = "Vul" // 漏洞扫描 + ModePort = "Port" // 端口扫描 + ModeICMP = "ICMP" // ICMP探测 + ModeLocal = "Local" // 本地信息收集 +) + +// 插件分类映射表 - 所有插件名使用小写 +var pluginGroups = map[string][]string{ + ModeAll: { + "web", "fcgi", // web类 + "mysql", "mssql", "redis", "mongodb", "postgres", // 数据库类 + "oracle", "memcached", // 数据库类 + "ftp", "ssh", "smb", "rdp", "vnc", "netbios", // 服务类 + "ms17010", "smbghost", "smb2", // 漏洞类 + "findnet", "wmiexec", // 其他 + }, + ModeBasic: { + "web", "ftp", "ssh", "smb", "findnet", + }, + ModeDatabase: { + "mysql", "mssql", "redis", "mongodb", + "postgres", "oracle", "memcached", + }, + ModeWeb: { + "web", "fcgi", + }, + ModeService: { + "ftp", "ssh", "smb", "rdp", "vnc", "netbios", + }, + ModeVul: { + "ms17010", "smbghost", "smb2", + }, + ModeLocal: { + "localinfo", + }, +} + +// ParseScanMode 解析扫描模式 +func ParseScanMode(mode string) { + fmt.Printf("[*] 解析扫描模式: %s\n", mode) + + // 检查是否是预设模式 + presetModes := []string{ + ModeAll, ModeBasic, ModeDatabase, ModeWeb, + ModeService, ModeVul, ModePort, ModeICMP, ModeLocal, + } + + for _, presetMode := range presetModes { + if mode == presetMode { + ScanMode = mode + if plugins := GetPluginsForMode(mode); plugins != nil { + fmt.Printf("[+] 使用预设模式: %s, 包含插件: %v\n", mode, plugins) + } else { + fmt.Printf("[+] 使用预设模式: %s\n", mode) + } + return + } + } + + // 检查是否是有效的插件名 + if _, exists := PluginManager[mode]; exists { + ScanMode = mode + fmt.Printf("[+] 使用单个插件: %s\n", mode) + return + } + + // 默认使用All模式 + ScanMode = ModeAll + fmt.Printf("[*] 未识别的模式,使用默认模式: %s\n", ModeAll) + fmt.Printf("[+] 包含插件: %v\n", pluginGroups[ModeAll]) +} + +// GetPluginsForMode 获取指定模式下的插件列表 +func GetPluginsForMode(mode string) []string { + plugins, exists := pluginGroups[mode] + if exists { + return plugins + } + return nil +} + +// 辅助函数 +func IsPortScan() bool { return ScanMode == ModePort } +func IsICMPScan() bool { return ScanMode == ModeICMP } +func IsLocalScan() bool { return ScanMode == ModeLocal } +func IsWebScan() bool { return ScanMode == ModeWeb } +func GetScanMode() string { return ScanMode } diff --git a/Core/Registry.go b/Core/Registry.go index 1ea8650..6a42d5c 100644 --- a/Core/Registry.go +++ b/Core/Registry.go @@ -9,7 +9,6 @@ func init() { // 注册标准端口服务扫描 Common.RegisterPlugin("ftp", Common.ScanPlugin{ Name: "FTP", - Port: 21, ScanFunc: Plugins.FtpScan, }) @@ -20,92 +19,77 @@ func init() { Common.RegisterPlugin("findnet", Common.ScanPlugin{ Name: "FindNet", - Port: 135, ScanFunc: Plugins.Findnet, }) Common.RegisterPlugin("netbios", Common.ScanPlugin{ Name: "NetBIOS", - Port: 139, ScanFunc: Plugins.NetBIOS, }) Common.RegisterPlugin("smb", Common.ScanPlugin{ Name: "SMB", - Port: 445, ScanFunc: Plugins.SmbScan, }) Common.RegisterPlugin("mssql", Common.ScanPlugin{ Name: "MSSQL", - Port: 1433, ScanFunc: Plugins.MssqlScan, }) Common.RegisterPlugin("oracle", Common.ScanPlugin{ Name: "Oracle", - Port: 1521, ScanFunc: Plugins.OracleScan, }) Common.RegisterPlugin("mysql", Common.ScanPlugin{ Name: "MySQL", - Port: 3306, ScanFunc: Plugins.MysqlScan, }) Common.RegisterPlugin("rdp", Common.ScanPlugin{ Name: "RDP", - Port: 3389, ScanFunc: Plugins.RdpScan, }) Common.RegisterPlugin("postgres", Common.ScanPlugin{ Name: "PostgreSQL", - Port: 5432, ScanFunc: Plugins.PostgresScan, }) Common.RegisterPlugin("vnc", Common.ScanPlugin{ Name: "VNC", - Port: 5900, ScanFunc: Plugins.VncScan, }) Common.RegisterPlugin("redis", Common.ScanPlugin{ Name: "Redis", - Port: 6379, ScanFunc: Plugins.RedisScan, }) Common.RegisterPlugin("fcgi", Common.ScanPlugin{ Name: "FastCGI", - Port: 9000, ScanFunc: Plugins.FcgiScan, }) Common.RegisterPlugin("memcached", Common.ScanPlugin{ Name: "Memcached", - Port: 11211, ScanFunc: Plugins.MemcachedScan, }) Common.RegisterPlugin("mongodb", Common.ScanPlugin{ Name: "MongoDB", - Port: 27017, ScanFunc: Plugins.MongodbScan, }) // 注册特殊扫描类型 Common.RegisterPlugin("ms17010", Common.ScanPlugin{ Name: "MS17010", - Port: 445, ScanFunc: Plugins.MS17010, }) Common.RegisterPlugin("smbghost", Common.ScanPlugin{ Name: "SMBGhost", - Port: 445, ScanFunc: Plugins.SmbGhost, }) @@ -114,21 +98,18 @@ func init() { ScanFunc: Plugins.WebTitle, }) - Common.RegisterPlugin("webonly", Common.ScanPlugin{ - Name: "WebOnly", - Port: 0, - ScanFunc: Plugins.WebTitle, + Common.RegisterPlugin("webpoc", Common.ScanPlugin{ + Name: "WebPoc", + ScanFunc: Plugins.WebPoc, }) Common.RegisterPlugin("smb2", Common.ScanPlugin{ Name: "SMBScan2", - Port: 445, ScanFunc: Plugins.SmbScan2, }) Common.RegisterPlugin("wmiexec", Common.ScanPlugin{ Name: "WMIExec", - Port: 135, ScanFunc: Plugins.WmiExec, }) diff --git a/Core/Scanner.go b/Core/Scanner.go index 8e79ef2..c326721 100644 --- a/Core/Scanner.go +++ b/Core/Scanner.go @@ -4,147 +4,141 @@ import ( "fmt" "github.com/shadow1ng/fscan/Common" "github.com/shadow1ng/fscan/WebScan/lib" - "strconv" "strings" "sync" ) +// Scan 执行扫描主流程 func Scan(info Common.HostInfo) { fmt.Println("[*] 开始信息扫描...") - // 本地信息收集模块 - if Common.ScanMode == "localinfo" { - ch := make(chan struct{}, Common.ThreadNum) - wg := sync.WaitGroup{} - AddScan("localinfo", info, &ch, &wg) - wg.Wait() - Common.LogWG.Wait() - close(Common.Results) - fmt.Printf("[✓] 扫描完成 %v/%v\n", Common.End, Common.Num) - return - } + Common.ParseScanMode(Common.ScanMode) - // 解析目标主机IP - Hosts, err := Common.ParseIP(info.Host, Common.HostsFile, Common.ExcludeHosts) - if err != nil { - fmt.Printf("[!] 解析主机错误: %v\n", err) - return - } - - // 初始化配置 - lib.Inithttp() ch := make(chan struct{}, Common.ThreadNum) wg := sync.WaitGroup{} - var AlivePorts []string - if len(Hosts) > 0 || len(Common.HostPort) > 0 { + // 本地信息收集模式 + if Common.IsLocalScan() { + executeScans([]Common.HostInfo{info}, &ch, &wg) + finishScan(&wg) + return + } + + // 初始化并解析目标 + hosts, err := Common.ParseIP(info.Host, Common.HostsFile, Common.ExcludeHosts) + if err != nil { + fmt.Printf("[-] 解析主机错误: %v\n", err) + return + } + lib.Inithttp() + + // 执行目标扫描 + executeScan(hosts, info, &ch, &wg) + finishScan(&wg) +} + +// executeScan 执行主扫描流程 +func executeScan(hosts []string, info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { + var targetInfos []Common.HostInfo + + // 处理主机和端口扫描 + if len(hosts) > 0 || len(Common.HostPort) > 0 { // ICMP存活性检测 - if (Common.DisablePing == false && len(Hosts) > 1) || Common.ScanMode == "icmp" { - Hosts = CheckLive(Hosts, Common.UsePing) - fmt.Printf("[+] ICMP存活主机数量: %d\n", len(Hosts)) - if Common.ScanMode == "icmp" { - Common.LogWG.Wait() + if (Common.DisablePing == false && len(hosts) > 1) || Common.IsICMPScan() { + hosts = CheckLive(hosts, Common.UsePing) + fmt.Printf("[+] ICMP存活主机数量: %d\n", len(hosts)) + if Common.IsICMPScan() { return } } - // 端口扫描策略 - AlivePorts = executeScanStrategy(Hosts, Common.ScanMode) + // 获取存活端口 + var alivePorts []string + if Common.IsWebScan() { + alivePorts = NoPortScan(hosts, Common.Ports) + } else if len(hosts) > 0 { + alivePorts = PortScan(hosts, Common.Ports, Common.Timeout) + fmt.Printf("[+] 存活端口数量: %d\n", len(alivePorts)) + if Common.IsPortScan() { + return + } + } // 处理自定义端口 if len(Common.HostPort) > 0 { - AlivePorts = append(AlivePorts, Common.HostPort...) - AlivePorts = Common.RemoveDuplicate(AlivePorts) + alivePorts = append(alivePorts, Common.HostPort...) + alivePorts = Common.RemoveDuplicate(alivePorts) Common.HostPort = nil - fmt.Printf("[+] 总计存活端口: %d\n", len(AlivePorts)) + fmt.Printf("[+] 总计存活端口: %d\n", len(alivePorts)) } - // 执行扫描任务 - fmt.Println("[*] 开始漏洞扫描...") - for _, targetIP := range AlivePorts { - hostParts := strings.Split(targetIP, ":") - if len(hostParts) != 2 { - fmt.Printf("[!] 无效的目标地址格式: %s\n", targetIP) - continue - } - info.Host, info.Ports = hostParts[0], hostParts[1] - - executeScanTasks(info, Common.ScanMode, &ch, &wg) - } + targetInfos = prepareTargetInfos(alivePorts, info) } - // URL扫描 + // 准备URL扫描目标 for _, url := range Common.URLs { - info.Url = url - AddScan("web", info, &ch, &wg) + urlInfo := info + urlInfo.Url = url + targetInfos = append(targetInfos, urlInfo) } - // 等待所有任务完成 + // 执行扫描任务 + if len(targetInfos) > 0 { + fmt.Println("[*] 开始漏洞扫描...") + executeScans(targetInfos, ch, wg) + } +} + +// prepareTargetInfos 准备扫描目标信息 +func prepareTargetInfos(alivePorts []string, baseInfo Common.HostInfo) []Common.HostInfo { + var infos []Common.HostInfo + for _, targetIP := range alivePorts { + hostParts := strings.Split(targetIP, ":") + if len(hostParts) != 2 { + fmt.Printf("[-] 无效的目标地址格式: %s\n", targetIP) + continue + } + info := baseInfo + info.Host = hostParts[0] + info.Ports = hostParts[1] + infos = append(infos, info) + } + return infos +} + +// executeScans 统一执行扫描任务 +func executeScans(targets []Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { + mode := Common.GetScanMode() + + // 判断是否是预设模式(大写开头) + if plugins := Common.GetPluginsForMode(mode); plugins != nil { + // 使用预设模式的插件组 + for _, target := range targets { + for _, plugin := range plugins { + AddScan(plugin, target, ch, wg) + } + } + } else { + // 使用单个插件 + for _, target := range targets { + AddScan(mode, target, ch, wg) + } + } +} + +// finishScan 完成扫描任务 +func finishScan(wg *sync.WaitGroup) { wg.Wait() Common.LogWG.Wait() close(Common.Results) fmt.Printf("[+] 扫描已完成: %v/%v\n", Common.End, Common.Num) } -// executeScanStrategy 执行端口扫描策略 -func executeScanStrategy(Hosts []string, scanType string) []string { - switch scanType { - case "webonly", "webpoc": - return NoPortScan(Hosts, Common.Ports) - case "hostname": - Common.Ports = "139" - return NoPortScan(Hosts, Common.Ports) - default: - if len(Hosts) > 0 { - ports := PortScan(Hosts, Common.Ports, Common.Timeout) - fmt.Printf("[+] 存活端口数量: %d\n", len(ports)) - if scanType == "portscan" { - Common.LogWG.Wait() - return nil - } - return ports - } - } - return nil -} - -// executeScanTasks 执行扫描任务 -func executeScanTasks(info Common.HostInfo, scanType string, ch *chan struct{}, wg *sync.WaitGroup) { - if scanType == "all" || scanType == "main" { - // 根据端口选择扫描插件 - switch info.Ports { - case "135": - AddScan("findnet", info, ch, wg) - if Common.EnableWmi { - AddScan("wmiexec", info, ch, wg) - } - case "445": - AddScan("ms17010", info, ch, wg) - case "9000": - AddScan("web", info, ch, wg) - AddScan("fcgi", info, ch, wg) - default: - // 查找对应端口的插件 - for name, plugin := range Common.PluginManager { - if strconv.Itoa(plugin.Port) == info.Ports { - AddScan(name, info, ch, wg) - return - } - } - // 默认执行Web扫描 - AddScan("web", info, ch, wg) - } - } else { - // 直接使用指定的扫描类型 - AddScan(scanType, info, ch, wg) - } -} - // Mutex用于保护共享资源的并发访问 var Mutex = &sync.Mutex{} // AddScan 添加扫描任务到并发队列 -func AddScan(scantype string, info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { +func AddScan(plugin string, info Common.HostInfo, ch *chan struct{}, wg *sync.WaitGroup) { // 获取信号量,控制并发数 *ch <- struct{}{} // 添加等待组计数 @@ -163,7 +157,7 @@ func AddScan(scantype string, info Common.HostInfo, ch *chan struct{}, wg *sync. Mutex.Unlock() // 执行扫描 - ScanFunc(&scantype, &info) + ScanFunc(&plugin, &info) // 增加已完成任务数 Mutex.Lock() diff --git a/Plugins/WebPoc.go b/Plugins/WebPoc.go new file mode 100644 index 0000000..98bee71 --- /dev/null +++ b/Plugins/WebPoc.go @@ -0,0 +1,12 @@ +package Plugins + +import ( + "github.com/shadow1ng/fscan/Common" + "github.com/shadow1ng/fscan/WebScan" +) + +// WebPoc 直接执行Web漏洞扫描 +func WebPoc(info *Common.HostInfo) error { + WebScan.WebScan(info) + return nil +} diff --git a/Plugins/WebTitle.go b/Plugins/WebTitle.go index 9337601..9719a23 100644 --- a/Plugins/WebTitle.go +++ b/Plugins/WebTitle.go @@ -20,12 +20,6 @@ import ( // WebTitle 获取Web标题并执行扫描 func WebTitle(info *Common.HostInfo) error { - // 如果是webpoc扫描模式,直接执行WebScan - if Common.ScanMode == "webpoc" { - WebScan.WebScan(info) - return nil - } - // 获取网站标题信息 err, CheckData := GOWebTitle(info) info.Infostr = WebScan.InfoCheck(info.Url, &CheckData) @@ -41,7 +35,7 @@ func WebTitle(info *Common.HostInfo) error { if !Common.DisablePoc && err == nil { WebScan.WebScan(info) } else { - errlog := fmt.Sprintf("[-] webtitle %v %v", info.Url, err) + errlog := fmt.Sprintf("[-] 网站标题 %v %v", info.Url, err) Common.LogError(errlog) }