├── LICENSE ├── README.md ├── alienvault └── alienvault.go ├── banner └── banner.go ├── certspotter └── certspotter.go ├── chaos └── chaos.go ├── crtsh └── crtsh.go ├── dnsdumpster └── dnsdumpster.go ├── go.mod ├── hackertarget └── hackertarget.go ├── jldc └── jldc.go ├── main.go ├── merklemap └── merklemap.go ├── reverseipdomain └── reverseipdomain.go ├── shodan └── shodan.go ├── subdomaincenter └── subdomaincenter.go ├── subdomainfinder └── subdomainfinder.go ├── trickest └── trickest.go ├── urlscan └── urlscan.go └── virustotal └── virustotal.go /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Bhagirath Saxena 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## subdog 2 | 3 | subdog collects subdomains from number of different sources to create a list of root subdomains (i.e.: corp.example.com) 4 | - Note: subdog tool gives unfiltered data it can be appex domain or subdomains. you have to filter manually for e.g, `grep -aE "^(.*\.)?domain\.com$"` 5 | 6 | ## Installation 7 | ``` 8 | go install github.com/rix4uni/subdog@latest 9 | ``` 10 | 11 | ## Download prebuilt binaries 12 | ``` 13 | wget https://github.com/rix4uni/subdog/releases/download/v0.0.3/subdog-linux-amd64-0.0.3.tgz 14 | tar -xvzf subdog-linux-amd64-0.0.3.tgz 15 | rm -rf subdog-linux-amd64-0.0.3.tgz 16 | mv subdog ~/go/bin/subdog 17 | ``` 18 | Or download [binary release](https://github.com/rix4uni/subdog/releases) for your platform. 19 | 20 | ## Compile from source 21 | ``` 22 | git clone --depth 1 github.com/rix4uni/subdog.git 23 | cd subdog; go install 24 | ``` 25 | 26 | ## Usage 27 | ``` 28 | Usage of subdog: 29 | -silent 30 | silent mode. 31 | -tools string 32 | Choose tools: subdomaincenter, jldc, virustotal, alienvault, urlscan, certspotter, hackertarget, crtsh, trickest, subdomainfinder, chaos, merklemap, shodan, reverseipdomain, or all (default "all") 33 | -verbose 34 | enable verbose mode 35 | -version 36 | Print the version of the tool and exit. 37 | ``` 38 | 39 | ## Examples Usages 40 | 41 | ### Single Domain: 42 | 1. Run with `subdomaincenter` only: 43 | ``` 44 | echo "dell.com" | subdog -tools subdomaincenter 45 | ``` 46 | 47 | 2. Run with multiple tools comma-seprated `subdomaincenter` and `jldc`: 48 | ``` 49 | echo "dell.com" | subdog -tools subdomaincenter,virustotal 50 | ``` 51 | 52 | 3. Run with all tools, (default): 53 | ``` 54 | echo "dell.com" | subdog -tools all 55 | ``` 56 | 57 | ### Multiple Domains: 58 | 1. Run with `subdomaincenter` only: 59 | ``` 60 | cat wildcards.txt | subdog -tools subdomaincenter 61 | ``` 62 | 63 | 2. Run with multiple tools comma-seprated `subdomaincenter` and `jldc`: 64 | ``` 65 | cat wildcards.txt | subdog -tools subdomaincenter,virustotal 66 | ``` 67 | 68 | 3. Run with all tools, (default): 69 | ``` 70 | cat wildcards.txt | subdog -tools all 71 | ``` 72 | 73 | ## Sources 74 | - [subdomainfinder](https://subdomainfinder.c99.nl) 75 | - [trickest](https://github.com/trickest/inventory) 76 | - [crt.sh](https://crt.sh) 77 | - [hackertarget](https://api.hackertarget.com) 78 | - [dnsdumpster](https://dnsdumpster.com) 79 | - [certspotter](https://api.certspotter.com) 80 | - [urlscan](https://urlscan.io) 81 | - [alienvault](https://otx.alienvault.com) 82 | - [virustotal](https://www.virustotal.com) 83 | - [jldc](https://jldc.me) 84 | - [subdomaincenter](https://api.subdomain.center) 85 | - [chaos](https://chaos.projectdiscovery.io) 86 | - [merklemap](https://api.merklemap.com) 87 | - [shodan](https://api.shodan.io) 88 | - [reverseipdomain](https://sub-scan-api.reverseipdomain.com) -------------------------------------------------------------------------------- /alienvault/alienvault.go: -------------------------------------------------------------------------------- 1 | package alienvault 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | type AlienVaultResponse struct { 11 | PassiveDNS []struct { 12 | Hostname string `json:"hostname"` 13 | } `json:"passive_dns"` 14 | } 15 | 16 | // FetchSubdomains fetches subdomains for a given domain from the AlienVault API 17 | func FetchSubdomains(domain string) ([]string, error) { 18 | url := fmt.Sprintf("https://otx.alienvault.com/api/v1/indicators/domain/%s/passive_dns", domain) 19 | 20 | resp, err := http.Get(url) 21 | if err != nil { 22 | return nil, err 23 | } 24 | defer resp.Body.Close() 25 | 26 | body, err := ioutil.ReadAll(resp.Body) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | var avResponse AlienVaultResponse 32 | err = json.Unmarshal(body, &avResponse) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | var subdomains []string 38 | for _, record := range avResponse.PassiveDNS { 39 | subdomains = append(subdomains, record.Hostname) 40 | } 41 | 42 | return subdomains, nil 43 | } 44 | -------------------------------------------------------------------------------- /banner/banner.go: -------------------------------------------------------------------------------- 1 | package banner 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // prints the version message 8 | const version = "v0.0.3" 9 | 10 | func PrintVersion() { 11 | fmt.Printf("Current subdog version %s\n", version) 12 | } 13 | 14 | // Prints the Colorful banner 15 | func PrintBanner() { 16 | banner := ` __ __ 17 | _____ __ __ / /_ ____/ /____ ____ _ 18 | / ___// / / // __ \ / __ // __ \ / __ / 19 | (__ )/ /_/ // /_/ // /_/ // /_/ // /_/ / 20 | /____/ \__,_//_.___/ \__,_/ \____/ \__, / 21 | /____/ 22 | ` 23 | fmt.Printf("%s\n%50s\n\n", banner, "Current subdog version "+version) 24 | } 25 | -------------------------------------------------------------------------------- /certspotter/certspotter.go: -------------------------------------------------------------------------------- 1 | package certspotter 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | type CertspotterResponse []struct { 11 | DNSNames []string `json:"dns_names"` 12 | } 13 | 14 | // FetchDNSNames fetches DNS names for a given domain from the Certspotter API 15 | func FetchDNSNames(domain string) ([]string, error) { 16 | url := fmt.Sprintf("https://api.certspotter.com/v1/issuances?domain=%s&include_subdomains=true&expand=dns_names", domain) 17 | 18 | resp, err := http.Get(url) 19 | if err != nil { 20 | return nil, err 21 | } 22 | defer resp.Body.Close() 23 | 24 | body, err := ioutil.ReadAll(resp.Body) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | var csResponse CertspotterResponse 30 | err = json.Unmarshal(body, &csResponse) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | var dnsNames []string 36 | for _, issuance := range csResponse { 37 | dnsNames = append(dnsNames, issuance.DNSNames...) 38 | } 39 | 40 | return dnsNames, nil 41 | } 42 | -------------------------------------------------------------------------------- /chaos/chaos.go: -------------------------------------------------------------------------------- 1 | package chaos 2 | 3 | import ( 4 | "archive/zip" 5 | "bytes" 6 | "encoding/json" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "net/http" 11 | "os" 12 | ) 13 | 14 | type Program struct { 15 | Name string `json:"name"` 16 | Domains []string `json:"domains"` 17 | } 18 | 19 | type Root struct { 20 | Programs []Program `json:"programs"` 21 | } 22 | 23 | type ChaosData struct { 24 | Name string `json:"name"` 25 | URL string `json:"url"` 26 | } 27 | 28 | // Fetch and process domain data from chaos 29 | func ProcessDomain(domain string) { 30 | // Fetch bugbounty list 31 | resp, err := http.Get("https://raw.githubusercontent.com/projectdiscovery/public-bugbounty-programs/main/chaos-bugbounty-list.json") 32 | if err != nil { 33 | fmt.Println("Error fetching bugbounty list:", err) 34 | return 35 | } 36 | defer resp.Body.Close() 37 | 38 | body, err := ioutil.ReadAll(resp.Body) 39 | if err != nil { 40 | fmt.Println("Error reading response:", err) 41 | return 42 | } 43 | 44 | var root Root 45 | err = json.Unmarshal(body, &root) 46 | if err != nil { 47 | fmt.Println("Error unmarshaling json:", err) 48 | return 49 | } 50 | 51 | var programName string 52 | for _, p := range root.Programs { 53 | for _, d := range p.Domains { 54 | if d == domain { 55 | programName = p.Name 56 | break 57 | } 58 | } 59 | if programName != "" { 60 | break 61 | } 62 | } 63 | 64 | // Fetch chaos data index 65 | resp, err = http.Get("https://chaos-data.projectdiscovery.io/index.json") 66 | if err != nil { 67 | fmt.Println("Error fetching chaos data index:", err) 68 | return 69 | } 70 | defer resp.Body.Close() 71 | 72 | body, err = ioutil.ReadAll(resp.Body) 73 | if err != nil { 74 | fmt.Println("Error reading response:", err) 75 | return 76 | } 77 | 78 | var chaosData []ChaosData 79 | err = json.Unmarshal(body, &chaosData) 80 | if err != nil { 81 | fmt.Println("Error unmarshaling json:", err) 82 | return 83 | } 84 | 85 | for _, data := range chaosData { 86 | if data.Name == programName { 87 | // Download and extract 88 | resp, err := http.Get(data.URL) 89 | if err != nil { 90 | fmt.Println("Error downloading data:", err) 91 | return 92 | } 93 | defer resp.Body.Close() 94 | 95 | dataBody, err := ioutil.ReadAll(resp.Body) 96 | if err != nil { 97 | fmt.Println("Error reading data response:", err) 98 | return 99 | } 100 | 101 | zipReader, err := zip.NewReader(bytes.NewReader(dataBody), int64(len(dataBody))) 102 | if err != nil { 103 | fmt.Println("Error reading zip:", err) 104 | return 105 | } 106 | 107 | for _, file := range zipReader.File { 108 | if file.Name == domain+".txt" { 109 | rc, err := file.Open() 110 | if err != nil { 111 | fmt.Println("Error opening zip file:", err) 112 | return 113 | } 114 | 115 | io.Copy(os.Stdout, rc) // Print to stdout 116 | rc.Close() 117 | } 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /crtsh/crtsh.go: -------------------------------------------------------------------------------- 1 | package crtsh 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "strings" 9 | ) 10 | 11 | // CsrData represents the JSON response structure from crt.sh 12 | type CsrData struct { 13 | CommonName string `json:"common_name"` 14 | NameValue string `json:"name_value"` 15 | } 16 | 17 | // FetchSubdomains fetches subdomains for a given domain from the crt.sh API 18 | func FetchSubdomains(domain string) ([]string, error) { 19 | url := fmt.Sprintf("https://crt.sh/?q=%s&output=json", domain) 20 | 21 | resp, err := http.Get(url) 22 | if err != nil { 23 | return nil, err 24 | } 25 | defer resp.Body.Close() 26 | 27 | body, err := ioutil.ReadAll(resp.Body) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | var certs []CsrData 33 | if err := json.Unmarshal(body, &certs); err != nil { 34 | return nil, err 35 | } 36 | 37 | var results []string 38 | for _, cert := range certs { 39 | // Split the NameValue field by commas and then by spaces 40 | names := strings.FieldsFunc(cert.NameValue, func(r rune) bool { 41 | return r == ',' || r == ' ' 42 | }) 43 | results = append(results, names...) 44 | } 45 | 46 | // Print results directly without duplicates 47 | for _, result := range results { 48 | trimmedResult := strings.TrimSpace(result) 49 | if trimmedResult != "" { 50 | fmt.Println(trimmedResult) 51 | } 52 | } 53 | 54 | return results, nil // Return results in case it's needed elsewhere 55 | } 56 | -------------------------------------------------------------------------------- /dnsdumpster/dnsdumpster.go: -------------------------------------------------------------------------------- 1 | package dnsdumpster 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "regexp" 9 | ) 10 | 11 | // FetchSubdomains fetches subdomains from DNSDumpster for a given domain 12 | func FetchSubdomains(domain string) ([]string, error) { 13 | url := "https://dnsdumpster.com/" 14 | csrfToken := "vZGRO1YfdzdviMYTYZqLrw0PxsV5mlAnVGFadIqkjIAhiyNgi5w70hIj7uuzdmXx" // Hardcoded CSRF token 15 | 16 | // Create a new request 17 | client := &http.Client{} 18 | form := fmt.Sprintf("csrfmiddlewaretoken=%s&targetip=%s&user=free", csrfToken, domain) 19 | req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(form))) 20 | if err != nil { 21 | return nil, err 22 | } 23 | 24 | // Set headers 25 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 26 | req.Header.Set("Referer", url) 27 | req.Header.Set("Cookie", "csrftoken=" + csrfToken) 28 | 29 | // Send request 30 | resp, err := client.Do(req) 31 | if err != nil { 32 | return nil, err 33 | } 34 | defer resp.Body.Close() 35 | 36 | // Read response body 37 | body, err := ioutil.ReadAll(resp.Body) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | // Regex to match the subdomain entries 43 | re := regexp.MustCompile(`(.*?)`) 44 | matches := re.FindAllStringSubmatch(string(body), -1) 45 | 46 | var subdomains []string 47 | for _, match := range matches { 48 | if len(match) > 1 { 49 | subdomains = append(subdomains, match[1]) 50 | } 51 | } 52 | 53 | return subdomains, nil 54 | } 55 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rix4uni/subdog 2 | 3 | go 1.23.4 4 | -------------------------------------------------------------------------------- /hackertarget/hackertarget.go: -------------------------------------------------------------------------------- 1 | package hackertarget 2 | 3 | import ( 4 | "encoding/csv" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "strings" 9 | ) 10 | 11 | // FetchSubdomains fetches subdomains for a given domain from the HackerTarget API 12 | func FetchSubdomains(domain string) ([]string, error) { 13 | url := fmt.Sprintf("https://api.hackertarget.com/hostsearch/?q=%s", domain) 14 | 15 | resp, err := http.Get(url) 16 | if err != nil { 17 | return nil, err 18 | } 19 | defer resp.Body.Close() 20 | 21 | body, err := ioutil.ReadAll(resp.Body) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | // Parse the CSV response 27 | reader := csv.NewReader(strings.NewReader(string(body))) 28 | records, err := reader.ReadAll() 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | var subdomains []string 34 | for _, record := range records { 35 | if len(record) > 0 { 36 | subdomains = append(subdomains, record[0]) // First column contains the subdomain 37 | } 38 | } 39 | 40 | return subdomains, nil 41 | } 42 | -------------------------------------------------------------------------------- /jldc/jldc.go: -------------------------------------------------------------------------------- 1 | package jldc 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | // FetchSubdomains fetches subdomains for a given domain from the jldc.me API 11 | func FetchSubdomains(domain string) ([]string, error) { 12 | url := fmt.Sprintf("https://jldc.me/anubis/subdomains/%s", domain) 13 | 14 | resp, err := http.Get(url) 15 | if err != nil { 16 | return nil, err 17 | } 18 | defer resp.Body.Close() 19 | 20 | body, err := ioutil.ReadAll(resp.Body) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | var subdomains []string 26 | err = json.Unmarshal(body, &subdomains) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | return subdomains, nil 32 | } 33 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "strings" 9 | 10 | "github.com/rix4uni/subdog/subdomaincenter" 11 | "github.com/rix4uni/subdog/jldc" 12 | "github.com/rix4uni/subdog/virustotal" 13 | "github.com/rix4uni/subdog/alienvault" 14 | "github.com/rix4uni/subdog/urlscan" 15 | "github.com/rix4uni/subdog/certspotter" 16 | "github.com/rix4uni/subdog/hackertarget" 17 | "github.com/rix4uni/subdog/crtsh" 18 | "github.com/rix4uni/subdog/trickest" 19 | "github.com/rix4uni/subdog/subdomainfinder" 20 | "github.com/rix4uni/subdog/chaos" 21 | "github.com/rix4uni/subdog/merklemap" 22 | "github.com/rix4uni/subdog/shodan" 23 | "github.com/rix4uni/subdog/reverseipdomain" 24 | "github.com/rix4uni/subdog/banner" 25 | ) 26 | 27 | func main() { 28 | tools := flag.String("tools", "all", "Choose tools: subdomaincenter, jldc, virustotal, alienvault, urlscan, certspotter, hackertarget, crtsh, trickest, subdomainfinder, chaos, merklemap, shodan, reverseipdomain, or all") 29 | silent := flag.Bool("silent", false, "silent mode.") 30 | versionFlag := flag.Bool("version", false, "Print the version of the tool and exit.") 31 | verbose := flag.Bool("verbose", false, "enable verbose mode") 32 | flag.Parse() 33 | 34 | if *versionFlag { 35 | banner.PrintBanner() 36 | banner.PrintVersion() 37 | return 38 | } 39 | 40 | if !*silent { 41 | banner.PrintBanner() 42 | } 43 | 44 | scanner := bufio.NewScanner(os.Stdin) 45 | for scanner.Scan() { 46 | domain := strings.TrimSpace(scanner.Text()) 47 | 48 | if *tools == "subdomaincenter" || *tools == "all" { 49 | if *verbose { 50 | fmt.Printf("Fetching from subdomaincenter for %s\n", domain) 51 | } 52 | subdomains, err := subdomaincenter.FetchSubdomains(domain) 53 | if err != nil { 54 | if *verbose { 55 | fmt.Printf("Error fetching subdomains from subdomaincenter for %s: %v\n", domain, err) 56 | } 57 | } else { 58 | for _, subdomain := range subdomains { 59 | fmt.Println(subdomain) 60 | } 61 | } 62 | } 63 | 64 | if *tools == "jldc" || *tools == "all" { 65 | if *verbose { 66 | fmt.Printf("Fetching from jldc for %s\n", domain) 67 | } 68 | subdomains, err := jldc.FetchSubdomains(domain) 69 | if err != nil { 70 | if *verbose { 71 | fmt.Printf("Error fetching subdomains from jldc for %s: %v\n", domain, err) 72 | } 73 | } else { 74 | for _, subdomain := range subdomains { 75 | fmt.Println(subdomain) 76 | } 77 | } 78 | } 79 | 80 | if *tools == "virustotal" || *tools == "all" { 81 | if *verbose { 82 | fmt.Printf("Fetching from VirusTotal for %s\n", domain) 83 | } 84 | subdomains, err := virustotal.FetchSubdomains(domain) 85 | if err != nil { 86 | if *verbose { 87 | fmt.Printf("Error fetching subdomains from VirusTotal for %s: %v\n", domain, err) 88 | } 89 | } else { 90 | for _, subdomain := range subdomains { 91 | fmt.Println(subdomain) 92 | } 93 | } 94 | } 95 | 96 | if *tools == "alienvault" || *tools == "all" { 97 | if *verbose { 98 | fmt.Printf("Fetching from AlienVault for %s\n", domain) 99 | } 100 | subdomains, err := alienvault.FetchSubdomains(domain) 101 | if err != nil { 102 | if *verbose { 103 | fmt.Printf("Error fetching subdomains from AlienVault for %s: %v\n", domain, err) 104 | } 105 | } else { 106 | for _, subdomain := range subdomains { 107 | fmt.Println(subdomain) 108 | } 109 | } 110 | } 111 | 112 | if *tools == "urlscan" || *tools == "all" { 113 | if *verbose { 114 | fmt.Printf("Fetching from URLScan for %s\n", domain) 115 | } 116 | subdomains, err := urlscan.FetchSubdomains(domain) 117 | if err != nil { 118 | if *verbose { 119 | fmt.Printf("Error fetching subdomains from URLScan for %s: %v\n", domain, err) 120 | } 121 | } else { 122 | for _, subdomain := range subdomains { 123 | fmt.Println(subdomain) 124 | } 125 | } 126 | } 127 | 128 | if *tools == "certspotter" || *tools == "all" { 129 | if *verbose { 130 | fmt.Printf("Fetching from Certspotter for %s\n", domain) 131 | } 132 | dnsNames, err := certspotter.FetchDNSNames(domain) 133 | if err != nil { 134 | if *verbose { 135 | fmt.Printf("Error fetching DNS names from Certspotter for %s: %v\n", domain, err) 136 | } 137 | } else { 138 | for _, dnsName := range dnsNames { 139 | fmt.Println(dnsName) 140 | } 141 | } 142 | } 143 | 144 | if *tools == "hackertarget" || *tools == "all" { 145 | if *verbose { 146 | fmt.Printf("Fetching from HackerTarget for %s\n", domain) 147 | } 148 | subdomains, err := hackertarget.FetchSubdomains(domain) 149 | if err != nil { 150 | if *verbose { 151 | fmt.Printf("Error fetching subdomains from HackerTarget for %s: %v\n", domain, err) 152 | } 153 | } else { 154 | for _, subdomain := range subdomains { 155 | fmt.Println(subdomain) 156 | } 157 | } 158 | } 159 | 160 | if *tools == "crtsh" || *tools == "all" { 161 | if *verbose { 162 | fmt.Printf("Fetching from crt.sh for %s\n", domain) 163 | } 164 | subdomains, err := crtsh.FetchSubdomains(domain) 165 | if err != nil { 166 | if *verbose { 167 | fmt.Printf("Error fetching subdomains from crt.sh for %s: %v\n", domain, err) 168 | } 169 | } else { 170 | for _, subdomain := range subdomains { 171 | fmt.Println(subdomain) 172 | } 173 | } 174 | } 175 | 176 | if *tools == "trickest" || *tools == "all" { 177 | if *verbose { 178 | fmt.Printf("Fetching from Trickest for %s\n", domain) 179 | } 180 | hostnames, err := trickest.FetchHostnames(domain) 181 | if err != nil { 182 | if *verbose { 183 | fmt.Printf("Error fetching hostnames from Trickest for %s: %v\n", domain, err) 184 | } 185 | } else { 186 | for _, hostname := range hostnames { 187 | fmt.Println(hostname) 188 | } 189 | } 190 | } 191 | 192 | if *tools == "subdomainfinder" || *tools == "all" { 193 | if *verbose { 194 | fmt.Printf("Fetching from Subdomain Finder for %s\n", domain) 195 | } 196 | subdomains, err := subdomainfinder.FetchSubdomains(domain) 197 | if err != nil { 198 | if *verbose { 199 | fmt.Printf("Error fetching subdomains from Subdomain Finder for %s: %v\n", domain, err) 200 | } 201 | } else { 202 | for _, subdomain := range subdomains { 203 | fmt.Println(subdomain) 204 | } 205 | } 206 | } 207 | 208 | if *tools == "chaos" || *tools == "all" { 209 | if *verbose { 210 | fmt.Printf("Fetching from Chaos for %s\n", domain) 211 | } 212 | chaos.ProcessDomain(domain) 213 | } 214 | 215 | if *tools == "merklemap" || *tools == "all" { 216 | if *verbose { 217 | fmt.Printf("Fetching from shodan for %s\n", domain) 218 | } 219 | subdomains, err := merklemap.FetchDomains(domain) 220 | if err != nil { 221 | if *verbose { 222 | fmt.Printf("Error fetching domains from shodan for %s: %v\n", domain, err) 223 | } 224 | } else { 225 | for _, subdomain := range subdomains { 226 | fmt.Println(subdomain) 227 | } 228 | } 229 | } 230 | 231 | if *tools == "shodan" || *tools == "all" { 232 | if *verbose { 233 | fmt.Printf("Fetching from MerkleMap for %s\n", domain) 234 | } 235 | subdomains, err := shodan.FetchSubdomains(domain) 236 | if err != nil { 237 | if *verbose { 238 | fmt.Printf("Error fetching domains from MerkleMap for %s: %v\n", domain, err) 239 | } 240 | } else { 241 | for _, subdomain := range subdomains { 242 | fmt.Println(subdomain) 243 | } 244 | } 245 | } 246 | 247 | if *tools == "reverseipdomain" || *tools == "all" { 248 | if *verbose { 249 | fmt.Printf("Fetching from reverseipdomain for %s\n", domain) 250 | } 251 | subdomains, err := reverseipdomain.FetchSubdomains(domain) 252 | if err != nil { 253 | if *verbose { 254 | fmt.Printf("Error fetching domains from reverseipdomain for %s: %v\n", domain, err) 255 | } 256 | } else { 257 | for _, subdomain := range subdomains { 258 | fmt.Println(subdomain) 259 | } 260 | } 261 | } 262 | } 263 | 264 | if err := scanner.Err(); err != nil { 265 | fmt.Printf("Error reading input: %v\n", err) 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /merklemap/merklemap.go: -------------------------------------------------------------------------------- 1 | package merklemap 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | // FetchDomains fetches subdomains for a given domain from the MerkleMap API 11 | func FetchDomains(domain string) ([]string, error) { 12 | url := fmt.Sprintf("https://api.merklemap.com/search?query=%s&stream=true", domain) 13 | 14 | resp, err := http.Get(url) 15 | if err != nil { 16 | return nil, err 17 | } 18 | defer resp.Body.Close() 19 | 20 | scanner := bufio.NewScanner(resp.Body) 21 | var subdomains []string 22 | 23 | for scanner.Scan() { 24 | line := strings.TrimSpace(scanner.Text()) 25 | if strings.HasPrefix(line, "data: ") { 26 | line = strings.TrimPrefix(line, "data: ") 27 | // Extract domain field from the JSON response 28 | if strings.Contains(line, `"domain"`) { 29 | domainStart := strings.Index(line, `"domain":"`) + 10 30 | domainEnd := strings.Index(line[domainStart:], `"`) + domainStart 31 | subdomains = append(subdomains, line[domainStart:domainEnd]) 32 | } 33 | } 34 | } 35 | 36 | if err := scanner.Err(); err != nil { 37 | return nil, err 38 | } 39 | 40 | return subdomains, nil 41 | } 42 | -------------------------------------------------------------------------------- /reverseipdomain/reverseipdomain.go: -------------------------------------------------------------------------------- 1 | package reverseipdomain 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | // FetchSubdomains fetches subdomains for a given domain from the Reverse IP Domain API 11 | func FetchSubdomains(domain string) ([]string, error) { 12 | url := fmt.Sprintf("https://sub-scan-api.reverseipdomain.com/?domain=%s", domain) 13 | 14 | resp, err := http.Get(url) 15 | if err != nil { 16 | return nil, err 17 | } 18 | defer resp.Body.Close() 19 | 20 | body, err := ioutil.ReadAll(resp.Body) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | var response struct { 26 | Result struct { 27 | Domains []string `json:"domains"` 28 | } `json:"result"` 29 | } 30 | 31 | err = json.Unmarshal(body, &response) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | return response.Result.Domains, nil 37 | } 38 | -------------------------------------------------------------------------------- /shodan/shodan.go: -------------------------------------------------------------------------------- 1 | package shodan 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | // FetchSubdomains fetches subdomains for a given domain from the Shodan 11 | func FetchSubdomains(domain string) ([]string, error) { 12 | url := fmt.Sprintf("https://api.shodan.io/dns/domain/%s", domain) 13 | 14 | resp, err := http.Get(url) 15 | if err != nil { 16 | return nil, err 17 | } 18 | defer resp.Body.Close() 19 | 20 | body, err := ioutil.ReadAll(resp.Body) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | var response struct { 26 | Subdomains []string `json:"subdomains"` 27 | } 28 | 29 | err = json.Unmarshal(body, &response) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | // Append the full domain to each subdomain 35 | fullSubdomains := make([]string, len(response.Subdomains)) 36 | for i, sub := range response.Subdomains { 37 | fullSubdomains[i] = fmt.Sprintf("%s.%s", sub, domain) 38 | } 39 | 40 | return fullSubdomains, nil 41 | } 42 | -------------------------------------------------------------------------------- /subdomaincenter/subdomaincenter.go: -------------------------------------------------------------------------------- 1 | package subdomaincenter 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | // FetchSubdomains fetches subdomains for a given domain from the API 11 | func FetchSubdomains(domain string) ([]string, error) { 12 | url := fmt.Sprintf("https://api.subdomain.center/?domain=%s", domain) 13 | 14 | resp, err := http.Get(url) 15 | if err != nil { 16 | return nil, err 17 | } 18 | defer resp.Body.Close() 19 | 20 | body, err := ioutil.ReadAll(resp.Body) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | var subdomains []string 26 | err = json.Unmarshal(body, &subdomains) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | return subdomains, nil 32 | } 33 | -------------------------------------------------------------------------------- /subdomainfinder/subdomainfinder.go: -------------------------------------------------------------------------------- 1 | package subdomainfinder 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "regexp" 9 | ) 10 | 11 | // FetchSubdomains fetches subdomains for a given domain from the subdomainfinder API 12 | func FetchSubdomains(domain string) ([]string, error) { 13 | url := "https://subdomainfinder.c99.nl/" 14 | 15 | // Prepare the POST request 16 | payload := fmt.Sprintf("CSRF9843433218797932=pirate107704869&is_admin=false&jn=JS+aan%2C+T+aangeroepen%2C+CSRF+aangepast&domain=%s&lol-stop-reverse-engineering-my-source-and-buy-an-api-key=cf917529992fd6f916e2b4ef8b37c6d97f040eba&scan_subdomains=", domain) 17 | req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(payload))) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | // Set the headers 23 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.6533.100 Safari/537.36") 24 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 25 | 26 | client := &http.Client{} 27 | resp, err := client.Do(req) 28 | if err != nil { 29 | return nil, err 30 | } 31 | defer resp.Body.Close() 32 | 33 | // Check if the request was successful 34 | if resp.StatusCode != http.StatusOK { 35 | return nil, fmt.Errorf("request failed with status: %s", resp.Status) 36 | } 37 | 38 | // Read the response body 39 | body, err := ioutil.ReadAll(resp.Body) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | // Extract the subdomains from the response body 45 | // Modified regex pattern to avoid lookbehind 46 | subdomainRegex := regexp.MustCompile(`href='//([^']+)'`) 47 | subdomainMatches := subdomainRegex.FindAllStringSubmatch(string(body), -1) 48 | 49 | // Collect unique subdomains 50 | subdomainMap := make(map[string]struct{}) 51 | for _, match := range subdomainMatches { 52 | if len(match) > 1 { 53 | subdomainMap[match[1]] = struct{}{} 54 | } 55 | } 56 | 57 | uniqueSubdomains := make([]string, 0, len(subdomainMap)) 58 | for sub := range subdomainMap { 59 | uniqueSubdomains = append(uniqueSubdomains, sub) 60 | } 61 | 62 | // If no subdomains were found, return an error 63 | if len(uniqueSubdomains) == 0 { 64 | return nil, fmt.Errorf("no subdomains found for %s", domain) 65 | } 66 | 67 | return uniqueSubdomains, nil 68 | } 69 | -------------------------------------------------------------------------------- /trickest/trickest.go: -------------------------------------------------------------------------------- 1 | package trickest 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "strings" 9 | ) 10 | 11 | // TrickestData represents the structure of the Trickest JSON response 12 | type TrickestData struct { 13 | Domain string `json:"domain"` 14 | Hostnames string `json:"hostnames"` 15 | } 16 | 17 | // FetchHostnames fetches hostnames for a given domain from the Trickest API 18 | func FetchHostnames(domain string) ([]string, error) { 19 | // Fetch the Trickest targets JSON file 20 | resp, err := http.Get("https://raw.githubusercontent.com/rix4uni/targets-filter/refs/heads/main/trickest-targets.json") 21 | if err != nil { 22 | return nil, err 23 | } 24 | defer resp.Body.Close() 25 | 26 | body, err := ioutil.ReadAll(resp.Body) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | // Unmarshal the JSON data 32 | var targets []TrickestData 33 | if err := json.Unmarshal(body, &targets); err != nil { 34 | return nil, err 35 | } 36 | 37 | var hostnames []string 38 | for _, target := range targets { 39 | if strings.EqualFold(target.Domain, domain) { 40 | // Fetch hostnames from the URL 41 | url := target.Hostnames 42 | resp, err := http.Get(url) 43 | if err != nil { 44 | return nil, fmt.Errorf("error fetching hostnames from Trickest: %v", err) 45 | } 46 | defer resp.Body.Close() 47 | 48 | // Read the hostnames from the response 49 | body, err = ioutil.ReadAll(resp.Body) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | // Split the response into individual hostnames 55 | lines := strings.Split(string(body), "\n") 56 | for _, line := range lines { 57 | if line = strings.TrimSpace(line); line != "" { 58 | hostnames = append(hostnames, line) 59 | } 60 | } 61 | break // No need to continue after finding the correct domain 62 | } 63 | } 64 | 65 | return hostnames, nil 66 | } 67 | -------------------------------------------------------------------------------- /urlscan/urlscan.go: -------------------------------------------------------------------------------- 1 | package urlscan 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | type URLScanResponse struct { 11 | Results []struct { 12 | Task struct { 13 | Domain string `json:"domain"` 14 | ApexDomain string `json:"apexDomain"` 15 | } `json:"task"` 16 | Page struct { 17 | Domain string `json:"domain"` 18 | ApexDomain string `json:"apexDomain"` 19 | } `json:"page"` 20 | } `json:"results"` 21 | } 22 | 23 | // FetchSubdomains fetches subdomains for a given domain from the URLScan API 24 | func FetchSubdomains(domain string) ([]string, error) { 25 | url := fmt.Sprintf("https://urlscan.io/api/v1/search/?q=domain:%s", domain) 26 | 27 | resp, err := http.Get(url) 28 | if err != nil { 29 | return nil, err 30 | } 31 | defer resp.Body.Close() 32 | 33 | body, err := ioutil.ReadAll(resp.Body) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | var usResponse URLScanResponse 39 | err = json.Unmarshal(body, &usResponse) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | var subdomains []string 45 | for _, result := range usResponse.Results { 46 | subdomains = append(subdomains, result.Task.Domain) 47 | subdomains = append(subdomains, result.Task.ApexDomain) 48 | subdomains = append(subdomains, result.Page.Domain) 49 | subdomains = append(subdomains, result.Page.ApexDomain) 50 | } 51 | 52 | return subdomains, nil 53 | } 54 | -------------------------------------------------------------------------------- /virustotal/virustotal.go: -------------------------------------------------------------------------------- 1 | package virustotal 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | ) 9 | 10 | type VirusTotalResponse struct { 11 | Data []struct { 12 | ID string `json:"id"` 13 | } `json:"data"` 14 | } 15 | 16 | // FetchSubdomains fetches subdomains for a given domain from VirusTotal API 17 | func FetchSubdomains(domain string) ([]string, error) { 18 | url := fmt.Sprintf("https://www.virustotal.com/ui/domains/%s/subdomains?limit=1000&relationships=resolutions", domain) 19 | 20 | req, err := http.NewRequest("GET", url, nil) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | req.Header.Set("X-Tool", "vt-ui-main") 26 | req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36") 27 | req.Header.Set("Accept-Ianguage", "en-US,en;q=0.9,es;q=0.8") 28 | req.Header.Set("X-VT-Anti-Abuse-Header", "MTY1NjA5Nzk1NjAtWkc5dWRDQmlaU0JsZG1scy0xNjgzNDI2MDY4Ljc2MQ==") 29 | 30 | client := &http.Client{} 31 | resp, err := client.Do(req) 32 | if err != nil { 33 | return nil, err 34 | } 35 | defer resp.Body.Close() 36 | 37 | body, err := ioutil.ReadAll(resp.Body) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | var vtResponse VirusTotalResponse 43 | err = json.Unmarshal(body, &vtResponse) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | var subdomains []string 49 | for _, data := range vtResponse.Data { 50 | subdomains = append(subdomains, data.ID) 51 | } 52 | 53 | return subdomains, nil 54 | } 55 | --------------------------------------------------------------------------------