├── README.md ├── apishodan └── api.go ├── go.mod ├── main.go └── shosubgo.png /README.md: -------------------------------------------------------------------------------- 1 | # shosubgo 2 | Small tool to Grab subdomains using Shodan api. 3 | ## Get your shodan api FREE with limit usage: 4 | 5 | 6 | ## Install 7 | 8 | ```bash 9 | $ go install github.com/incogbyte/shosubgo@latest 10 | # verify inside your $GOPATH the folder "bin" 11 | ``` 12 | 13 | ## Usage 14 | ```bash 15 | go run main.go -d target.com -s YourAPIKEY / go run main.go -f file -s YourAPIKEY 16 | ``` 17 | ## Usage download from releases: 18 | 19 | https://github.com/incogbyte/shosubgo/releases/tag/1.1 20 | 21 | ```bash 22 | # From Download Releases 23 | 24 | ./shosubgo_linux -d target.com -s YourAPIKEY 25 | ./shosubgo_linux -f file -s YourAPIKEY 26 | ``` 27 | 28 | ![shosubgo](https://raw.githubusercontent.com/incogbyte/shosubgo/master/shosubgo.png) 29 | 30 | ![gopher](https://encrypted-tbn0.gstatic.com/images?q=tbn%3AANd9GcTFcFPxQzLnq18PnHBkUxF6KfavmHX9q6Ukz-JWSNOg7iJu7Dsy) 31 | 32 | #### Mentions 33 | 34 | 1. Quoted in the vídeo https://www.youtube.com/watch?v=qLTe6Z10vj8 h@cktivitycon 2020: The Bug Hunter's Methodology v4: Recon Edition by @Jhaddix 35 | 2. Mentioned at @SANSOffensive con :) 2024 by @Jhaddix 36 | 37 | -------------------------------------------------------------------------------- /apishodan/api.go: -------------------------------------------------------------------------------- 1 | package apishodan 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | ) 9 | 10 | const URL = "https://api.shodan.io" 11 | const URLDOMAIN = "https://api.shodan.io/dns/domain/" 12 | 13 | type API struct { 14 | apiKey string 15 | } 16 | 17 | type JsonData struct { 18 | QueryCredits int `json:"query_credits"` 19 | ScanCredits int `json:"scan_credits"` 20 | Telnet bool `json:"telnet"` 21 | Plan string `json:"plan"` 22 | HTTPS bool `json:"https"` 23 | Unlocked bool `json:"unlocked"` 24 | } 25 | 26 | type JsonSubDomain struct { 27 | Domain string `json:"domain,omitempty"` 28 | Tags []string `json:"tags,omitempty"` 29 | Data []SubDomain `json:"data,omitempty"` 30 | SubDomains []string `json:"subdomains,omitempty"` 31 | } 32 | 33 | type SubDomain struct { 34 | SubD string `json:"subdomain,omitempty"` 35 | Type string `json:"type,omitempty"` 36 | Value string `json:"value,omitempty"` 37 | LastSeen string `json:"last_seen,omitempty"` 38 | } 39 | 40 | func New(key string) *API { 41 | return &API{apiKey: key} 42 | } 43 | 44 | func (s *API) InfoAccount() (*JsonData, error) { 45 | res, err := http.Get(fmt.Sprintf("%s/api-info?key=%s", URL, s.apiKey)) 46 | if err != nil { 47 | return nil, fmt.Errorf("failed to make request ( Info Account Shodan ): %v", err) 48 | } 49 | defer res.Body.Close() 50 | 51 | if res.StatusCode == http.StatusUnauthorized { 52 | return nil, fmt.Errorf("authorization error: invalid Shodan API key (HTTP 401)") 53 | } 54 | 55 | if res.StatusCode != http.StatusOK { 56 | return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode) 57 | } 58 | 59 | body, err := io.ReadAll(res.Body) 60 | if err != nil { 61 | return nil, fmt.Errorf("failed to read response body: %v", err) 62 | } 63 | 64 | var ret JsonData 65 | if err := json.Unmarshal(body, &ret); err != nil { 66 | return nil, fmt.Errorf("failed to decode JSON response: %v", err) 67 | } 68 | 69 | return &ret, nil 70 | } 71 | 72 | func (s *API) GetSubdomain(domain string) (*JsonSubDomain, error) { 73 | 74 | url := URLDOMAIN + domain + "?key=" + s.apiKey 75 | res, err := http.Get(url) 76 | 77 | if err != nil { 78 | fmt.Println(">> Something went wrong") 79 | panic(err) 80 | } 81 | defer res.Body.Close() 82 | 83 | var sub JsonSubDomain 84 | 85 | if err := json.NewDecoder(res.Body).Decode(&sub); err != nil { 86 | return nil, err 87 | } 88 | 89 | return &sub, nil 90 | 91 | } 92 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/incogbyte/shosubgo 2 | 3 | go 1.19 4 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "flag" 7 | "fmt" 8 | "log" 9 | "os" 10 | 11 | "github.com/incogbyte/shosubgo/apishodan" 12 | ) 13 | 14 | const Author = "inc0gbyt3" 15 | 16 | func main() { 17 | domain := flag.String("d", "", "> Domain to find subdomains") 18 | shodanKey := flag.String("s", "", "> Shodan api key") 19 | verbose := flag.Bool("v", false, "> Show all output") 20 | fileName := flag.String("o", "", "> Save domains into a file") 21 | inputFile := flag.String("f", "", "> File containing domains to find subdomains") 22 | jsonFlag := flag.Bool("json", false, "> Save output in JSON format") 23 | flag.Parse() 24 | 25 | if *domain == "" && *inputFile == "" { 26 | fmt.Printf("[*] Usage: %s -d target.com -s shodanKey [-f input_file]\n", os.Args[0]) 27 | fmt.Printf("[*] Author: %s\n", Author) 28 | os.Exit(1) 29 | } 30 | 31 | apiKey := apishodan.New(*shodanKey) 32 | 33 | var domains []string 34 | 35 | if *domain != "" { 36 | domains = append(domains, *domain) 37 | } 38 | 39 | if *inputFile != "" { 40 | fileDomains, err := readDomainsFromFile(*inputFile) 41 | if err != nil { 42 | log.Fatalf("Failed to read domains from file: %v", err) 43 | } 44 | domains = append(domains, fileDomains...) 45 | } 46 | 47 | for _, domainSearch := range domains { 48 | subdomain, err := apiKey.GetSubdomain(domainSearch) 49 | if err != nil { 50 | fmt.Printf("Error fetching subdomains for domain %s: %v\n", domainSearch, err) 51 | continue 52 | } 53 | 54 | if len(subdomain.SubDomains) == 0 { 55 | fmt.Printf("No subdomains found for domain %s\n", domainSearch) 56 | continue 57 | } 58 | 59 | if *verbose { 60 | info, err := apiKey.InfoAccount() 61 | if err != nil { 62 | fmt.Printf("Error fetching account info: %v\n", err) 63 | continue 64 | } 65 | fmt.Printf("[*] Credits: %d\nScan Credits: %d\n\n", info.QueryCredits, info.ScanCredits) 66 | 67 | for _, v := range subdomain.Data { 68 | d := v.SubD + subdomain.Domain 69 | fmt.Printf("[*] Domain: %s\nIP/DNS: %s\nLast Scan made by Shodan: %s\n", d, v.Value, v.LastSeen) 70 | } 71 | } else { 72 | if *jsonFlag { 73 | jsonData, err := json.MarshalIndent(subdomain.SubDomains, "", " ") 74 | if err != nil { 75 | log.Fatal("Error marshaling JSON:", err) 76 | } 77 | if *fileName != "" { 78 | f, err := os.OpenFile(*fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 79 | if err != nil { 80 | log.Fatal(err) 81 | } 82 | _, err = f.Write(jsonData) 83 | if err != nil { 84 | log.Fatal(err) 85 | } 86 | _, err = f.WriteString("\n") 87 | if err != nil { 88 | log.Fatal(err) 89 | } 90 | f.Close() 91 | fmt.Println("[*] DONE writing JSON to file:", *fileName) 92 | } else { 93 | fmt.Println(string(jsonData)) 94 | } 95 | } else { 96 | for _, v := range subdomain.SubDomains { 97 | if *fileName != "" { 98 | f, err := os.OpenFile(*fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 99 | if err != nil { 100 | log.Fatal(err) 101 | } 102 | _, err = f.WriteString(v + "\n") 103 | if err != nil { 104 | log.Fatal(err) 105 | } 106 | f.Close() 107 | fmt.Println("[*] DONE writing to file:", *fileName) 108 | } 109 | fmt.Printf("%s.%s\n", v, domainSearch) 110 | } 111 | } 112 | } 113 | } 114 | } 115 | 116 | func readDomainsFromFile(filename string) ([]string, error) { 117 | file, err := os.Open(filename) 118 | if err != nil { 119 | return nil, err 120 | } 121 | defer file.Close() 122 | 123 | var domains []string 124 | scanner := bufio.NewScanner(file) 125 | for scanner.Scan() { 126 | domains = append(domains, scanner.Text()) 127 | } 128 | if err := scanner.Err(); err != nil { 129 | return nil, err 130 | } 131 | 132 | return domains, nil 133 | } 134 | -------------------------------------------------------------------------------- /shosubgo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/incogbyte/shosubgo/f605bdd65d8288791f2607aaf6f08b6f3d4aebb4/shosubgo.png --------------------------------------------------------------------------------