mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-24 12:11:52 +08:00
fix: harden scan edge cases
This commit is contained in:
@@ -67,7 +67,12 @@ func (s *AliveScanStrategy) Execute(ctx context.Context, session *common.ScanSes
|
||||
|
||||
// performAliveScan 执行存活探测
|
||||
func (s *AliveScanStrategy) performAliveScan(ctx context.Context, info common.HostInfo, session *common.ScanSession) {
|
||||
iter, err := parsers.NewHostIterator(info.Host, session.Params.HostsFile, session.Params.ExcludeHosts)
|
||||
excludes, err := loadHostExcludes(session.Params)
|
||||
if err != nil {
|
||||
session.LogError(i18n.Tr("parse_target_failed", err))
|
||||
return
|
||||
}
|
||||
iter, err := parsers.NewHostIterator(info.Host, session.Params.HostsFile, excludes...)
|
||||
if err != nil {
|
||||
session.LogError(i18n.Tr("parse_target_failed", err))
|
||||
return
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/shadow1ng/fscan/common"
|
||||
"github.com/shadow1ng/fscan/common/parsers"
|
||||
)
|
||||
|
||||
func loadHostExcludes(params *common.FlagVars) ([]string, error) {
|
||||
if params == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
excludes := make([]string, 0, 1)
|
||||
if strings.TrimSpace(params.ExcludeHosts) != "" {
|
||||
excludes = append(excludes, params.ExcludeHosts)
|
||||
}
|
||||
if strings.TrimSpace(params.ExcludeHostsFile) == "" {
|
||||
return excludes, nil
|
||||
}
|
||||
|
||||
lines, err := parsers.ReadLinesFromFile(params.ExcludeHostsFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return append(excludes, lines...), nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/shadow1ng/fscan/common"
|
||||
)
|
||||
|
||||
func TestLoadHostExcludesIncludesExcludeFile(t *testing.T) {
|
||||
path := t.TempDir() + "/exclude.txt"
|
||||
if err := os.WriteFile(path, []byte("192.168.1.2\n# comment\n192.168.1.3\n"), 0o600); err != nil {
|
||||
t.Fatalf("WriteFile error = %v", err)
|
||||
}
|
||||
|
||||
got, err := loadHostExcludes(&common.FlagVars{
|
||||
ExcludeHosts: "192.168.1.1",
|
||||
ExcludeHostsFile: path,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("loadHostExcludes error = %v", err)
|
||||
}
|
||||
|
||||
want := []string{"192.168.1.1", "192.168.1.2", "192.168.1.3"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("loadHostExcludes = %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -203,7 +203,7 @@ func EnhancedPortScan(ctx context.Context, hosts []string, ports string, timeout
|
||||
// 初始化端口扫描进度条
|
||||
if totalTasks > 0 && config.Output.ShowProgress {
|
||||
description := i18n.Tr("port_scan_progress_description", threadNum)
|
||||
common.InitProgressBar(int64(totalTasks), description)
|
||||
common.InitProgressBar(totalTasks, description)
|
||||
}
|
||||
session.LogDebug(i18n.GetText("port_scan_debug_progress_ready"))
|
||||
|
||||
|
||||
+14
-7
@@ -145,7 +145,12 @@ func (s *ServiceScanStrategy) performHostScan(ctx context.Context, session *comm
|
||||
config := session.Config
|
||||
state := session.State
|
||||
|
||||
iter, err := parsers.NewHostIterator(info.Host, session.Params.HostsFile, session.Params.ExcludeHosts)
|
||||
excludes, err := loadHostExcludes(session.Params)
|
||||
if err != nil {
|
||||
session.LogError(fmt.Sprintf("%s: %v", i18n.GetText("parse_target_failed"), err))
|
||||
return
|
||||
}
|
||||
iter, err := parsers.NewHostIterator(info.Host, session.Params.HostsFile, excludes...)
|
||||
if err != nil {
|
||||
session.LogError(fmt.Sprintf("%s: %v", i18n.GetText("parse_target_failed"), err))
|
||||
return
|
||||
@@ -157,6 +162,7 @@ func (s *ServiceScanStrategy) performHostScan(ctx context.Context, session *comm
|
||||
pluginsToRun, isCustomMode := s.GetPlugins(config)
|
||||
totalAlive := 0
|
||||
sawHosts := false
|
||||
performedLiveness := false
|
||||
|
||||
for {
|
||||
hosts, err := iter.NextBatch(ctx, targetHostBatchSize(config))
|
||||
@@ -170,6 +176,7 @@ func (s *ServiceScanStrategy) performHostScan(ctx context.Context, session *comm
|
||||
sawHosts = true
|
||||
|
||||
if s.shouldPerformLivenessCheck(hosts, config) {
|
||||
performedLiveness = true
|
||||
hosts = CheckLive(ctx, hosts, false, session)
|
||||
}
|
||||
totalAlive += len(hosts)
|
||||
@@ -181,7 +188,7 @@ func (s *ServiceScanStrategy) performHostScan(ctx context.Context, session *comm
|
||||
s.scanHostBatch(ctx, session, hosts, info, pluginsToRun, isCustomMode, ch, wg)
|
||||
}
|
||||
|
||||
if sawHosts && s.shouldReportAliveCount(config) {
|
||||
if sawHosts && performedLiveness {
|
||||
session.LogInfo(i18n.Tr("alive_hosts_count_info", totalAlive))
|
||||
}
|
||||
|
||||
@@ -234,10 +241,6 @@ func (s *ServiceScanStrategy) scanHostBatch(ctx context.Context, session *common
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ServiceScanStrategy) shouldReportAliveCount(config *common.Config) bool {
|
||||
return !config.DisablePing
|
||||
}
|
||||
|
||||
// dispatchUDPPlugins 分发UDP协议插件,跳过TCP端口扫描链路
|
||||
func (s *ServiceScanStrategy) dispatchUDPPlugins(ctx context.Context, session *common.ScanSession, hosts []string, baseInfo common.HostInfo, config *common.Config, ch chan struct{}, wg *sync.WaitGroup) {
|
||||
_, isCustomMode := s.GetPlugins(config)
|
||||
@@ -341,7 +344,11 @@ func (s *ServiceScanStrategy) discoverTargets(ctx context.Context, hostInput str
|
||||
config := session.Config
|
||||
state := session.State
|
||||
// 标准流程:解析目标主机
|
||||
hosts, err := parsers.ParseIP(hostInput, session.Params.HostsFile, session.Params.ExcludeHosts)
|
||||
excludes, err := loadHostExcludes(session.Params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("parse_target_failed"), err)
|
||||
}
|
||||
hosts, err := parsers.ParseIP(hostInput, session.Params.HostsFile, excludes...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("parse_target_failed"), err)
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ type SocketIterator struct {
|
||||
ports []int
|
||||
hostIdx int
|
||||
portIdx int
|
||||
total int
|
||||
total int64
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ func NewSocketIterator(hosts []string, ports []int, exclude map[int]struct{}) *S
|
||||
return &SocketIterator{
|
||||
hosts: hosts,
|
||||
ports: sortedPorts,
|
||||
total: len(hosts) * len(sortedPorts),
|
||||
total: int64(len(hosts)) * int64(len(sortedPorts)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func (it *SocketIterator) Next() (string, int, bool) {
|
||||
}
|
||||
|
||||
// Total 返回总任务数(用于进度条)
|
||||
func (it *SocketIterator) Total() int {
|
||||
func (it *SocketIterator) Total() int64 {
|
||||
return it.total
|
||||
}
|
||||
|
||||
|
||||
@@ -182,6 +182,17 @@ func TestSocketIterator_EmptyInputs(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSocketIteratorTotalUsesInt64(t *testing.T) {
|
||||
hosts := make([]string, 1<<20)
|
||||
ports := make([]int, 4096)
|
||||
it := NewSocketIterator(hosts, ports, nil)
|
||||
|
||||
want := int64(len(hosts)) * int64(len(ports))
|
||||
if it.Total() != want {
|
||||
t.Fatalf("Total() = %d, want %d", it.Total(), want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSocketIterator_PortPrioritySort 验证端口优先级排序
|
||||
// 高价值端口(80, 443, 22等)应该排在前面
|
||||
func TestSocketIterator_PortPrioritySort(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user