mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-22 03:10:42 +08:00
refactor: 完成全局状态到 session 的完整迁移
将 plugins/services、plugins/local、plugins/web、webscan 层的日志输出、 漏洞结果保存和 TCP 计数器从全局 common.Log*/GetGlobalState() 迁移到 session 实例方法,确保 SDK 并发扫描时各实例完全隔离。 - 50 个文件,所有插件日志走 session.Log* - DoRequest 加入 session 参数,计数器走 session.State - POC 执行器通过 POCContext.Session 传递 - 仅保留 init() 和 CEL runtime 等无 session 场景的全局回退
This commit is contained in:
+22
-7
@@ -367,7 +367,7 @@ func reverseCheck(r *Reverse, timeout int64) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
resp, err := DoRequest(req, false)
|
||||
resp, err := DoRequest(req, false, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -419,7 +419,8 @@ func RandomStr(randSource *rand.Rand, letterBytes string, n int) string {
|
||||
}
|
||||
|
||||
// DoRequest 执行 HTTP 请求
|
||||
func DoRequest(req *http.Request, redirect bool) (*Response, error) {
|
||||
// session 为 nil 时回退到全局 state(兼容 CEL runtime 等无 session 场景)
|
||||
func DoRequest(req *http.Request, redirect bool, session *common.ScanSession) (*Response, error) {
|
||||
// 处理请求头
|
||||
if req.Body != nil && req.Body != http.NoBody {
|
||||
body, err := io.ReadAll(req.Body)
|
||||
@@ -444,9 +445,23 @@ func DoRequest(req *http.Request, redirect bool) (*Response, error) {
|
||||
|
||||
// 执行请求
|
||||
// 检查发包限制
|
||||
if canSend, reason := common.CanSendPacket(); !canSend {
|
||||
common.LogError(i18n.Tr("webscan_request_restricted", req.URL.String(), reason))
|
||||
return nil, fmt.Errorf("%s", i18n.Tr("network_rate_limited", reason))
|
||||
var state *common.State
|
||||
if session != nil {
|
||||
state = session.State
|
||||
if canSend, err := common.CanSendPacketWith(session.Config, state); !canSend {
|
||||
reason := ""
|
||||
if err != nil {
|
||||
reason = err.Error()
|
||||
}
|
||||
common.LogError(i18n.Tr("webscan_request_restricted", req.URL.String(), reason))
|
||||
return nil, fmt.Errorf("%s", i18n.Tr("network_rate_limited", reason))
|
||||
}
|
||||
} else {
|
||||
state = common.GetGlobalState()
|
||||
if canSend, reason := common.CanSendPacket(); !canSend {
|
||||
common.LogError(i18n.Tr("webscan_request_restricted", req.URL.String(), reason))
|
||||
return nil, fmt.Errorf("%s", i18n.Tr("network_rate_limited", reason))
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -480,12 +495,12 @@ func DoRequest(req *http.Request, redirect bool) (*Response, error) {
|
||||
|
||||
if err != nil {
|
||||
// HTTP请求失败,计为TCP失败
|
||||
common.GetGlobalState().IncrementTCPFailedPacketCount()
|
||||
state.IncrementTCPFailedPacketCount()
|
||||
return nil, fmt.Errorf("%s: %w", i18n.GetText("webscan_request_execute_failed"), err)
|
||||
}
|
||||
|
||||
// HTTP请求成功,计为TCP成功
|
||||
common.GetGlobalState().IncrementTCPSuccessPacketCount()
|
||||
state.IncrementTCPSuccessPacketCount()
|
||||
defer func() { _ = oResp.Body.Close() }()
|
||||
|
||||
// 解析响应
|
||||
|
||||
@@ -1104,7 +1104,7 @@ func TestDoRequestBuffersUnknownLengthBody(t *testing.T) {
|
||||
}
|
||||
req.ContentLength = -1
|
||||
|
||||
if _, err := DoRequest(req, false); err != nil {
|
||||
if _, err := DoRequest(req, false, nil); err != nil {
|
||||
t.Fatalf("DoRequest error = %v", err)
|
||||
}
|
||||
if gotContentLength != "3" {
|
||||
@@ -1147,7 +1147,7 @@ func TestDoRequestReplaysBodyForGMTLSFallback(t *testing.T) {
|
||||
t.Fatalf("NewRequest error = %v", err)
|
||||
}
|
||||
|
||||
if _, err := DoRequest(req, false); err != nil {
|
||||
if _, err := DoRequest(req, false, nil); err != nil {
|
||||
t.Fatalf("DoRequest error = %v", err)
|
||||
}
|
||||
if gotBody != "payload" {
|
||||
|
||||
+18
-17
@@ -52,6 +52,7 @@ type VulnResult struct {
|
||||
type POCContext struct {
|
||||
DNSLog bool // 是否启用DNSLog检测
|
||||
POCFull bool // 是否完整POC扫描
|
||||
Session *common.ScanSession
|
||||
}
|
||||
|
||||
// CheckMultiPoc 并发执行多个POC检测
|
||||
@@ -82,7 +83,7 @@ func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int, pocCtx *POCConte
|
||||
|
||||
// 处理执行过程中的错误
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("webscan_poc_exec_error", task.Poc.Name, err))
|
||||
pocCtx.Session.LogError(i18n.Tr("webscan_poc_exec_error", task.Poc.Name, err))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -117,7 +118,7 @@ func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int, pocCtx *POCConte
|
||||
Status: "vulnerable",
|
||||
Details: details,
|
||||
}
|
||||
_ = common.SaveResult(result)
|
||||
_ = pocCtx.Session.SaveResult(result)
|
||||
|
||||
// 构造控制台输出的日志信息
|
||||
logMsg := i18n.Tr("webscan_vuln_detail_header",
|
||||
@@ -141,7 +142,7 @@ func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int, pocCtx *POCConte
|
||||
}
|
||||
|
||||
// 输出成功日志
|
||||
common.LogVuln(logMsg)
|
||||
pocCtx.Session.LogVuln(logMsg)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -216,7 +217,7 @@ func executePoc(oReq *http.Request, p *Poc, pocCtx *POCContext) (bool, string, e
|
||||
continue
|
||||
}
|
||||
if _, err = evalset(env, variableMap, key, expression); err != nil {
|
||||
common.LogError(i18n.Tr("webscan_set_exec_error", p.Name, err))
|
||||
pocCtx.Session.LogError(i18n.Tr("webscan_set_exec_error", p.Name, err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,11 +227,11 @@ func executePoc(oReq *http.Request, p *Poc, pocCtx *POCContext) (bool, string, e
|
||||
return success, "", err
|
||||
}
|
||||
|
||||
return executeRules(oReq, p, variableMap, req, env)
|
||||
return executeRules(oReq, p, variableMap, req, env, pocCtx.Session)
|
||||
}
|
||||
|
||||
// executeRules 执行POC规则并返回结果
|
||||
func executeRules(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, env *cel.Env) (bool, string, error) {
|
||||
func executeRules(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, env *cel.Env, session *common.ScanSession) (bool, string, error) {
|
||||
// 处理单个规则的函数
|
||||
executeRule := func(rule Rules) (bool, error) {
|
||||
Headers := cloneMap(rule.Headers)
|
||||
@@ -279,7 +280,7 @@ func executeRules(oReq *http.Request, p *Poc, variableMap map[string]interface{}
|
||||
_ = Headers // 清空Headers
|
||||
|
||||
// 发送请求
|
||||
resp, err := DoRequest(newRequest, rule.FollowRedirects)
|
||||
resp, err := DoRequest(newRequest, rule.FollowRedirects, session)
|
||||
newRequest = nil
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -447,7 +448,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{},
|
||||
// 检查是否需要进行参数Fuzz测试
|
||||
if !isFuzz(rule, p.Sets) {
|
||||
// 不需要Fuzz,直接发送请求
|
||||
success, err = clustersend(oReq, variableMap, req, env, rule)
|
||||
success, err = clustersend(oReq, variableMap, req, env, rule, pocCtx.Session)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -492,7 +493,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{},
|
||||
}
|
||||
output, err := evalset1(env, variableMap, key, expr)
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("webscan_set_exec_error", key, err))
|
||||
pocCtx.Session.LogError(i18n.Tr("webscan_set_exec_error", key, err))
|
||||
}
|
||||
payloads[key] = output
|
||||
}
|
||||
@@ -513,7 +514,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{},
|
||||
ruleHash[ruleMD5] = struct{}{}
|
||||
|
||||
// 发送请求并处理结果
|
||||
success, err = clustersend(oReq, variableMap, req, env, currentRule)
|
||||
success, err = clustersend(oReq, variableMap, req, env, currentRule, pocCtx.Session)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -524,7 +525,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{},
|
||||
// 处理成功情况
|
||||
if currentRule.Continue {
|
||||
// 使用Continue标志时,记录但继续测试其他参数
|
||||
recordVulnerabilityResult(targetURL, p, currentParams, false)
|
||||
recordVulnerabilityResult(targetURL, p, currentParams, false, pocCtx.Session)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -532,7 +533,7 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{},
|
||||
strMap = append(strMap, currentParams...)
|
||||
if ruleIndex == len(p.Rules)-1 {
|
||||
// 最终规则成功,记录完整的结果并返回
|
||||
recordVulnerabilityResult(targetURL, p, strMap, false)
|
||||
recordVulnerabilityResult(targetURL, p, strMap, false, pocCtx.Session)
|
||||
return false, nil
|
||||
}
|
||||
break paramLoop
|
||||
@@ -617,7 +618,7 @@ func getRuleHash(rule *Rules) string {
|
||||
}
|
||||
|
||||
// recordVulnerabilityResult 记录漏洞检测结果
|
||||
func recordVulnerabilityResult(targetURL string, pocDef *Poc, params StrMap, skipSave bool) {
|
||||
func recordVulnerabilityResult(targetURL string, pocDef *Poc, params StrMap, skipSave bool, session *common.ScanSession) {
|
||||
// 构造详细信息
|
||||
details := make(map[string]interface{})
|
||||
details["vulnerability_type"] = pocDef.Name
|
||||
@@ -656,7 +657,7 @@ func recordVulnerabilityResult(targetURL string, pocDef *Poc, params StrMap, ski
|
||||
Status: "vulnerable",
|
||||
Details: details,
|
||||
}
|
||||
_ = common.SaveResult(result)
|
||||
_ = session.SaveResult(result)
|
||||
}
|
||||
|
||||
// 生成日志消息
|
||||
@@ -668,7 +669,7 @@ func recordVulnerabilityResult(targetURL string, pocDef *Poc, params StrMap, ski
|
||||
}
|
||||
|
||||
// 输出成功日志
|
||||
common.LogVuln(logMsg)
|
||||
session.LogVuln(logMsg)
|
||||
}
|
||||
|
||||
// isFuzz 检查规则是否包含需要Fuzz测试的参数
|
||||
@@ -738,7 +739,7 @@ func MakeData(base [][]string, nextData []string) [][]string {
|
||||
}
|
||||
|
||||
// clustersend 执行单个规则的HTTP请求和响应检测
|
||||
func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Request, env *cel.Env, rule Rules) (bool, error) {
|
||||
func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Request, env *cel.Env, rule Rules, session *common.ScanSession) (bool, error) {
|
||||
// 替换请求中的变量
|
||||
for varName, varValue := range variableMap {
|
||||
// 跳过map类型的变量
|
||||
@@ -786,7 +787,7 @@ func clustersend(oReq *http.Request, variableMap map[string]interface{}, req *Re
|
||||
}
|
||||
|
||||
// 发送请求
|
||||
resp, err := DoRequest(newRequest, rule.FollowRedirects)
|
||||
resp, err := DoRequest(newRequest, rule.FollowRedirects, session)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("%s: %w", i18n.GetText("webscan_request_send_error"), err)
|
||||
}
|
||||
|
||||
+15
-14
@@ -48,7 +48,7 @@ var (
|
||||
)
|
||||
|
||||
// WebScan 执行Web漏洞扫描
|
||||
func WebScan(ctx context.Context, info *common.HostInfo, cfg *common.Config) {
|
||||
func WebScan(ctx context.Context, info *common.HostInfo, cfg *common.Config, session *common.ScanSession) {
|
||||
// 初始化POC配置(用于CEL回调函数)
|
||||
lib.InitPOCConfig(cfg.DNSLog)
|
||||
|
||||
@@ -65,19 +65,19 @@ func WebScan(ctx context.Context, info *common.HostInfo, cfg *common.Config) {
|
||||
|
||||
// 验证输入
|
||||
if info == nil {
|
||||
common.LogError(i18n.GetText("invalid_scan_target"))
|
||||
session.LogError(i18n.GetText("invalid_scan_target"))
|
||||
return
|
||||
}
|
||||
|
||||
if len(allPocs) == 0 {
|
||||
common.LogError(i18n.GetText("poc_load_failed"))
|
||||
session.LogError(i18n.GetText("poc_load_failed"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建目标URL
|
||||
target, err := buildTargetURL(info)
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("webscan_target_url_failed", err))
|
||||
session.LogError(i18n.Tr("webscan_target_url_failed", err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -91,13 +91,13 @@ func WebScan(ctx context.Context, info *common.HostInfo, cfg *common.Config) {
|
||||
// 根据扫描策略执行POC
|
||||
if cfg.POC.PocName == "" && len(info.Info) == 0 {
|
||||
// 执行所有POC
|
||||
executePOCs(ctx, config.PocInfo{Target: target}, cfg)
|
||||
executePOCs(ctx, config.PocInfo{Target: target}, cfg, session)
|
||||
} else if len(info.Info) > 0 {
|
||||
// 基于指纹信息执行POC
|
||||
scanByFingerprints(ctx, target, info.Info, cfg)
|
||||
scanByFingerprints(ctx, target, info.Info, cfg, session)
|
||||
} else if cfg.POC.PocName != "" {
|
||||
// 基于指定POC名称执行
|
||||
executePOCs(ctx, config.PocInfo{Target: target, PocName: cfg.POC.PocName}, cfg)
|
||||
executePOCs(ctx, config.PocInfo{Target: target, PocName: cfg.POC.PocName}, cfg, session)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ func hasProtocolPrefix(urlStr string) bool {
|
||||
}
|
||||
|
||||
// scanByFingerprints 根据指纹执行POC
|
||||
func scanByFingerprints(ctx context.Context, target string, fingerprints []string, cfg *common.Config) {
|
||||
func scanByFingerprints(ctx context.Context, target string, fingerprints []string, cfg *common.Config, session *common.ScanSession) {
|
||||
for _, fingerprint := range fingerprints {
|
||||
if fingerprint == "" {
|
||||
continue
|
||||
@@ -137,15 +137,15 @@ func scanByFingerprints(ctx context.Context, target string, fingerprints []strin
|
||||
continue
|
||||
}
|
||||
|
||||
executePOCs(ctx, config.PocInfo{Target: target, PocName: pocName}, cfg)
|
||||
executePOCs(ctx, config.PocInfo{Target: target, PocName: pocName}, cfg, session)
|
||||
}
|
||||
}
|
||||
|
||||
// executePOCs 执行POC检测
|
||||
func executePOCs(ctx context.Context, pocInfo config.PocInfo, cfg *common.Config) {
|
||||
func executePOCs(ctx context.Context, pocInfo config.PocInfo, cfg *common.Config, session *common.ScanSession) {
|
||||
// 验证目标
|
||||
if pocInfo.Target == "" {
|
||||
common.LogError(ErrEmptyTarget.Error())
|
||||
session.LogError(ErrEmptyTarget.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -157,21 +157,21 @@ func executePOCs(ctx context.Context, pocInfo config.PocInfo, cfg *common.Config
|
||||
// 验证URL
|
||||
_, err := url.Parse(pocInfo.Target)
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("webscan_invalid_url", ErrInvalidURL, pocInfo.Target, err))
|
||||
session.LogError(i18n.Tr("webscan_invalid_url", ErrInvalidURL, pocInfo.Target, err))
|
||||
return
|
||||
}
|
||||
|
||||
// 创建基础请求
|
||||
req, err := createBaseRequest(ctx, pocInfo.Target, cfg)
|
||||
if err != nil {
|
||||
common.LogError(i18n.Tr("webscan_request_create_failed", err))
|
||||
session.LogError(i18n.Tr("webscan_request_create_failed", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 筛选POC
|
||||
matchedPocs := filterPocs(pocInfo.PocName)
|
||||
if len(matchedPocs) == 0 {
|
||||
common.LogDebug(fmt.Sprintf("%v: %s", ErrPocNotFound, pocInfo.PocName))
|
||||
session.LogDebug(fmt.Sprintf("%v: %s", ErrPocNotFound, pocInfo.PocName))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -179,6 +179,7 @@ func executePOCs(ctx context.Context, pocInfo config.PocInfo, cfg *common.Config
|
||||
pocCtx := &lib.POCContext{
|
||||
DNSLog: cfg.DNSLog,
|
||||
POCFull: cfg.POC.Full,
|
||||
Session: session,
|
||||
}
|
||||
|
||||
// 执行POC检测
|
||||
|
||||
Reference in New Issue
Block a user