域名前置技术,亦称为域名伪装,是一种用于规避审查的网络技术,其核心功能在于隐藏真实的服务端点。该技术在应用层面上运作,允许用户通过HTTPS协议连接至被屏蔽的服务,同时在表面上看似与另一个完全不同的网站进行通信。
域名前置技术的运作原理主要依赖于内容分发网络(CDN)。通过配置A记录或AAAA记录,将网站域名指向服务器的公网IP地址,从而实现用户能够通过易于记忆的域名直接访问服务器上部署的网站,而无需依赖于难以记忆且缺乏直观标识的IP地址。CDN不仅能够加速域名的访问,而且在域名访问请求过程中,并不直接解析至IP地址,这为申请CDN加速服务的虚拟私人服务器(V-PS)提供了隐藏真实IP地址的效果。
内容分发网络(CDN)的工作机制如下:当用户尝试访问某个域名时,首先会向本地DNS服务器(LDNS)发起请求。如果LDNS服务器中存在缓存,则直接返回相应的IP地址给用户;若无缓存,则LDNS会向授权DNS服务器发起请求。授权DNS服务器解析域名后返回一个别名域名,随后请求被转发至公有云DNS调度系统,该系统负责分配最佳的节点IP地址。LDNS服务器会缓存此IP地址,用户随后根据该IP地址请求所需资源。通过这种方式,节点IP地址实际上隐藏了真实的IP地址。
函数即服务提供的是计算能力。原有的计算能力,无论是容器也好,虚拟机也好都承载在一定的操作系统之上,函数即服务把计算能力进行了进一步抽象。
后端及服务(BaaS: Backend as a Service)
后端即服务,比如对象存储,数据库应用,缓存服务,我们也可以称之为Serverless,因为这些服务也能够在云上提供开通即服务,开通即使用的能力。在使用这些产品时同样不需要关注它的服务器是什么样的,它的服务器部署在哪里,而是服务开通就可以使用了,后面的运维工作都交给了云,所以不用感知它的最底层服务器。
函数即服务(FaaS: Function as a Service)是指一种计算能力的提供方式。传统的计算能力,无论是通过容器还是虚拟机实现,均建立在特定的操作系统之上。而函数即服务则进一步抽象了这种计算能力。
后端即服务(BaaS: Backend as a Service)涉及的是一系列后端功能,例如对象存储、数据库应用和缓存服务等。这类服务亦可被称为无服务器(Serverless),因为它们能够在云平台上实现即开即用的服务模式。在使用这些服务时,用户无需关心服务器的具体配置、部署位置等底层细节,仅需开通服务即可开始使用。相应的运维工作由云服务提供商负责,因此用户无需直接感知到最底层的服务器架构。
通过客户端观察可见,部署后的IP地址会周期性地自动更迭(此为云函数的固有特性)。若一次性部署大量IP地址,将导致攻击方难以辨识真实来源(即便将所有IP部署于同一虚拟机亦无妨,因为云函数的特性使得每次心跳包均被视为新的请求,从而分配新的IP地址)。
云函数隐藏的命令与控制(C2)中心与内容分发网络(CDN)相似,均存在一个共同的弱点:访问时需计费。因此,可以编写脚本消耗攻击方的云函数配额,从而使得攻击方的所有恶意程序无法上线。
相关工具可于以下网址获取:工具 https://github.com/a1phaboy/MenoyGone
通过重新发送心跳包以模拟上线,但攻击方无法执行任何命令(消磨攻击者精力时间)。
在收集证据时,应重点记录具有云函数特征的信息,如主机名、X-Api-FuncName、X-Api-AppId等(其中X-Api-AppId尤为重要)。这些信息表明对方正在利用云函数对我公司进行恶意攻击。据此,我们应请求相关机构对攻击方进行临时封禁处理。
首先,基于公司收到的恶意钓鱼邮件或者恶意程序进行初始分析,得到相关特征。
提取内容:
云函数信息:提取样本中使用的云函数名称、提供商、配置等信息。
C2通信地址:提取样本中嵌入的C2服务器地址,包括域名和IP地址。
域名:提取样本中使用的所有域名。
IP地址:提取样本中使用的所有IP地址。
URL:提取样本中访问的所有URL。
工具:
YARA规则:使用YARA规则进行样本特征匹配和提取。
静态分析工具:如IDA Pro、Ghidra,用于逆向工程和静态分析。
动态分析工具:如Cuckoo Sandbox,用于行为分析和提取动态信息。
来源:
威胁情报平台:订阅和使用威胁情报平台(如微步在线情报社区、VirusTotal、AlienVault OTX)。
开源情报(OSINT):利用开源情报工具(如Shodan、Censys)和社区(如GitHub、Reddit)。
合作伙伴共享:与其他企业或组织建立威胁情报共享机制。
蜜罐系统:部署蜜罐系统(如Cowrie、Dionaea)来捕获恶意样本。
工具:
VirusTotal:用于提交和分析恶意文件。
Cuckoo Sandbox:用于自动化恶意软件分析。
微步沙箱:用于高级恶意软件行为分析。
静态分析:
特征字符串:提取样本中的可疑字符串,如URL、IP地址、域名等。(这里主要提取流量特征)
API调用:分析样本中调用的API函数,识别潜在的恶意行为。
嵌入域名/IP:提取样本中硬编码的域名和IP地址。
动态分析:
行为监控:在沙箱环境中运行样本,记录其行为,如文件操作、注册表修改、网络通信等。
网络流量:捕获和分析样本的网络流量,识别其通信模式和目标。
云函数映射:
微步情报社区:提交样本到微步沙箱,提取与样本相关的云函数映射IP。(微步对域前置、CDN识别得特别准确)
统计IP:统计样本中所有提取到的IP地址,识别潜在的云服务提供商。
相似度对比:
相似度算法:使用VT内部的相似度对比功能,分析样本与已知恶意样本的相似性。
特征对比:对比样本的特征,如文件哈希、字符串、API调用等。
C2特征对比:
URL模式:分析样本中使用的URL模式,识别C2服务器。
通信协议:分析样本使用的通信协议和加密方式,识别C2通信特征。
行为特征:记录样本的行为特征,如定时任务、数据泄露等。
识别域前置:
DNS解析:使用DNS解析工具(如nslookup、dig)解析样本中使用的域名,识别域前置技术。
前置域名记录:记录前置域名和实际通信域名,分析其关联性。
CDN检测:
CDN节点识别:分析样本中使用的CDN服务,记录相关的CDN节点IP和域名。
流量分析:使用流量分析工具(如Wireshark、Zeek)监控样本的网络通信,识别CDN流量特征。
微步沙箱/情报社区API:
基于微步强大的样本库以及情报能力,收敛相关的恶意样本进行特征关联。
IOC匹配:
IOC提取:提取样本中的IOC(Indicator of Compromise),如域名、IP地址、URL、文件哈希等。
日志匹配:将提取的IOC与现有日志进行匹配,识别潜在威胁。
网络流量分析:
流量捕获:使用网络流量捕获工具(如Wireshark、Zeek)捕获网络流量。
异常检测:分析捕获的网络流量,识别异常流量和恶意通信。
日志分析:
日志收集:收集防火墙、IDS/IPS、Web服务器等设备的日志。
日志分析:使用日志分析工具(如ELK Stack)分析日志,识别与样本相关的恶意活动。
阻断恶意域名/IP:
防火墙规则:在防火墙上添加规则,阻断恶意域名和IP地址。
DNS黑名单:在DNS服务器上添加黑名单,阻止解析恶意域名。
隔离受感染系统:
网络隔离:将受感染的系统从网络中隔离,防止进一步传播。
物理隔离:如果必要,将受感染的系统物理断网。
数字取证:
证据收集:使用数字取证工具(如FTK Imager、EnCase)收集证据。
证据分析:分析收集到的证据,识别攻击路径和攻击者行为。
系统恢复:
备份恢复:根据备份和恢复计划,恢复受感染系统的正常运行。
补丁管理:应用最新的安全补丁,修复系统漏洞。
威胁通报:
内部通报:将分析结果通报给相关部门和团队。
外部通报:将威胁情报通报给合作伙伴和威胁情报共享社区。
安全改进:
策略更新:根据分析结果,更新安全策略和措施。
技术改进:引入新的安全技术和工具,提升整体安全水平。
实时监控:
MISP系统:使用MISP系统,进行实时监控和告警。(或商业化安全运营平台)
行为分析:使用用户和实体行为分析(UEBA)工具,监控异常行为。
定期扫描:
漏洞扫描:定期进行漏洞扫描,识别和修复系统漏洞。
渗透测试:定期进行渗透测试,评估系统的安全性。
规则更新:
检测规则:根据最新威胁情报,更新防火墙、IDS/IPS等设备的检测规则。
YARA规则:更新YARA规则,用于样本特征匹配和提取。
培训与演练:
安全培训:定期进行安全培训,提高员工的安全意识和技能。
应急演练:定期进行应急演练,提高安全团队的响应能力。
对于CDN运营商,一般流量请求的第一跳IP在一段时间内,不会有太大的变化,可以基于微步情报社区API提取IP相关的云函数信息,挖掘云函数关联的恶意样本,对其流量进行匹配,将流量近似的恶意样本进行梳理关联,做到提前预知,同步封禁。
近似流量匹配
Api
1package main 2 3 4 5 6 7import ( 8 9 10 "HunterShell/UtilsRun" 11 12 13 "bufio" 14 15 16 "context" 17 18 19 "encoding/json" 20 21 22 "fmt" 23 24 25 "github.com/adrg/strutil" 26 27 28 "github.com/adrg/strutil/metrics" 29 30 31 "golang.org/x/time/rate" 32 33 34 "io/ioutil" 35 36 37 "log" 38 39 40 "os" 41 42 43 "path/filepath" 44 45 46 "sync" 47 48 49 "time" 50 51 52) 53 54 55 56 57 58func main() { 59 60 61 if len(os.Args) < 2 { 62 63 64 fmt.Println("Usage: go run main.go") 65 66 67 return 68 69 70 } 71 72 73 74 75 76 ipFilePath := os.Args[1] 77 78 79 80 81 82 // 读取IP.txt文件中的IP地址 83 84 85 log.Println("Reading IP addresses from", ipFilePath) 86 87 88 ips, err := readLines(ipFilePath) 89 90 91 if err != nil { 92 93 94 log.Fatalf("Error reading IP.txt file: %v", err) 95 96 97 } 98 99 100 101 102 103 // 将所有域名按行存储到domain.txt文件中 104 105 106 log.Println("Creating domain.txt file") 107 108 109 domainFile, err := os.Create("domain.txt") 110 111 112 if err != nil { 113 114 115 log.Fatalf("Error creating domain.txt file: %v", err) 116 117 118 } 119 120 121 defer domainFile.Close() 122 123 124 125 126 127 var wg sync.WaitGroup 128 129 130 domainChan := make(chan string, len(ips)) 131 132 133 134 135 136 // 速率限制器,每秒最多10个请求 137 138 139 limiter := rate.NewLimiter(rate.Every(50*time.Millisecond), 1) 140 141 142 143 144 145 // 用于存储已经处理过的IP地址 146 147 148 processedIPs := make(map[string]bool) 149 150 151 152 153 154 for _, ip := range ips { 155 156 157 if processedIPs[ip] { 158 159 160 continue 161 162 163 } 164 165 166 processedIPs[ip] = true 167 168 169 170 171 172 wg.Add(1) 173 174 175 go func(ip string) { 176 177 178 defer wg.Done() 179 180 181 if err := limiter.Wait(context.Background()); err != nil { 182 183 184 log.Printf("Rate limit wait error: %v", err) 185 186 187 return 188 189 190 } 191 192 193 log.Printf("Querying IP %s for domains", ip) 194 195 196 domains, err := UtilsRun.QueryIPForDomains(ip) 197 198 199 if err != nil { 200 201 202 log.Printf("Error querying IP %s: %v", ip, err) 203 204 205 return 206 207 208 } 209 210 211 for _, domain := range domains { 212 213 214 domainChan <- domain 215 216 217 } 218 219 220 }(ip) 221 222 223 } 224 225 226 227 228 229 go func() { 230 231 232 wg.Wait() 233 234 235 close(domainChan) 236 237 238 }() 239 240 241 242 243 244 for domain := range domainChan { 245 246 247 _, err := domainFile.WriteString(domain + "\n") 248 249 250 if err != nil { 251 252 253 log.Fatalf("Error writing to domain.txt file: %v", err) 254 255 256 } 257 258 259 } 260 261 262 263 264 265 // 读取domain.txt文件中的域名 266 267 268 log.Println("Reading domains from domain.txt") 269 270 271 domains, err := readLines("domain.txt") 272 273 274 if err != nil { 275 276 277 log.Fatalf("Error reading domain.txt file: %v", err) 278 279 280 } 281 282 283 284 285 286 // 将所有样本数据按行存储到result.txt文件中 287 288 289 log.Println("Creating result.txt file") 290 291 292 resultFile, err := os.Create("result.txt") 293 294 295 if err != nil { 296 297 298 log.Fatalf("Error creating result.txt file: %v", err) 299 300 301 } 302 303 304 defer resultFile.Close() 305 306 307 308 309 310 sampleChan := make(chan UtilsRun.Sample, len(domains)) 311 312 313 314 315 316 // 用于存储已经处理过的域名 317 318 319 processedDomains := make(map[string]bool) 320 321 322 323 324 325 for _, domain := range domains { 326 327 328 if processedDomains[domain] { 329 330 331 continue 332 333 334 } 335 336 337 processedDomains[domain] = true 338 339 340 341 342 343 wg.Add(1) 344 345 346 go func(domain string) { 347 348 349 defer wg.Done() 350 351 352 if err := limiter.Wait(context.Background()); err != nil { 353 354 355 log.Printf("Rate limit wait error: %v", err) 356 357 358 return 359 360 361 } 362 363 364 //log.Printf("Querying domain %s for samples", domain) 365 366 367 samples, err := UtilsRun.QueryDomainForSamples(domain) 368 369 370 if err != nil { 371 372 373 log.Printf("Error querying domain %s: %v", domain, err) 374 375 376 return 377 378 379 } 380 381 382 for _, sample := range samples { 383 384 385 sampleChan <- sample 386 387 388 } 389 390 391 }(domain) 392 393 394 } 395 396 397 398 399 400 go func() { 401 402 403 wg.Wait() 404 405 406 close(sampleChan) 407 408 409 }() 410 411 412 413 414 415 for sample := range sampleChan { 416 417 418 _, err := resultFile.WriteString(fmt.Sprintf(" SHA256: %s\n", sample.SHA256)) 419 420 421 if err != nil { 422 423 424 log.Fatalf("Error writing to result.txt file: %v", err) 425 426 427 } 428 429 430 431 432 433 // 获取样本详细内容并保存为JSON文件 434 435 436 if err := limiter.Wait(context.Background()); err != nil { 437 438 439 log.Printf("Rate limit wait error: %v", err) 440 441 442 continue 443 444 445 } 446 447 448 log.Printf("Querying sample details for %s", sample.SHA256) 449 450 451 err = UtilsRun.QuerySampleDetails(sample.SHA256) 452 453 454 if err != nil { 455 456 457 log.Printf("Error querying sample details for %s: %v", sample.SHA256, err) 458 459 460 } 461 462 463 } 464 465 466 467 468 469 fmt.Println("Samples have been written to result.txt") 470 471 472 //样本流量对比 473 474 475 476 477 478 // 保存为JSON文件到指定目录 479 480 481 dirPath := "reportjson" 482 483 484 485 486 487 // 检查目录是否存在,如果不存在则创建 488 489 490 log.Println("Checking reportjson directory") 491 492 493 if _, err := os.Stat(dirPath); os.IsNotExist(err) { 494 495 496 err := os.Mkdir(dirPath, 0755) 497 498 499 if err != nil { 500 501 502 log.Fatalf("Error creating directory: %v", err) 503 504 505 } 506 507 508 } 509 510 511 512 513 514 requestMap := make(map[string]string) 515 516 517 groupMap := make(map[string]string) 518 519 520 matchInfoMap := make(map[string]string) 521 522 523 524 525 526 // 遍历目录中的所有文件 527 528 529 log.Println("Walking reportjson directory") 530 531 532 err = filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { 533 534 535 if err != nil { 536 537 538 fmt.Println("Error accessing path:", path, err) 539 540 541 return err 542 543 544 } 545 546 547 548 549 550 if !info.IsDir() && filepath.Ext(path) == ".json" { 551 552 553 // 读取 JSON 文件 554 555 556 jsonFile, err := os.Open(path) 557 558 559 if err != nil { 560 561 562 fmt.Println("Error opening file:", path, err) 563 564 565 return err 566 567 568 } 569 570 571 defer jsonFile.Close() 572 573 574 575 576 577 byteValue, err := ioutil.ReadAll(jsonFile) 578 579 580 if err != nil { 581 582 583 fmt.Println("Error reading file:", path, err) 584 585 586 return err 587 588 589 } 590 591 592 593 594 595 var jsonData struct { 596 597 598 Data struct { 599 600 601 Network struct { 602 603 604 HTTPSEx []UtilsRun.HTTPSEx `json:"https_ex"` 605 606 607 } `json:"network"` 608 609 610 } `json:"data"` 611 612 613 } 614 615 616 617 618 619 err = json.Unmarshal(byteValue, &jsonData) 620 621 622 if err != nil { 623 624 625 fmt.Println("Error unmarshalling JSON:", path, err) 626 627 628 return err 629 630 631 } 632 633 634 635 636 637 // 提取 request 信息 638 639 640 for _, httpsEx := range jsonData.Data.Network.HTTPSEx { 641 642 643 requestMap[path] = httpsEx.Request 644 645 646 break 647 648 649 } 650 651 652 } 653 654 655 656 657 658 return nil 659 660 661 }) 662 663 664 665 666 667 if err != nil { 668 669 670 fmt.Println("Error walking the path:", dirPath, err) 671 672 673 } 674 675 676 677 678 679 // 文本相似性归类 680 681 682 log.Println("Performing text similarity classification") 683 684 685 for file1, request1 := range requestMap { 686 687 688 groupMap[file1] = file1 689 690 691 matchInfoMap[file1] = "" 692 693 694 for file2, request2 := range requestMap { 695 696 697 698 699 700 if file1 != file2 { 701 702 703 similarity := strutil.Similarity(request1, request2, metrics.NewJaccard()) 704 705 706 //fmt.Printf(request1, request2) 707 708 709 if similarity > 0.8 { // 设置相似性阈值 710 711 712 groupMap[file2] = groupMap[file1] 713 714 715 matchInfoMap[file2] = fmt.Sprintf("Matched with %s (similarity: %.2f), Key Data: %s", file1, similarity, UtilsRun.GetMatchingKeyData(request1, request2)) 716 717 718 } 719 720 721 } 722 723 724 } 725 726 727 } 728 729 730 731 732 733 // 写入分组信息到 spliet.txt 734 735 736 log.Println("Creating spliet.txt file") 737 738 739 groupFile, err := os.Create("spliet.txt") 740 741 742 if err != nil { 743 744 745 fmt.Println("Error creating file:", err) 746 747 748 return 749 750 751 } 752 753 754 defer groupFile.Close() 755 756 757 758 759 760 groupSet := make(map[string]bool) 761 762 763 for file, group := range groupMap { 764 765 766 if !groupSet[group] { 767 768 769 groupSet[group] = true 770 771 772 groupFile.WriteString(fmt.Sprintf("Group: %s\n", group)) 773 774 775 } 776 777 778 groupFile.WriteString(fmt.Sprintf(" %s\n", file)) 779 780 781 if matchInfo := matchInfoMap[file]; matchInfo != "" { 782 783 784 groupFile.WriteString(fmt.Sprintf(" %s\n", matchInfo)) 785 786 787 } 788 789 790 } 791 792 793 // 输出文件输出成功及文件路径 794 795 796 outputPath := groupFile.Name() 797 798 799 fmt.Printf("文件输出成功,输出路径: %s\n", outputPath) 800 801 802 803 804 805} 806 807 808 809 810 811// 读取文件内容并按行分割 812 813 814func readLines(filename string) ([]string, error) { 815 816 817 file, err := os.Open(filename) 818 819 820 if err != nil { 821 822 823 return nil, err 824 825 826 } 827 828 829 defer file.Close() 830 831 832 833 834 835 var lines []string 836 837 838 scanner := bufio.NewScanner(file) 839 840 841 for scanner.Scan() { 842 843 844 lines = append(lines, scanner.Text()) 845 846 847 } 848 849 850 return lines, scanner.Err() 851 852 853}
UtilsRun:
1package UtilsRun
2
3
4
5
6
7import (
8
9
10 "encoding/json"
11
12
13 "fmt"
14
15
16 "io/ioutil"
17
18
19 "log"
20
21
22 "net/http"
23
24
25 "os"
26
27
28 "strings"
29
30
31 "time"
32
33
34)
35
36
37
38
39
40// HTTPRequest 结构体表示一个HTTP请求
41
42
43type HTTPRequest struct {
44
45
46 Method string // 请求方法
47
48
49 Path string // 请求路径
50
51
52 Version string // HTTP协议版本
53
54
55 Headers map[string]string // 请求头
56
57
58 Body string // 请求体
59
60
61}
62
63
64
65
66
67const (
68
69
70 maxRetries = 3
71
72
73 retryDelay = 2 * time.Second
74
75
76)
77
78
79
80
81
82// 查询IP地址获取域名
83
84
85func QueryIPForDomains(ip string) ([]string, error) {
86
87
88 url := fmt.Sprintf("https://api.threatbook.cn/v3/ip/adv_query?apikey=xxx&resource=%s&exclude=cur_domains", ip)
89
90
91
92
93
94 var domains []string
95
96
97 var err error
98
99
100 for i := 0; i < maxRetries; i++ {
101
102
103 domains, err = queryIPForDomainsWithRetry(url)
104
105
106 if err == nil {
107
108
109 return domains, nil
110
111
112 }
113
114
115 time.Sleep(retryDelay)
116
117
118 }
119
120
121 return nil, err
122
123
124}
125
126
127
128
129
130func queryIPForDomainsWithRetry(url string) ([]string, error) {
131
132
133 client := &http.Client{Timeout: 300 * time.Second}
134
135
136 req, err := http.NewRequest("GET", url, nil)
137
138
139 if err != nil {
140
141
142 return nil, err
143
144
145 }
146
147
148
149
150
151 res, err := client.Do(req)
152
153
154 if err != nil {
155
156
157 return nil, err
158
159
160 }
161
162
163 defer res.Body.Close()
164
165
166
167
168
169 body, err := ioutil.ReadAll(res.Body)
170
171
172 if err != nil {
173
174
175 return nil, err
176
177
178 }
179
180
181
182
183
184 var response Response
185
186
187 err = json.Unmarshal(body, &response)
188
189
190 if err != nil {
191
192
193 return nil, err
194
195
196 }
197
198
199
200
201
202 var domains []string
203
204
205 for _, d := range response.Data.HistoryDomains {
206
207
208 domains = append(domains, d...)
209
210
211 }
212
213
214
215
216
217 return domains, nil
218
219
220}
221
222
223
224
225
226// 查询域名获取样本数据
227
228
229func QueryDomainForSamples(domain string) ([]Sample, error) {
230
231
232 url := fmt.Sprintf("https://api.threatbook.cn/v3/domain/query?apikey=xxxx&resource=%s", domain)
233
234
235
236
237
238 var samples []Sample
239
240
241 var err error
242
243
244 for i := 0; i < maxRetries; i++ {
245
246
247 samples, err = queryDomainForSamplesWithRetry(url, domain)
248
249
250 if err == nil {
251
252
253 return samples, nil
254
255
256 }
257
258
259 time.Sleep(retryDelay)
260
261
262 }
263
264
265 return nil, err
266
267
268}
269
270
271
272
273
274func queryDomainForSamplesWithRetry(url, domain string) ([]Sample, error) {
275
276
277 client := &http.Client{Timeout: 300 * time.Second}
278
279
280 req, err := http.NewRequest("GET", url, nil)
281
282
283 if err != nil {
284
285
286 return nil, err
287
288
289 }
290
291
292
293
294
295 res, err := client.Do(req)
296
297
298 if err != nil {
299
300
301 return nil, err
302
303
304 }
305
306
307 defer res.Body.Close()
308
309
310
311
312
313 body, err := ioutil.ReadAll(res.Body)
314
315
316 if err != nil {
317
318
319 return nil, err
320
321
322 }
323
324
325
326
327
328 var response Response2
329
330
331 err = json.Unmarshal(body, &response)
332
333
334 if err != nil {
335
336
337 return nil, err
338
339
340 }
341
342
343
344
345
346 if data, ok := response.Data[domain]; ok {
347
348
349 return data.Samples, nil
350
351
352 }
353
354
355
356
357
358 return nil, nil
359
360
361}
362
363
364
365
366
367// 查询样本详细内容并保存为JSON文件
368
369
370func QuerySampleDetails(sha256 string) error {
371
372
373 url := fmt.Sprintf("https://api.threatbook.cn/v3/file/report?apikey=xxxxx&resource=%s&query_fields=network", sha256)
374
375
376
377
378
379 var err error
380
381
382 for i := 0; i < maxRetries; i++ {
383
384
385 err = querySampleDetailsWithRetry(url, sha256)
386
387
388 if err == nil {
389
390
391 return nil
392
393
394 }
395
396
397 time.Sleep(retryDelay)
398
399
400 }
401
402
403 return err
404
405
406}
407
408
409
410
411
412func querySampleDetailsWithRetry(url, sha256 string) error {
413
414
415 client := &http.Client{Timeout: 300 * time.Second}
416
417
418 req, err := http.NewRequest("GET", url, nil)
419
420
421 if err != nil {
422
423
424 return err
425
426
427 }
428
429
430
431
432
433 resp, err := client.Do(req)
434
435
436 if err != nil {
437
438
439 return err
440
441
442 }
443
444
445 defer resp.Body.Close()
446
447
448
449
450
451 respBody, err := ioutil.ReadAll(resp.Body)
452
453
454 if err != nil {
455
456
457 return err
458
459
460 }
461
462
463
464
465
466 // 保存为JSON文件到指定目录
467
468
469 directory := "reportjson"
470
471
472
473
474
475 // 检查目录是否存在,如果不存在则创建
476
477
478 if _, err := os.Stat(directory); os.IsNotExist(err) {
479
480
481 err := os.Mkdir(directory, 0755)
482
483
484 if err != nil {
485
486
487 log.Fatalf("Error creating directory: %v", err)
488
489
490 }
491
492
493 }
494
495
496
497
498
499 filename := fmt.Sprintf("%s/%s.json", directory, sha256)
500
501
502
503
504
505 // 确保目录存在
506
507
508 err = os.MkdirAll(directory, 0755)
509
510
511 if err != nil {
512
513
514 return err
515
516
517 }
518
519
520
521
522
523 err = ioutil.WriteFile(filename, respBody, 0644)
524
525
526 if err != nil {
527
528
529 return err
530
531
532 }
533
534
535
536
537
538 return nil
539
540
541}
542
543
544
545
546
547// 获取匹配的关键数据
548
549
550func GetMatchingKeyData(request1, request2 string) string {
551
552
553 // 这里可以根据实际需求实现具体的匹配关键数据提取逻辑
554
555
556 // 例如,提取请求方法、URL、Host等
557
558
559 method1 := extractMethod(request1)
560
561
562 url1 := extractURL(request1)
563
564
565 host1 := extractHost(request1)
566
567
568
569
570
571 return fmt.Sprintf("Method: %s, URL: %s, Host: %s", method1, url1, host1)
572
573
574}
575
576
577
578
579
580// 提取请求方法
581
582
583func extractMethod(request string) string {
584
585
586 lines := strings.Split(request, "\n")
587
588
589 if len(lines) > 0 {
590
591
592 parts := strings.SplitN(lines[0], " ", 3)
593
594
595 if len(parts) > 0 {
596
597
598 return parts[0]
599
600
601 }
602
603
604 }
605
606
607 return ""
608
609
610}
611
612
613
614
615
616// 提取URL
617
618
619func extractURL(request string) string {
620
621
622 lines := strings.Split(request, "\n")
623
624
625 if len(lines) > 0 {
626
627
628 parts := strings.SplitN(lines[0], " ", 3)
629
630
631 if len(parts) > 1 {
632
633
634 return parts[1]
635
636
637 }
638
639
640 }
641
642
643 return ""
644
645
646}
647
648
649
650
651
652// 提取Host
653
654
655func extractHost(request string) string {
656
657
658 lines := strings.Split(request, "\n")
659
660
661 for _, line := range lines {
662
663
664 if strings.HasPrefix(line, "Host:") {
665
666
667 return strings.TrimSpace(strings.TrimPrefix(line, "Host:"))
668
669
670 }
671
672
673 }
674
675
676 return ""
677
678
679}
这不请我咖啡啊我丢