@@ -3,359 +3,8 @@ package runner
33import (
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-
3598func IsWildCard (domain string ) (bool , []string ) {
3609 var ret []string
36110 for i := 0 ; i < 4 ; i ++ {
0 commit comments