├── 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 |
--------------------------------------------------------------------------------
|