Skip to content

Commit b5e028b

Browse files
committed
inital commit
0 parents  commit b5e028b

File tree

2 files changed

+196
-0
lines changed

2 files changed

+196
-0
lines changed

main.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package main
2+
3+
import (
4+
"crypto/tls"
5+
"crypto/x509"
6+
"flag"
7+
"fmt"
8+
"io/ioutil"
9+
"log"
10+
"net"
11+
"sort"
12+
"strings"
13+
"syscall"
14+
"time"
15+
)
16+
17+
// TODO follow https redirects?
18+
19+
// vars
20+
var conf = &tls.Config{
21+
InsecureSkipVerify: true,
22+
}
23+
var markedDomains = make(map[string]bool)
24+
var domainGraph = make(map[string][]string)
25+
var timeout time.Duration
26+
var port string
27+
var quiet bool
28+
var depth int
29+
var maxDepth int
30+
31+
func checkErr(err error) bool {
32+
if err == nil {
33+
return false
34+
35+
} else if netError, ok := err.(net.Error); ok && netError.Timeout() {
36+
log.Println("Timeout")
37+
} else {
38+
switch t := err.(type) {
39+
case *net.OpError:
40+
if t.Op == "dial" {
41+
log.Println("Unknown host")
42+
} else if t.Op == "read" {
43+
log.Println("Connection refused")
44+
}
45+
46+
case syscall.Errno:
47+
if t == syscall.ECONNREFUSED {
48+
log.Println("Connection refused")
49+
}
50+
}
51+
}
52+
return true
53+
}
54+
55+
/*
56+
* given a domain returns the non-wildecard version of that domain
57+
*/
58+
func directDomain(domain string) string {
59+
if domain[0:2] == "*." {
60+
domain = domain[2:]
61+
}
62+
return domain
63+
}
64+
65+
func printGraph() {
66+
// print map in sorted order
67+
domains := make([]string, 0, len(domainGraph))
68+
for domain, _ := range domainGraph {
69+
domains = append(domains, domain)
70+
}
71+
sort.Strings(domains)
72+
73+
for _, domain := range domains {
74+
fmt.Println(domain, domainGraph[domain])
75+
}
76+
}
77+
78+
func main() {
79+
log.SetFlags(0)
80+
host := flag.String("host", "localhost", "Host to Scan")
81+
flag.StringVar(&port, "port", "443", "Port to connect to")
82+
timeoutPtr := flag.Int("timeout", 5, "TCP Timeout in seconds")
83+
flag.BoolVar(&quiet, "quiet", false, "Do not print domains as they are visited")
84+
flag.IntVar(&maxDepth, "depth", 20, "Maximum BFS Depth to go")
85+
// TODO threads
86+
87+
flag.Parse()
88+
if quiet {
89+
log.SetOutput(ioutil.Discard)
90+
}
91+
timeout = time.Duration(*timeoutPtr) * time.Second
92+
93+
startDomain := strings.ToLower(*host)
94+
95+
BFS(startDomain)
96+
97+
log.Println("Done...")
98+
99+
printGraph()
100+
101+
log.Println("Found", len(domainGraph), "domains")
102+
log.Println("Graph Depth:", depth)
103+
104+
}
105+
106+
func BFS(root string) {
107+
var domainQueue = make(Queue, 0)
108+
domainQueue.Push(&Node{root, 0})
109+
markedDomains[directDomain(root)] = true
110+
111+
for domainQueue.Len() > 0 {
112+
domainNode := domainQueue.Pop()
113+
if domainNode.Depth > maxDepth {
114+
log.Println("Max Depth Reached, skipping:", domainNode.Domain)
115+
continue
116+
}
117+
if domainNode.Depth > depth {
118+
depth = domainNode.Depth
119+
}
120+
domain := directDomain(domainNode.Domain)
121+
neighbors := BFSPeers(domain) // visit
122+
domainGraph[domain] = neighbors
123+
for _, neighbor := range neighbors {
124+
directNeighbor := directDomain(neighbor)
125+
if !markedDomains[directNeighbor] {
126+
markedDomains[directNeighbor] = true
127+
domainQueue.Push(&Node{neighbor, domainNode.Depth + 1})
128+
}
129+
}
130+
}
131+
}
132+
133+
func BFSPeers(host string) []string {
134+
log.Println("Visiting", host)
135+
domains := make([]string, 0)
136+
certs := getPeerCerts(host)
137+
138+
if len(certs) == 0 {
139+
return domains
140+
}
141+
142+
// used to ensure uniq entries in domains array
143+
domainMap := make(map[string]bool)
144+
145+
cn := strings.ToLower(certs[0].Subject.CommonName)
146+
domainMap[cn] = true
147+
148+
for _, cert := range certs {
149+
for _, domain := range cert.DNSNames {
150+
domain = strings.ToLower(domain)
151+
domainMap[domain] = true
152+
}
153+
}
154+
155+
for domain, _ := range domainMap {
156+
domains = append(domains, domain)
157+
}
158+
sort.Strings(domains)
159+
return domains
160+
161+
}
162+
163+
func getPeerCerts(host string) []*x509.Certificate {
164+
addr := net.JoinHostPort(host, port)
165+
dialer := &net.Dialer{Timeout: timeout}
166+
conn, err := tls.DialWithDialer(dialer, "tcp", addr, conf)
167+
if checkErr(err) {
168+
return make([]*x509.Certificate, 0)
169+
170+
}
171+
conn.Close()
172+
connState := conn.ConnectionState()
173+
return connState.PeerCertificates
174+
}

queue.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package main
2+
3+
type Node struct {
4+
Domain string
5+
Depth int
6+
}
7+
8+
type Queue []*Node
9+
10+
func (q *Queue) Push(n *Node) {
11+
*q = append(*q, n)
12+
}
13+
14+
func (q *Queue) Pop() (n *Node) {
15+
n = (*q)[0]
16+
*q = (*q)[1:]
17+
return
18+
}
19+
20+
func (q *Queue) Len() int {
21+
return len(*q)
22+
}

0 commit comments

Comments
 (0)