├── .gitignore ├── go.mod ├── .github └── FUNDING.yml ├── README.md ├── LICENSE ├── go.sum └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | .goreleaser.yml 3 | .idea 4 | dist 5 | out* -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/j3ssie/go-auxs/cinfo 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/genkiroid/cert v0.0.0-20191007122723-897560fbbe50 7 | github.com/json-iterator/go v1.1.12 8 | golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 9 | ) 10 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: j3ssie 5 | open_collective: osmedeus 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: [ 'https://paypal.me/j3ssiejjj', 'https://www.buymeacoffee.com/j3ssie' ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Cinfo 2 | 3 | Extract domain from SSL Information 4 | 5 | ## Install 6 | 7 | ```shell 8 | GO111MODULE=on go get -u github.com/j3ssie/cinfo 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```shell 14 | # Basic Usage 15 | echo '1.2.3.4:443' | cinfo 16 | echo '1.2.3.4:443' | cinfo -v -json 17 | 18 | 19 | # probe for common SSL ports like 443 and 8443 too 20 | echo '1.2.3.4' | cinfo -e 21 | 22 | # get alexa rank of IPs / domains 23 | echo '1.2.3.4' | cinfo -e -a 24 | echo 'sub.example.com' | cinfo -e -a 25 | ``` 26 | 27 | ## Donation 28 | 29 | [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://paypal.me/j3ssiejjj) 30 | 31 | [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/j3ssie) 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 j3ssie 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 | 23 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/genkiroid/cert v0.0.0-20191007122723-897560fbbe50 h1:vLwmYBduhnWWqShoUGbVgDulhcLdanoYtCQxYMzwaqQ= 5 | github.com/genkiroid/cert v0.0.0-20191007122723-897560fbbe50/go.mod h1:Pb7nyGYAfDyE/IkU6AJeRshIFko0wJC9cOqeYzYQffk= 6 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 7 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 8 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 9 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 10 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 11 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 12 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 13 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 14 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 15 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 16 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 17 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 18 | golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4 h1:DZshvxDdVoeKIbudAdFEKi+f70l51luSy/7b76ibTY0= 19 | golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 20 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 21 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 22 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 23 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 24 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 25 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "crypto/tls" 6 | "encoding/xml" 7 | "flag" 8 | "fmt" 9 | "golang.org/x/net/publicsuffix" 10 | "io/ioutil" 11 | "net/http" 12 | "net/url" 13 | "os" 14 | "sort" 15 | "strings" 16 | "sync" 17 | 18 | "github.com/genkiroid/cert" 19 | jsoniter "github.com/json-iterator/go" 20 | ) 21 | 22 | // Extract domain from SSL info 23 | // cat /tmp/list_of_IP | cinfo -c 100 24 | var ( 25 | verbose bool 26 | alexa bool 27 | extra bool 28 | jsonOutput bool 29 | ports string 30 | concurrency int 31 | ) 32 | 33 | func main() { 34 | // cli arguments 35 | flag.IntVar(&concurrency, "c", 20, "Set the concurrency level") 36 | flag.BoolVar(&jsonOutput, "json", false, "Show Output as Json format") 37 | flag.BoolVar(&alexa, "a", false, "Check Alexa Rank of domain") 38 | flag.BoolVar(&extra, "e", false, "Append common extra HTTPS port too") 39 | flag.StringVar(&ports, "p", "443,8443,9443", "Common extra HTTPS port too (default: 443,8443,9443)") 40 | flag.BoolVar(&verbose, "v", false, "Verbose output") 41 | flag.Parse() 42 | 43 | stat, _ := os.Stdin.Stat() 44 | if (stat.Mode() & os.ModeCharDevice) != 0 { 45 | args := os.Args[1:] 46 | sort.Strings(args) 47 | url := args[len(args)-1] 48 | 49 | hostname := getHostName(url, "") 50 | if !getCerts(hostname) { 51 | getCerts(getHostName(hostname, "443")) 52 | } 53 | os.Exit(0) 54 | } 55 | 56 | var wg sync.WaitGroup 57 | jobs := make(chan string, concurrency) 58 | 59 | for i := 0; i < concurrency; i++ { 60 | wg.Add(1) 61 | go func() { 62 | defer wg.Done() 63 | for job := range jobs { 64 | hostname := getHostName(job, "") 65 | if hostname != "" { 66 | if extra { 67 | if strings.Contains(hostname, ":") { 68 | hostname = strings.Split(hostname, ":")[0] 69 | } 70 | hostnames := moreHosts(hostname) 71 | for _, host := range hostnames { 72 | getCerts(host) 73 | } 74 | } 75 | if !getCerts(hostname) { 76 | getCerts(getHostName(job, "443")) 77 | } 78 | } 79 | } 80 | }() 81 | } 82 | 83 | sc := bufio.NewScanner(os.Stdin) 84 | go func() { 85 | for sc.Scan() { 86 | url := strings.TrimSpace(sc.Text()) 87 | if err := sc.Err(); err == nil && url != "" { 88 | jobs <- url 89 | } 90 | } 91 | close(jobs) 92 | }() 93 | wg.Wait() 94 | } 95 | 96 | func moreHosts(raw string) []string { 97 | var result []string 98 | mports := strings.Split(raw, ",") 99 | for _, mport := range mports { 100 | result = append(result, fmt.Sprintf("%s:%s", raw, mport)) 101 | } 102 | return result 103 | } 104 | 105 | func getHostName(raw string, port string) string { 106 | if !strings.HasPrefix(raw, "http") { 107 | raw = "https://" + raw 108 | } 109 | u, err := url.Parse(raw) 110 | if err != nil { 111 | fmt.Println(err) 112 | return "" 113 | } 114 | var hostname string 115 | if port != "" { 116 | return u.Hostname() + ":" + port 117 | } 118 | 119 | if u.Port() == "" { 120 | hostname = u.Hostname() 121 | } else { 122 | hostname = u.Hostname() + ":" + u.Port() 123 | } 124 | return hostname 125 | } 126 | 127 | type CertInfo struct { 128 | Input string `json:"input"` 129 | Domains []string `json:"domains"` 130 | Info string `json:"info"` 131 | } 132 | 133 | func getCerts(raw string) bool { 134 | var certs cert.Certs 135 | var err error 136 | var rank string 137 | 138 | cert.SkipVerify = true 139 | 140 | certs, err = cert.NewCerts([]string{raw}) 141 | if err != nil { 142 | return false 143 | } 144 | 145 | certInfo := CertInfo{ 146 | Input: raw, 147 | } 148 | 149 | for _, certItem := range certs { 150 | if verbose { 151 | info, err := GetCertificatesInfo(raw) 152 | certInfo.Info = info 153 | if err == nil { 154 | if !jsonOutput { 155 | fmt.Printf("%s - %s\n", raw, info) 156 | } 157 | } 158 | } 159 | 160 | for _, domain := range certItem.SANs { 161 | data := domain 162 | if alexa { 163 | rank, _ = getAlexaRank(domain) 164 | data = fmt.Sprintf("%v,%v,%s", raw, domain, rank) 165 | } else if !jsonOutput { 166 | data = fmt.Sprintf("%v,%v", raw, domain) 167 | } 168 | 169 | if jsonOutput { 170 | certInfo.Domains = append(certInfo.Domains, data) 171 | } else { 172 | fmt.Println(data) 173 | } 174 | } 175 | } 176 | 177 | if jsonOutput { 178 | if data, err := jsoniter.MarshalToString(certInfo); err == nil { 179 | fmt.Println(data) 180 | } 181 | } 182 | 183 | return true 184 | 185 | } 186 | 187 | func getAlexaRank(raw string) (string, error) { 188 | rank := "-1" 189 | 190 | if strings.Contains(raw, "*.") { 191 | raw = strings.ReplaceAll(raw, "*.", "") 192 | } 193 | 194 | // sub.example.com --> example.com 195 | suffix, ok := publicsuffix.PublicSuffix(raw) 196 | if ok { 197 | root := strings.ReplaceAll(raw, fmt.Sprintf(".%s", suffix), "") 198 | if strings.Contains(root, ".") { 199 | parts := strings.Split(root, ".") 200 | root = parts[len(parts)-1] 201 | raw = fmt.Sprintf("%s.%s", root, suffix) 202 | } 203 | } 204 | 205 | resp, err := http.Get("http://data.alexa.com/data?cli=10&dat=snbamz&url=" + raw) 206 | if err != nil { 207 | return rank, err 208 | } 209 | 210 | defer resp.Body.Close() 211 | alexaData, err := ioutil.ReadAll(resp.Body) 212 | if err != nil { 213 | return rank, err 214 | } 215 | 216 | decoder := xml.NewDecoder(strings.NewReader(string(alexaData))) 217 | for { 218 | token, _ := decoder.Token() 219 | if token == nil { 220 | break 221 | } 222 | 223 | switch startElement := token.(type) { 224 | case xml.StartElement: 225 | if startElement.Name.Local == "POPULARITY" { 226 | if len(startElement.Attr) >= 2 { 227 | rank = startElement.Attr[1].Value 228 | } 229 | } 230 | } 231 | } 232 | return rank, nil 233 | } 234 | 235 | func GetCertificatesInfo(address string) (string, error) { 236 | if !strings.Contains(address, ":") { 237 | address = fmt.Sprintf("%s:443", address) 238 | } 239 | conn, err := tls.Dial("tcp", address, &tls.Config{ 240 | InsecureSkipVerify: true, 241 | }) 242 | if err != nil { 243 | return "", err 244 | } 245 | defer conn.Close() 246 | return fmt.Sprintf("%v", conn.ConnectionState().PeerCertificates[0].Subject), nil 247 | } 248 | --------------------------------------------------------------------------------