Skip to content

Commit eb9a666

Browse files
committed
wild update
1 parent 5bb56b5 commit eb9a666

3 files changed

Lines changed: 343 additions & 360 deletions

File tree

pkg/runner/wildcard.go

Lines changed: 0 additions & 351 deletions
Original file line numberDiff line numberDiff line change
@@ -3,359 +3,8 @@ package runner
33
import (
44
"github.com/boy-hack/ksubdomain/pkg/core"
55
"net"
6-
"sort"
7-
"strings"
8-
9-
"github.com/boy-hack/ksubdomain/pkg/core/gologger"
10-
"github.com/boy-hack/ksubdomain/pkg/runner/result"
116
)
127

13-
type Pair struct {
14-
Key string
15-
Value int
16-
}
17-
type PairList []Pair
18-
19-
func (p PairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
20-
func (p PairList) Len() int { return len(p) }
21-
func (p PairList) Less(i, j int) bool { return p[i].Value > p[j].Value }
22-
23-
// A function to turn a map into a PairList, then sort and return it.
24-
func sortMapByValue(m map[string]int) PairList {
25-
p := make(PairList, len(m))
26-
i := 0
27-
for k, v := range m {
28-
p[i] = Pair{k, v}
29-
i++
30-
}
31-
sort.Sort(p)
32-
return p
33-
}
34-
35-
// FilterWildCard 基于Result类型数据过滤泛解析
36-
// 传入参数为[]result.Result,返回过滤后的[]result.Result
37-
// 通过分析整体结果,对解析记录中相同的ip进行阈值判断,超过则丢弃该结果
38-
func FilterWildCard(results []result.Result) []result.Result {
39-
if len(results) == 0 {
40-
return results
41-
}
42-
43-
gologger.Debugf("泛解析处理中,共 %d 条记录...\n", len(results))
44-
45-
// 统计每个IP出现的次数
46-
ipFrequency := make(map[string]int)
47-
// 记录IP到域名的映射关系
48-
ipToDomains := make(map[string][]string)
49-
// 域名计数
50-
totalDomains := len(results)
51-
52-
// 第一遍扫描,统计IP频率
53-
for _, res := range results {
54-
for _, answer := range res.Answers {
55-
// 跳过非IP的记录(CNAME等)
56-
if !strings.HasPrefix(answer, "CNAME ") && !strings.HasPrefix(answer, "NS ") &&
57-
!strings.HasPrefix(answer, "TXT ") && !strings.HasPrefix(answer, "PTR ") {
58-
ipFrequency[answer]++
59-
ipToDomains[answer] = append(ipToDomains[answer], res.Subdomain)
60-
}
61-
}
62-
}
63-
64-
// 按出现频率排序IP
65-
sortedIPs := sortMapByValue(ipFrequency)
66-
67-
// 确定疑似泛解析的IP列表
68-
// 使用两个标准:
69-
// 1. IP解析超过总域名数量的特定百分比(动态阈值)
70-
// 2. 该IP解析的子域名数量超过特定阈值
71-
suspiciousIPs := make(map[string]bool)
72-
73-
for _, pair := range sortedIPs {
74-
ip := pair.Key
75-
count := pair.Value
76-
77-
// 计算该IP解析占总体的百分比
78-
percentage := float64(count) / float64(totalDomains) * 100
79-
80-
// 动态阈值:根据总域名数量调整
81-
// 域名数量少时阈值较高,域名数量多时阈值较低
82-
var threshold float64
83-
if totalDomains < 100 {
84-
threshold = 30 // 如果域名总数小于100,阈值设为30%
85-
} else if totalDomains < 1000 {
86-
threshold = 20 // 如果域名总数在100-1000,阈值设为20%
87-
} else {
88-
threshold = 10 // 如果域名总数超过1000,阈值设为10%
89-
}
90-
91-
// 绝对数量阈值
92-
absoluteThreshold := 70
93-
94-
// 如果超过阈值,标记为可疑IP
95-
if percentage > threshold || count > absoluteThreshold {
96-
gologger.Debugf("发现可疑泛解析IP: %s (解析了 %d 个域名, %.2f%%)\n",
97-
ip, count, percentage)
98-
suspiciousIPs[ip] = true
99-
}
100-
}
101-
102-
// 第二遍扫描,过滤结果
103-
var filteredResults []result.Result
104-
105-
for _, res := range results {
106-
// 检查该域名的所有IP是否均为可疑IP
107-
// 如果有不可疑的IP,保留该记录
108-
validRecord := false
109-
var filteredAnswers []string
110-
111-
for _, answer := range res.Answers {
112-
// 保留所有非IP记录(如CNAME)
113-
if strings.HasPrefix(answer, "CNAME ") || strings.HasPrefix(answer, "NS ") ||
114-
strings.HasPrefix(answer, "TXT ") || strings.HasPrefix(answer, "PTR ") {
115-
validRecord = true
116-
filteredAnswers = append(filteredAnswers, answer)
117-
} else if !suspiciousIPs[answer] {
118-
// 保留不在可疑IP列表中的IP
119-
validRecord = true
120-
filteredAnswers = append(filteredAnswers, answer)
121-
}
122-
}
123-
124-
if validRecord && len(filteredAnswers) > 0 {
125-
filteredRes := result.Result{
126-
Subdomain: res.Subdomain,
127-
Answers: filteredAnswers,
128-
}
129-
filteredResults = append(filteredResults, filteredRes)
130-
}
131-
}
132-
133-
gologger.Infof("泛解析过滤完成,从 %d 条记录中过滤出 %d 条有效记录\n",
134-
totalDomains, len(filteredResults))
135-
136-
return filteredResults
137-
}
138-
139-
// FilterWildCardAdvanced 提供更高级的泛解析检测算法
140-
// 使用多种启发式方法和特征检测来识别泛解析
141-
func FilterWildCardAdvanced(results []result.Result) []result.Result {
142-
if len(results) == 0 {
143-
return results
144-
}
145-
146-
gologger.Debugf("高级泛解析检测开始,共 %d 条记录...\n", len(results))
147-
148-
// 统计IP出现频率
149-
ipFrequency := make(map[string]int)
150-
// 统计每个IP解析的不同子域名前缀数量
151-
ipPrefixVariety := make(map[string]map[string]bool)
152-
// 统计IP解析的不同顶级域数量
153-
ipTLDVariety := make(map[string]map[string]bool)
154-
// 记录IP到域名的映射
155-
ipToDomains := make(map[string][]string)
156-
// 记录CNAME信息
157-
cnameRecords := make(map[string][]string)
158-
159-
totalDomains := len(results)
160-
161-
// 第一轮:收集统计信息
162-
for _, res := range results {
163-
subdomain := res.Subdomain
164-
parts := strings.Split(subdomain, ".")
165-
166-
// 提取顶级域和前缀
167-
prefix := ""
168-
tld := ""
169-
if len(parts) > 1 {
170-
prefix = parts[0]
171-
tld = strings.Join(parts[1:], ".")
172-
} else {
173-
prefix = subdomain
174-
tld = subdomain
175-
}
176-
177-
for _, answer := range res.Answers {
178-
if strings.HasPrefix(answer, "CNAME ") {
179-
// 提取CNAME目标
180-
cnameParts := strings.SplitN(answer, " ", 2)
181-
if len(cnameParts) == 2 {
182-
cnameTarget := cnameParts[1]
183-
cnameRecords[subdomain] = append(cnameRecords[subdomain], cnameTarget)
184-
}
185-
continue
186-
}
187-
188-
// 只处理IP记录
189-
if !strings.HasPrefix(answer, "NS ") &&
190-
!strings.HasPrefix(answer, "TXT ") &&
191-
!strings.HasPrefix(answer, "PTR ") {
192-
// 计数IP频率
193-
ipFrequency[answer]++
194-
195-
// 初始化IP的前缀集合和TLD集合
196-
if ipPrefixVariety[answer] == nil {
197-
ipPrefixVariety[answer] = make(map[string]bool)
198-
}
199-
if ipTLDVariety[answer] == nil {
200-
ipTLDVariety[answer] = make(map[string]bool)
201-
}
202-
203-
// 记录这个IP解析了哪些不同的前缀和TLD
204-
ipPrefixVariety[answer][prefix] = true
205-
ipTLDVariety[answer][tld] = true
206-
207-
// 记录IP到域名的映射
208-
ipToDomains[answer] = append(ipToDomains[answer], subdomain)
209-
}
210-
}
211-
}
212-
213-
// 按照IP频率排序
214-
sortedIPs := sortMapByValue(ipFrequency)
215-
216-
// 识别可疑IP列表
217-
suspiciousIPs := make(map[string]float64) // IP -> 可疑度分数(0-100)
218-
219-
for _, pair := range sortedIPs {
220-
ip := pair.Key
221-
count := pair.Value
222-
223-
// 初始可疑度分数
224-
suspiciousScore := 0.0
225-
226-
// 因子1: IP频率百分比
227-
freqPercentage := float64(count) / float64(totalDomains) * 100
228-
229-
// 因子2: 前缀多样性
230-
prefixVariety := len(ipPrefixVariety[ip])
231-
prefixVarietyRatio := float64(prefixVariety) / float64(count) * 100
232-
233-
// 因子3: TLD多样性
234-
tldVariety := len(ipTLDVariety[ip])
235-
236-
// 计算可疑度分数
237-
// 1. 频率因子
238-
if freqPercentage > 30 {
239-
suspiciousScore += 40
240-
} else if freqPercentage > 10 {
241-
suspiciousScore += 20
242-
} else if freqPercentage > 5 {
243-
suspiciousScore += 10
244-
}
245-
246-
// 2. 前缀多样性因子
247-
// 如果一个IP解析了大量不同前缀的域名,可能是CDN或者泛解析
248-
if prefixVarietyRatio > 90 && prefixVariety > 10 {
249-
suspiciousScore += 30
250-
} else if prefixVarietyRatio > 70 && prefixVariety > 5 {
251-
suspiciousScore += 20
252-
}
253-
254-
// 3. 绝对数量因子
255-
if count > 100 {
256-
suspiciousScore += 20
257-
} else if count > 50 {
258-
suspiciousScore += 10
259-
} else if count > 20 {
260-
suspiciousScore += 5
261-
}
262-
263-
// 4. TLD多样性因子 - 如果一个IP解析了多个不同TLD,更可能是合法的
264-
if tldVariety > 3 {
265-
suspiciousScore -= 20
266-
} else if tldVariety > 1 {
267-
suspiciousScore -= 10
268-
}
269-
270-
// 只有当可疑度分数超过阈值时,才标记为可疑IP
271-
if suspiciousScore >= 35 {
272-
gologger.Debugf("可疑IP: %s (解析域名数: %d, 占比: %.2f%%, 前缀多样性: %d/%d, 可疑度: %.2f)\n",
273-
ip, count, freqPercentage, prefixVariety, count, suspiciousScore)
274-
suspiciousIPs[ip] = suspiciousScore
275-
}
276-
}
277-
278-
// 第二轮:过滤结果
279-
var filteredResults []result.Result
280-
281-
// CNAME聚类分析:检测指向相同目标的多个CNAME记录
282-
cnameTargetCount := make(map[string]int)
283-
for _, targets := range cnameRecords {
284-
for _, target := range targets {
285-
cnameTargetCount[target]++
286-
}
287-
}
288-
289-
// 识别可疑CNAME目标
290-
suspiciousCnames := make(map[string]bool)
291-
for cname, count := range cnameTargetCount {
292-
if count > 5 && float64(count)/float64(totalDomains)*100 > 10 {
293-
gologger.Debugf("可疑CNAME目标: %s (指向次数: %d)\n", cname, count)
294-
suspiciousCnames[cname] = true
295-
}
296-
}
297-
298-
// 过滤结果
299-
for _, res := range results {
300-
// 检查是否含有可疑CNAME
301-
hasSuspiciousCname := false
302-
if targets, ok := cnameRecords[res.Subdomain]; ok {
303-
for _, target := range targets {
304-
if suspiciousCnames[target] {
305-
hasSuspiciousCname = true
306-
break
307-
}
308-
}
309-
}
310-
311-
validRecord := !hasSuspiciousCname
312-
var filteredAnswers []string
313-
314-
// 处理所有回答
315-
for _, answer := range res.Answers {
316-
isIP := !strings.HasPrefix(answer, "CNAME ") &&
317-
!strings.HasPrefix(answer, "NS ") &&
318-
!strings.HasPrefix(answer, "TXT ") &&
319-
!strings.HasPrefix(answer, "PTR ")
320-
321-
// 保留所有非IP记录但排除可疑CNAME
322-
if !isIP {
323-
if strings.HasPrefix(answer, "CNAME ") {
324-
cnameParts := strings.SplitN(answer, " ", 2)
325-
if len(cnameParts) == 2 && suspiciousCnames[cnameParts[1]] {
326-
continue // 跳过可疑CNAME
327-
}
328-
}
329-
validRecord = true
330-
filteredAnswers = append(filteredAnswers, answer)
331-
} else {
332-
// 针对IP记录,根据可疑度评分过滤
333-
suspiciousScore, isSuspicious := suspiciousIPs[answer]
334-
335-
// 如果不在可疑IP列表中,或者可疑度较低,则保留
336-
if !isSuspicious || suspiciousScore < 50 {
337-
validRecord = true
338-
filteredAnswers = append(filteredAnswers, answer)
339-
}
340-
}
341-
}
342-
343-
// 只添加有效记录
344-
if validRecord && len(filteredAnswers) > 0 {
345-
filteredRes := result.Result{
346-
Subdomain: res.Subdomain,
347-
Answers: filteredAnswers,
348-
}
349-
filteredResults = append(filteredResults, filteredRes)
350-
}
351-
}
352-
353-
gologger.Infof("高级泛解析过滤完成,从 %d 条记录中过滤出 %d 条有效记录\n",
354-
totalDomains, len(filteredResults))
355-
356-
return filteredResults
357-
}
358-
3598
func IsWildCard(domain string) (bool, []string) {
3609
var ret []string
36110
for i := 0; i < 4; i++ {

0 commit comments

Comments
 (0)