mirror of
https://github.com/shadow1ng/fscan.git
synced 2025-07-13 12:52:44 +08:00
fix: 修复#431
This commit is contained in:
parent
3e04e7801f
commit
87ceba4d8f
@ -27,46 +27,69 @@ type Task struct {
|
|||||||
Poc *Poc // POC检测脚本
|
Poc *Poc // POC检测脚本
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VulnResult 漏洞结果结构体
|
||||||
|
type VulnResult struct {
|
||||||
|
Poc *Poc // POC脚本
|
||||||
|
VulName string // 漏洞名称
|
||||||
|
Target string // 目标URL
|
||||||
|
Details map[string]interface{} // 详细信息
|
||||||
|
}
|
||||||
|
|
||||||
// CheckMultiPoc 并发执行多个POC检测
|
// CheckMultiPoc 并发执行多个POC检测
|
||||||
// 参数说明:
|
// 参数说明:
|
||||||
// - req: HTTP请求对象
|
// - req: HTTP请求对象
|
||||||
// - pocs: POC检测脚本列表
|
// - pocs: POC检测脚本列表
|
||||||
// - workers: 并发工作协程数量
|
// - workers: 并发工作协程数量
|
||||||
func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int) {
|
func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int) {
|
||||||
|
// 确保至少有一个工作协程
|
||||||
if workers <= 0 {
|
if workers <= 0 {
|
||||||
workers = 1 // 确保至少有一个工作协程
|
workers = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建任务通道,缓冲区大小为POC列表长度
|
||||||
tasks := make(chan Task, len(pocs))
|
tasks := make(chan Task, len(pocs))
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
// 启动工作协程池
|
// 启动指定数量的工作协程池
|
||||||
for i := 0; i < workers; i++ {
|
for i := 0; i < workers; i++ {
|
||||||
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
// 从任务通道循环获取任务
|
||||||
for task := range tasks {
|
for task := range tasks {
|
||||||
|
// 执行POC检测,返回是否存在漏洞、错误信息和漏洞名称
|
||||||
isVulnerable, err, vulName := executePoc(task.Req, task.Poc)
|
isVulnerable, err, vulName := executePoc(task.Req, task.Poc)
|
||||||
|
|
||||||
|
// 处理执行过程中的错误
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wg.Done()
|
Common.LogError(fmt.Sprintf("执行POC错误 %s: %v", task.Poc.Name, err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if isVulnerable {
|
// 仅当通过普通POC规则(非clusterpoc)检测到漏洞时,才创建结果
|
||||||
// 构造详细信息
|
// 因为clusterpoc已在内部处理了漏洞输出
|
||||||
|
if isVulnerable && vulName != "" {
|
||||||
|
// 构造漏洞详细信息
|
||||||
details := make(map[string]interface{})
|
details := make(map[string]interface{})
|
||||||
details["vulnerability_type"] = task.Poc.Name
|
details["vulnerability_type"] = task.Poc.Name
|
||||||
details["vulnerability_name"] = vulName
|
details["vulnerability_name"] = vulName
|
||||||
|
|
||||||
|
// 添加作者信息(如果有)
|
||||||
if task.Poc.Detail.Author != "" {
|
if task.Poc.Detail.Author != "" {
|
||||||
details["author"] = task.Poc.Detail.Author
|
details["author"] = task.Poc.Detail.Author
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加参考链接(如果有)
|
||||||
if len(task.Poc.Detail.Links) != 0 {
|
if len(task.Poc.Detail.Links) != 0 {
|
||||||
details["references"] = task.Poc.Detail.Links
|
details["references"] = task.Poc.Detail.Links
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加漏洞描述(如果有)
|
||||||
if task.Poc.Detail.Description != "" {
|
if task.Poc.Detail.Description != "" {
|
||||||
details["description"] = task.Poc.Detail.Description
|
details["description"] = task.Poc.Detail.Description
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存漏洞结果
|
// 创建并保存扫描结果
|
||||||
result := &Common.ScanResult{
|
result := &Common.ScanResult{
|
||||||
Time: time.Now(),
|
Time: time.Now(),
|
||||||
Type: Common.VULN,
|
Type: Common.VULN,
|
||||||
@ -76,41 +99,96 @@ func CheckMultiPoc(req *http.Request, pocs []*Poc, workers int) {
|
|||||||
}
|
}
|
||||||
Common.SaveResult(result)
|
Common.SaveResult(result)
|
||||||
|
|
||||||
// 控制台输出
|
// 构造控制台输出的日志信息
|
||||||
logMsg := fmt.Sprintf("目标: %s\n 漏洞类型: %s\n 漏洞名称: %s\n 详细信息:",
|
logMsg := fmt.Sprintf("目标: %s\n 漏洞类型: %s\n 漏洞名称: %s\n 详细信息:",
|
||||||
task.Req.URL,
|
task.Req.URL,
|
||||||
task.Poc.Name,
|
task.Poc.Name,
|
||||||
vulName)
|
vulName)
|
||||||
|
|
||||||
|
// 添加作者信息到日志
|
||||||
if task.Poc.Detail.Author != "" {
|
if task.Poc.Detail.Author != "" {
|
||||||
logMsg += "\n\tauthor:" + task.Poc.Detail.Author
|
logMsg += "\n\t作者:" + task.Poc.Detail.Author
|
||||||
}
|
|
||||||
if len(task.Poc.Detail.Links) != 0 {
|
|
||||||
logMsg += "\n\tlinks:" + strings.Join(task.Poc.Detail.Links, "\n")
|
|
||||||
}
|
|
||||||
if task.Poc.Detail.Description != "" {
|
|
||||||
logMsg += "\n\tdescription:" + task.Poc.Detail.Description
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加参考链接到日志
|
||||||
|
if len(task.Poc.Detail.Links) != 0 {
|
||||||
|
logMsg += "\n\t参考链接:" + strings.Join(task.Poc.Detail.Links, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加描述信息到日志
|
||||||
|
if task.Poc.Detail.Description != "" {
|
||||||
|
logMsg += "\n\t描述:" + task.Poc.Detail.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出成功日志
|
||||||
Common.LogSuccess(logMsg)
|
Common.LogSuccess(logMsg)
|
||||||
}
|
}
|
||||||
wg.Done()
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分发任务
|
// 分发所有POC任务到通道
|
||||||
for _, poc := range pocs {
|
for _, poc := range pocs {
|
||||||
wg.Add(1)
|
|
||||||
tasks <- Task{
|
tasks <- Task{
|
||||||
Req: req,
|
Req: req,
|
||||||
Poc: poc,
|
Poc: poc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 等待所有任务完成
|
// 关闭任务通道
|
||||||
wg.Wait()
|
|
||||||
close(tasks)
|
close(tasks)
|
||||||
|
|
||||||
|
// 等待所有POC检测任务完成
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// createVulnDetails 创建漏洞详情信息
|
||||||
|
func createVulnDetails(poc *Poc, vulName string) map[string]interface{} {
|
||||||
|
details := make(map[string]interface{})
|
||||||
|
details["vulnerability_type"] = poc.Name
|
||||||
|
details["vulnerability_name"] = vulName
|
||||||
|
|
||||||
|
// 添加作者信息(如果有)
|
||||||
|
if poc.Detail.Author != "" {
|
||||||
|
details["author"] = poc.Detail.Author
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加参考链接(如果有)
|
||||||
|
if len(poc.Detail.Links) != 0 {
|
||||||
|
details["references"] = poc.Detail.Links
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加漏洞描述(如果有)
|
||||||
|
if poc.Detail.Description != "" {
|
||||||
|
details["description"] = poc.Detail.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
return details
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildLogMessage 构建漏洞日志消息
|
||||||
|
func buildLogMessage(result *VulnResult) string {
|
||||||
|
logMsg := fmt.Sprintf("目标: %s\n 漏洞类型: %s\n 漏洞名称: %s\n 详细信息:",
|
||||||
|
result.Target,
|
||||||
|
result.Poc.Name,
|
||||||
|
result.VulName)
|
||||||
|
|
||||||
|
// 添加作者信息到日志
|
||||||
|
if result.Poc.Detail.Author != "" {
|
||||||
|
logMsg += "\n\t作者:" + result.Poc.Detail.Author
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加参考链接到日志
|
||||||
|
if len(result.Poc.Detail.Links) != 0 {
|
||||||
|
logMsg += "\n\t参考链接:" + strings.Join(result.Poc.Detail.Links, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加描述信息到日志
|
||||||
|
if result.Poc.Detail.Description != "" {
|
||||||
|
logMsg += "\n\t描述:" + result.Poc.Detail.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
return logMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
// executePoc 执行单个POC检测
|
// executePoc 执行单个POC检测
|
||||||
@ -170,8 +248,13 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) {
|
|||||||
return success, err, ""
|
return success, err, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return executeRules(oReq, p, variableMap, req, env)
|
||||||
|
}
|
||||||
|
|
||||||
|
// executeRules 执行POC规则并返回结果
|
||||||
|
func executeRules(oReq *http.Request, p *Poc, variableMap map[string]interface{}, req *Request, env *cel.Env) (bool, error, string) {
|
||||||
// 处理单个规则的函数
|
// 处理单个规则的函数
|
||||||
DealWithRule := func(rule Rules) (bool, error) {
|
executeRule := func(rule Rules) (bool, error) {
|
||||||
Headers := cloneMap(rule.Headers)
|
Headers := cloneMap(rule.Headers)
|
||||||
|
|
||||||
// 替换变量
|
// 替换变量
|
||||||
@ -251,9 +334,9 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 处理规则组的函数
|
// 处理规则组的函数
|
||||||
DealWithRules := func(rules []Rules) bool {
|
executeRuleSet := func(rules []Rules) bool {
|
||||||
for _, rule := range rules {
|
for _, rule := range rules {
|
||||||
flag, err := DealWithRule(rule)
|
flag, err := executeRule(rule)
|
||||||
if err != nil || !flag {
|
if err != nil || !flag {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -264,17 +347,18 @@ func executePoc(oReq *http.Request, p *Poc) (bool, error, string) {
|
|||||||
// 执行检测规则
|
// 执行检测规则
|
||||||
success := false
|
success := false
|
||||||
if len(p.Rules) > 0 {
|
if len(p.Rules) > 0 {
|
||||||
success = DealWithRules(p.Rules)
|
success = executeRuleSet(p.Rules)
|
||||||
|
return success, nil, ""
|
||||||
} else {
|
} else {
|
||||||
for _, item := range p.Groups {
|
for _, item := range p.Groups {
|
||||||
name, rules := item.Key, item.Value
|
name, rules := item.Key, item.Value
|
||||||
if success = DealWithRules(rules); success {
|
if success = executeRuleSet(rules); success {
|
||||||
return true, nil, name
|
return true, nil, name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return success, nil, ""
|
return false, nil, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// doSearch 在响应体中执行正则匹配并提取命名捕获组
|
// doSearch 在响应体中执行正则匹配并提取命名捕获组
|
||||||
@ -378,6 +462,61 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{},
|
|||||||
var strMap StrMap // 存储成功的参数组合
|
var strMap StrMap // 存储成功的参数组合
|
||||||
var shiroKeyCount int // shiro key测试计数
|
var shiroKeyCount int // shiro key测试计数
|
||||||
|
|
||||||
|
// 记录漏洞的辅助函数,统一保存结果和输出日志
|
||||||
|
recordVulnerability := func(targetURL string, params StrMap, skipSave bool) {
|
||||||
|
// 构造详细信息
|
||||||
|
details := make(map[string]interface{})
|
||||||
|
details["vulnerability_type"] = p.Name
|
||||||
|
details["vulnerability_name"] = p.Name // 使用POC名称作为漏洞名称
|
||||||
|
|
||||||
|
// 添加作者信息(如果有)
|
||||||
|
if p.Detail.Author != "" {
|
||||||
|
details["author"] = p.Detail.Author
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加参考链接(如果有)
|
||||||
|
if len(p.Detail.Links) != 0 {
|
||||||
|
details["references"] = p.Detail.Links
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加漏洞描述(如果有)
|
||||||
|
if p.Detail.Description != "" {
|
||||||
|
details["description"] = p.Detail.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加参数信息(如果有)
|
||||||
|
if len(params) > 0 {
|
||||||
|
paramMap := make(map[string]string)
|
||||||
|
for _, item := range params {
|
||||||
|
paramMap[item.Key] = item.Value
|
||||||
|
}
|
||||||
|
details["parameters"] = paramMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存漏洞结果(除非明确指示跳过)
|
||||||
|
if !skipSave {
|
||||||
|
result := &Common.ScanResult{
|
||||||
|
Time: time.Now(),
|
||||||
|
Type: Common.VULN,
|
||||||
|
Target: targetURL,
|
||||||
|
Status: "vulnerable",
|
||||||
|
Details: details,
|
||||||
|
}
|
||||||
|
Common.SaveResult(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成日志消息
|
||||||
|
var logMsg string
|
||||||
|
if p.Name == "poc-yaml-backup-file" || p.Name == "poc-yaml-sql-file" {
|
||||||
|
logMsg = fmt.Sprintf("检测到漏洞 %s %s", targetURL, p.Name)
|
||||||
|
} else {
|
||||||
|
logMsg = fmt.Sprintf("检测到漏洞 %s %s 参数:%v", targetURL, p.Name, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出成功日志
|
||||||
|
Common.LogSuccess(logMsg)
|
||||||
|
}
|
||||||
|
|
||||||
// 遍历POC规则
|
// 遍历POC规则
|
||||||
for ruleIndex, rule := range p.Rules {
|
for ruleIndex, rule := range p.Rules {
|
||||||
// 检查是否需要进行参数Fuzz测试
|
// 检查是否需要进行参数Fuzz测试
|
||||||
@ -497,22 +636,20 @@ func clusterpoc(oReq *http.Request, p *Poc, variableMap map[string]interface{},
|
|||||||
}
|
}
|
||||||
|
|
||||||
if success {
|
if success {
|
||||||
|
targetURL := fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path)
|
||||||
|
|
||||||
// 处理成功情况
|
// 处理成功情况
|
||||||
if currentRule.Continue {
|
if currentRule.Continue {
|
||||||
targetURL := fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path)
|
// 使用Continue标志时,记录但继续测试其他参数
|
||||||
if p.Name == "poc-yaml-backup-file" || p.Name == "poc-yaml-sql-file" {
|
recordVulnerability(targetURL, currentParams, false)
|
||||||
Common.LogSuccess(fmt.Sprintf("检测到漏洞 %s %s", targetURL, p.Name))
|
|
||||||
} else {
|
|
||||||
Common.LogSuccess(fmt.Sprintf("检测到漏洞 %s %s 参数:%v", targetURL, p.Name, currentParams))
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// 记录成功的参数组合
|
// 记录成功的参数组合
|
||||||
strMap = append(strMap, currentParams...)
|
strMap = append(strMap, currentParams...)
|
||||||
if ruleIndex == len(p.Rules)-1 {
|
if ruleIndex == len(p.Rules)-1 {
|
||||||
targetURL := fmt.Sprintf("%s://%s%s", req.Url.Scheme, req.Url.Host, req.Url.Path)
|
// 最终规则成功,记录完整的结果并返回
|
||||||
Common.LogSuccess(fmt.Sprintf("检测到漏洞 %s %s 参数:%v", targetURL, p.Name, strMap))
|
recordVulnerability(targetURL, strMap, false)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
break paramLoop
|
break paramLoop
|
||||||
@ -698,11 +835,15 @@ func cloneRules(tags Rules) Rules {
|
|||||||
FollowRedirects: tags.FollowRedirects,
|
FollowRedirects: tags.FollowRedirects,
|
||||||
Expression: tags.Expression,
|
Expression: tags.Expression,
|
||||||
Headers: cloneMap(tags.Headers),
|
Headers: cloneMap(tags.Headers),
|
||||||
|
Continue: tags.Continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cloneMap 深度复制字符串映射
|
// cloneMap 深度复制字符串映射
|
||||||
func cloneMap(tags map[string]string) map[string]string {
|
func cloneMap(tags map[string]string) map[string]string {
|
||||||
|
if tags == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
cloneTags := make(map[string]string, len(tags))
|
cloneTags := make(map[string]string, len(tags))
|
||||||
for key, value := range tags {
|
for key, value := range tags {
|
||||||
cloneTags[key] = value
|
cloneTags[key] = value
|
||||||
|
Loading…
Reference in New Issue
Block a user