前言

免责声明

文章仅用作网络安全人员对自己网站、服务器等进行自查检测,不可用于其他用途,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作

本次测试只作为学习用处,请勿未授权进行渗透测试,切勿用于其它用途!

  • 最新代码请在GitHub查看https://github.com/birdy02-com/NsSubDomainBurst

作用

子域名爆破是一种技术,用于枚举潜在存在的子域名以寻找可被攻击的目标。它可以帮助发现一个组织的所有已知和未知资产,并且可以通过在Internet上公开可用的数据源中搜索与给定父域名相关的信息来提高网络安全状况。

避免

  1. 避免泛解析

  2. 避免使用单一的dns服务

  3. 并发扫描

前提

  1. 不保证该思路为最合适的(网上还有很多类似的工具)

  2. 不保证输出的美观(本身功能集成在自己的平台中,没有做终端输出的计划)

  3. 请抱着学习和汲取经验的态度阅读

可能存在的弊端

  • 在本工具中,如果目标域名存在泛解析且dns将所有的域名都解析到一个ip上,则同一IP下只会保留一个域名

编写主要代码

实现一个dns请求方法

先设计一个方法返回结构

type DnsCheckDomainData struct {
	Domain    string `json:"domain"`
	Ip        string `json:"ip"`
	DnsServer string `json:"dnsServer"`
	Timer     string `json:"timer"`
}
  • 结构中返回了:

    • Domain:查询的域名

    • Ip:解析的IP

    • DnsServer:负责查询的Dns Server IP

    • Timer:查询结果的时间

实现一个查询方法

多DNS Server
dnsServers := []string{"114.114.114.114", "8.8.8.8", "8.8.4.4", "114.114.115.115", "114.114.114.119", "114.114.115.119", "114.114.114.110", "114.114.115.110", "223.5.5.5", "223.6.6.6", "180.76.76.76", "119.29.29.29", "119.28.28.28", "182.254.116.116", "182.254.118.118", "1.2.4.8", "52.80.66.66", "117.50.10.10", "52.80.52.52", "117.50.60.30", "52.80.60.30", "210.2.4.8", "112.124.47.27", "114.215.126.16", "123.125.81.6", "117.50.11.11", "101.226.4.6", "218.30.118.6"}
server := dnsServers[rand.Intn(len(dnsServers))]
  1. 先声明一个字符串列表并赋值DNS Server的IP

  2. 使用 dnsServers[rand.Intn(len(dnsServers))] 方法实现随机选中一个DNS Server

查询方法
m := new(dns.Msg)
m.SetQuestion(domain+".", dns.TypeA)
c := new(dns.Client)
if r, _, err := c.Exchange(m, net.JoinHostPort(server, "53")); err == nil {
		for _, ans := range r.Answer {
			if a, ok := ans.(*dns.A); ok {
				res := DnsCheckDomainData{Domain: domain, Ip: a.A.String(), DnsServer: server, Timer: Timer()}
				return &res
			}
		}
	}
  1. if a, ok := ans.(*dns.A); ok {} 是go中的一种写法,作用当ans.(*dns.A)返回无错误时执行{}内代码

  2. ans.(*dns.A) 表示只查询IPv4记录

完整代码

package main // 声明包名称

import ( // 导入使用的库
	"fmt"
	"github.com/miekg/dns"
)

func DnsCheckDomain(domain string) *DnsCheckDomainData {
	dnsServers := []string{"114.114.114.114", "8.8.8.8", "8.8.4.4", "114.114.115.115", "114.114.114.119", "114.114.115.119", "114.114.114.110", "114.114.115.110", "223.5.5.5", "223.6.6.6", "180.76.76.76", "119.29.29.29", "119.28.28.28", "182.254.116.116", "182.254.118.118", "1.2.4.8", "52.80.66.66", "117.50.10.10", "52.80.52.52", "117.50.60.30", "52.80.60.30", "210.2.4.8", "112.124.47.27", "114.215.126.16", "123.125.81.6", "117.50.11.11", "101.226.4.6", "218.30.118.6"}
	m := new(dns.Msg)
	m.SetQuestion(domain+".", dns.TypeA)
	c := new(dns.Client)
	server := dnsServers[rand.Intn(len(dnsServers))]
	if r, _, err := c.Exchange(m, net.JoinHostPort(server, "53")); err == nil {
		for _, ans := range r.Answer {
			if a, ok := ans.(*dns.A); ok {
				res := DnsCheckDomainData{Domain: domain, Ip: a.A.String(), DnsServer: server, Timer: Timer()}
				return &res
			}
		}
	}
	return nil
}
  • 这里我们已经实现了一次DNS A记录查询

使用它

func main() {
	res := DnsCheckDomain("www.baidu.com")
	fmt.Println("Domain:", res.Domain)
	fmt.Println("IPv4:", res.Ip)
	fmt.Println("Dns:", res.DnsServer)
	fmt.Println("Time:", res.Timer)
}

输出结果

Domain: www.baidu.com
IPv4: 180.101.50.188
Dns: 182.254.116.116
Time: 2024-09-20 11:28:30

实现多并发检测

并发方法

// threadSubDomain 多并发子域名检测
func threadSubDomain(domain string, wg *sync.WaitGroup, semaphore chan struct{}, result chan *DnsCheckDomainData) {
	semaphore <- struct{}{}
	defer func() { <-semaphore }()
	defer wg.Done()
	result <- DnsCheckDomain(domain)
}
  • 返回一个 *DnsCheckDomainData 结构的数据

使用并发

import "sync"

func main() {
	domain := "birdy02.com"
	domains := []string{"www", "w", "api"}
	var workWg sync.WaitGroup // 声明、类似于信标
	var semaphore = make(chan struct{}, 20) // 设置最大并发20
	task := make(chan *DnsCheckDomainData, len(domains)+1) // 存储并发返回值
	for _, sub := range domains { // 循环执行并发任务
		workWg.Add(1)// 添加一个任务标识
		go threadSubDomain(fmt.Sprintf("%s.%s", sub, domain), &workWg, semaphore, task) // 执行并发检测
	}
	workWg.Wait() // 等待并发结束
	close(task)
	for r := range task { // 循环获取返回值
		if r != nil { // 判断当返回非空的时候
			fmt.Println(r.Timer, r.Domain, r.Ip, r.DnsServer)
		}
	}
}

输出结果

  • 输出:时间、域名、IPv4、DNS Server

2024-09-20 11:37:18 w.birdy02.com 38.147.170.156 1.2.4.8
2024-09-20 11:37:18 api.birdy02.com 38.147.170.156 218.30.118.6
2024-09-20 11:37:18 www.birdy02.com 38.147.170.156 114.114.114.119

丰富验证方法

判断是否存在泛解析

判断是否存在泛解析就需要使用不可能存在的域名解析测试,如果解析到了IP则说明存在泛解析

  • 编写一个获取长32位包含数字字母的字符串当作子域前缀

func RandomString() string {
	length := 32
	const charset = "abcdefghijklmnopqrstuvwxyz1234567890"
	var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))
	b := make([]byte, length)
	for i := range b {
		b[i] = charset[seededRand.Intn(len(charset))]
	}
	return string(b)
}
  • 获取10个随机子域名请求

  • 如果存在IP则记录

  • 不存在则退出

var ipFan = make(map[string]bool)
fmt.Println("【SubDomain Burst】 泛解析测试")
for i := 0; i <= 10; i++ {
	if r := DnsCheckDomain(fmt.Sprintf("%s.%s", RandomString(), domain)); r != nil {
		if ipFan[r.Ip] {
			continue
		}
		ipFan[r.Ip] = true
	} else {
		break
	}
}
  • map类似于python中的dict

对所有子域列表进行请求

fmt.Println(fmt.Sprintf("【SubDomain Burst】 Count SubDomains Dict %d 个", len(domains)))
workWg.Add(1)
go threadSubDomain(domain, &workWg, semaphore, task)
for _, sub := range domains {
	workWg.Add(1)
	go threadSubDomain(fmt.Sprintf("%s.%s", sub, domain), &workWg, semaphore, task)
}
workWg.Wait()
close(task)

去除泛解析IP的域名

fmt.Println(fmt.Sprintf("【SubDomain Burst】 %s 去除泛解析", domain))
var tempRes = make(map[string][]DnsCheckDomainData)
for r := range task {
	if r == nil || (r.Ip == "" || r.Domain == "") {
		continue
	}
	tempRes[r.Ip] = append(tempRes[r.Ip], *r)
}

输出结果

fmt.Println(fmt.Sprintf("【SubDomain Burst】 %s 数据输出", domain))
for vIp, v := range tempRes {
	if ipFan[vIp] && len(v) > 0 {
		var data *DnsCheckDomainData
		if num := FanCheckWWW(v, fmt.Sprintf("www.%s", domain)); num != nil {
			data = &v[*num]
		} else {
			data = &v[0]
		}
		result = append(result, *data)
		continue
	}
    for _, i := range v {
			result = append(result, i)
	}
}
func FanCheckWWW(list []DnsCheckDomainData, char string) *int {
	for num, c := range list {
		if c.Domain == char {
			return &num
		}
	}
	return nil
}
  • FanCheckWWW 是为了判断当域名存在泛解析时,保留www域名

完整代码

// CheckSubDomain 验证子域名
func CheckSubDomain(domain string) []DnsCheckDomainData {
	var result []DnsCheckDomainData
	domains := []string{"www", "w", "api", "blog", "doc-api"}
	var workWg sync.WaitGroup
	var semaphore = make(chan struct{}, 20)
	task := make(chan *DnsCheckDomainData, len(domains)+1)
	var ipFan = make(map[string]bool)
	fmt.Println("【SubDomain Burst】 泛解析测试")
	for i := 0; i <= 10; i++ {
		if r := DnsCheckDomain(fmt.Sprintf("%s.%s", RandomString(), domain)); r != nil {
			if ipFan[r.Ip] {
				continue
			}
			ipFan[r.Ip] = true
		} else {
			break
		}
	}
	for i := range ipFan {
		fmt.Println(fmt.Sprintf("【SubDomain Burst】 泛解析IP: %s", i))
	}
	fmt.Println(fmt.Sprintf("【SubDomain Burst】 Count SubDomains Dict %d 个", len(domains)))
	workWg.Add(1)
	go threadSubDomain(domain, &workWg, semaphore, task)
	for _, sub := range domains {
		workWg.Add(1)
		go threadSubDomain(fmt.Sprintf("%s.%s", sub, domain), &workWg, semaphore, task)
	}
	workWg.Wait()
	close(task)
	fmt.Println(fmt.Sprintf("【SubDomain Burst】 %s 去除泛解析", domain))
	var tempRes = make(map[string][]DnsCheckDomainData)
	for r := range task {
		if r == nil || (r.Ip == "" || r.Domain == "") {
			continue
		}
		tempRes[r.Ip] = append(tempRes[r.Ip], *r)
	}
	fmt.Println(fmt.Sprintf("【SubDomain Burst】 %s 数据输出", domain))
	for vIp, v := range tempRes {
		fmt.Println(v)
		if ipFan[vIp] && len(v) > 0 {
			var data DnsCheckDomainData
			if num := FanCheckWWW(v, fmt.Sprintf("www.%s", domain)); num != nil {
				data = *&v[*num]
			} else {
				data = *&v[0]
			}
			result = append(result, data)
			continue
		}
		for _, i := range v {
			result = append(result, i)
		}
	}
	return result
}

使用它

func main() {
	res := CheckSubDomain("birdy02.com")
	for _, r := range res {
		fmt.Println(r.Timer, r.Domain, r.Ip, r.DnsServer)
	}
}

输出结果

【SubDomain Burst】 泛解析测试
【SubDomain Burst】 Count SubDomains Dict 5 个
【SubDomain Burst】 birdy02.com 去除泛解析
【SubDomain Burst】 birdy02.com 数据输出
2024-09-20 12:05:07 doc-api.birdy02.com 38.147.170.156 52.80.66.66
2024-09-20 12:05:07 api.birdy02.com 38.147.170.156 112.124.47.27
2024-09-20 12:05:07 blog.birdy02.com 38.147.170.156 123.125.81.6
2024-09-20 12:05:07 www.birdy02.com 38.147.170.156 8.8.8.8
2024-09-20 12:05:07 w.birdy02.com 38.147.170.156 1.2.4.8

全部代码

package main //声明包名称
import (
	"flag"
	"fmt"
	"github.com/miekg/dns"
	"math/rand"
	"net"
	"sync"
	"time"
)

type DnsCheckDomainData struct {
	Domain    string `json:"domain"`
	Ip        string `json:"ip"`
	DnsServer string `json:"dnsServer"`
	Timer     string `json:"timer"`
}

func Timer() string { // 获取当前时间
	now := time.Now()
	formatted := now.Format("2006-01-02 15:04:05")
	return formatted
}

func RandomString() string {
	length := 32
	const charset = "abcdefghijklmnopqrstuvwxyz1234567890"
	var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))
	b := make([]byte, length)
	for i := range b {
		b[i] = charset[seededRand.Intn(len(charset))]
	}
	return string(b)
}

func FanCheckWWW(list []DnsCheckDomainData, char string) *int {
	for num, c := range list {
		if c.Domain == char {
			return &num
		}
	}
	return nil
}

// CheckSubDomain 验证子域名
func CheckSubDomain(domain string) []DnsCheckDomainData {
	var result []DnsCheckDomainData
	var domains = []string{"www", "mail", "ftp", "smtp", "pop", "m", "webmail", "pop3", "imap", "localhost", "autodiscover", "admin", "bbs", "test", "mx", "en", "email", "wap", "blog", "oa", "ns1", "vpn", "ns2", "www2", "mysql", "webdisk", "dev", "old", "news", "calendar", "shop", "potala", "mobile", "web", "sip", "mobilemail", "ns", "cpanel", "www1", "whm", "new", "img", "search", "support", "mail2", "media", "files", "e", "video", "app", "secure", "my", "crm", "intranet", "portal", "demo", "api", "beta", "fax", "lyncdiscover", "dns", "images", "db", "staging", "info", "docs", "static"}
	var workWg sync.WaitGroup
	var semaphore = make(chan struct{}, 20)
	task := make(chan *DnsCheckDomainData, len(domains)+1)
	var ipFan = make(map[string]bool)
	fmt.Println("【SubDomain Burst】 泛解析测试")
	for i := 0; i <= 10; i++ {
		if r := DnsCheckDomain(fmt.Sprintf("%s.%s", RandomString(), domain)); r != nil {
			if ipFan[r.Ip] {
				continue
			}
			ipFan[r.Ip] = true
		} else {
			break
		}
	}
	for i := range ipFan {
		fmt.Println(fmt.Sprintf("【SubDomain Burst】 泛解析IP: %s", i))
	}
	fmt.Println(fmt.Sprintf("【SubDomain Burst】 Count SubDomains Dict %d 个", len(domains)))
	workWg.Add(1)
	go threadSubDomain(domain, &workWg, semaphore, task)
	for _, sub := range domains {
		workWg.Add(1)
		go threadSubDomain(fmt.Sprintf("%s.%s", sub, domain), &workWg, semaphore, task)
	}
	workWg.Wait()
	close(task)
	fmt.Println(fmt.Sprintf("【SubDomain Burst】 %s 去除泛解析", domain))
	var tempRes = make(map[string][]DnsCheckDomainData)
	for r := range task {
		if r == nil || (r.Ip == "" || r.Domain == "") {
			continue
		}
		tempRes[r.Ip] = append(tempRes[r.Ip], *r)
	}
	fmt.Println(fmt.Sprintf("【SubDomain Burst】 %s 数据输出", domain))
	for vIp, v := range tempRes {
		if ipFan[vIp] && len(v) > 0 {
			var data DnsCheckDomainData
			if num := FanCheckWWW(v, fmt.Sprintf("www.%s", domain)); num != nil {
				data = *&v[*num]
			} else {
				data = *&v[0]
			}
			result = append(result, data)
			continue
		}
		for _, i := range v {
			result = append(result, i)
		}
	}
	return result
}

// threadSubDomain 多并发子域名检测
func threadSubDomain(domain string, wg *sync.WaitGroup, semaphore chan struct{}, result chan *DnsCheckDomainData) {
	semaphore <- struct{}{}
	defer func() { <-semaphore }()
	defer wg.Done()
	result <- DnsCheckDomain(domain)
}

// DnsCheckDomain DNS尝试域名解析
func DnsCheckDomain(domain string) *DnsCheckDomainData {
	dnsServers := []string{"114.114.114.114", "8.8.8.8", "8.8.4.4", "114.114.115.115", "114.114.114.119", "114.114.115.119", "114.114.114.110", "114.114.115.110", "223.5.5.5", "223.6.6.6", "180.76.76.76", "119.29.29.29", "119.28.28.28", "182.254.116.116", "182.254.118.118", "1.2.4.8", "52.80.66.66", "117.50.10.10", "52.80.52.52", "117.50.60.30", "52.80.60.30", "210.2.4.8", "112.124.47.27", "114.215.126.16", "123.125.81.6", "117.50.11.11", "101.226.4.6", "218.30.118.6"}
	m := new(dns.Msg)
	m.SetQuestion(domain+".", dns.TypeA)
	c := new(dns.Client)
	server := dnsServers[rand.Intn(len(dnsServers))]
	if r, _, err := c.Exchange(m, net.JoinHostPort(server, "53")); err == nil {
		for _, ans := range r.Answer {
			if a, ok := ans.(*dns.A); ok {
				res := DnsCheckDomainData{Domain: domain, Ip: a.A.String(), DnsServer: server, Timer: Timer()}
				return &res
			}
		}
	}
	return nil
}

func main() {
	var d = flag.String("d", "", "输入单个子域名用于解析")
	var s = flag.String("s", "", "输入根域名,进行域名爆破")
	flag.Parse()
	if *d != "" {
		if res := DnsCheckDomain(*d); res != nil {
			fmt.Println("Domain:", res.Domain)
			fmt.Println("IPv4:", res.Ip)
			fmt.Println("Dns:", res.DnsServer)
			fmt.Println("Time:", res.Timer)
		}
	} else if *s != "" {
		res := CheckSubDomain(*s)
		for _, r := range res {
			fmt.Println("[+]", r.Timer, r.Domain, r.Ip, r.DnsServer)
		}
	}
}

使用方法

>> .\NsSubDomain.exe -d www.birdy02.com
Domain: www.birdy02.com
IPv4: 38.147.170.156
Dns: 182.254.116.116
Time: 2024-09-20 13:23:29

>> .\NsSubDomain.exe -s birdy02.com
【SubDomain Burst】 泛解析测试
【SubDomain Burst】 Count SubDomains Dict 8 个
【SubDomain Burst】 birdy02.com 去除泛解析
【SubDomain Burst】 birdy02.com 数据输出
2024-09-20 13:26:00 www.birdy02.com 38.147.170.156 114.215.126.16

下载

林乐天的云存储:https://oss.birdy02.com/p/halo/NsSubDomain.exe?sign=2AbGLhHhEz0x1bas-xVTWDwkBmmAxrj9D4AcdYmkvJE=:0

GitHub: 点击下载