Skip to content

Commit ee7929f

Browse files
authoredNov 4, 2016
Merge pull request getlantern#5396 from getlantern/cfscanner
Cfscanner
2 parents 73e0376 + 3794255 commit ee7929f

File tree

1 file changed

+239
-0
lines changed

1 file changed

+239
-0
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
package main
2+
3+
import (
4+
"crypto/tls"
5+
"crypto/x509"
6+
"encoding/json"
7+
"flag"
8+
"fmt"
9+
"io/ioutil"
10+
"net"
11+
"net/http"
12+
"os"
13+
"strings"
14+
"sync"
15+
"time"
16+
17+
"github.com/getlantern/golog"
18+
"github.com/getlantern/keyman"
19+
)
20+
21+
var (
22+
log = golog.LoggerFor("cfscanner")
23+
help = flag.Bool("help", false, "Get usage help")
24+
numWorkers = flag.Int("workers", 1, "Number of concurrent workers")
25+
out = flag.String("o", "masquerades.txt", "Output file")
26+
)
27+
28+
func webSlurp(url string) ([]byte, error) {
29+
resp, err := http.Get(url)
30+
if err != nil {
31+
return nil, fmt.Errorf("Error fetching IP list: %v", err)
32+
}
33+
defer resp.Body.Close()
34+
body, err := ioutil.ReadAll(resp.Body)
35+
if err != nil {
36+
return nil, fmt.Errorf("Error trying to read response: %v", err)
37+
}
38+
return body, nil
39+
}
40+
41+
type castat struct {
42+
CommonName string
43+
Cert string
44+
freq float64
45+
}
46+
47+
func main() {
48+
main_()
49+
}
50+
51+
type cloudfrontPrefix struct {
52+
Ip_prefix string
53+
Region string
54+
Service string
55+
}
56+
57+
func main_() {
58+
flag.Parse()
59+
60+
if *help {
61+
flag.Usage()
62+
os.Exit(1)
63+
}
64+
65+
bs, err := webSlurp("https://ip-ranges.amazonaws.com/ip-ranges.json")
66+
if err != nil {
67+
log.Fatal(err)
68+
}
69+
var objmap map[string]*json.RawMessage
70+
if err = json.Unmarshal(bs, &objmap); err != nil {
71+
log.Fatal(err)
72+
}
73+
var prefixes []cloudfrontPrefix
74+
if err = json.Unmarshal(*objmap["prefixes"], &prefixes); err != nil {
75+
log.Fatal(err)
76+
}
77+
78+
ipch := make(chan string)
79+
ipwg := sync.WaitGroup{}
80+
81+
for _, prefix := range prefixes {
82+
if prefix.Service == "CLOUDFRONT" {
83+
ipwg.Add(1)
84+
go enumerateRange(prefix.Ip_prefix, ipch, &ipwg)
85+
}
86+
}
87+
88+
// Send death pill to all workers when we're done feeding IPs.
89+
go func() {
90+
ipwg.Wait()
91+
for i := 0; i < *numWorkers; i++ {
92+
ipch <- ""
93+
}
94+
}()
95+
96+
reswg := sync.WaitGroup{}
97+
reswg.Add(*numWorkers)
98+
resch := make(chan string)
99+
go func() {
100+
reswg.Wait()
101+
resch <- ""
102+
}()
103+
for i := 0; i < *numWorkers; i++ {
104+
go checkIPs(ipch, resch, &reswg)
105+
}
106+
107+
f, err := os.Create(*out)
108+
if err != nil {
109+
log.Fatal(err)
110+
}
111+
defer f.Close()
112+
for result := range resch {
113+
if result == "" {
114+
fmt.Println("Done!")
115+
break
116+
}
117+
fmt.Printf("*** Successfully verified %v\n", result)
118+
_, err = f.WriteString(result + "\n")
119+
if err != nil {
120+
log.Fatal(err)
121+
}
122+
f.Sync()
123+
}
124+
}
125+
126+
func checkIPs(ipch <-chan string, resch chan<- string, wg *sync.WaitGroup) {
127+
defer wg.Done()
128+
for ip := range ipch {
129+
if ip == "" {
130+
break
131+
}
132+
domain, err := checkIP(ip)
133+
if err == nil {
134+
resch <- domain
135+
} else {
136+
log.Errorf("Error checking %v: %v\n", ip, err)
137+
}
138+
}
139+
}
140+
141+
func checkIP(ip string) (string, error) {
142+
cfg := tls.Config{InsecureSkipVerify: true}
143+
conn, err := tls.DialWithDialer(
144+
&net.Dialer{Timeout: 5 * time.Second},
145+
"tcp",
146+
ip+":443",
147+
&cfg)
148+
if err != nil {
149+
return "", err
150+
}
151+
cert := conn.ConnectionState().PeerCertificates[0]
152+
153+
// XXX, refactor out DRY: genconfig.go
154+
domain, chain := findVerifiedChain((*cert).DNSNames, conn, &cfg)
155+
if domain == "" {
156+
return "", fmt.Errorf("Couldn't verify any cert chains for %v.", ip)
157+
}
158+
rootCA := chain[len(chain)-1]
159+
_, err = keyman.LoadCertificateFromX509(rootCA)
160+
if err != nil {
161+
return "", fmt.Errorf("Error loading root CA cert: %v", err)
162+
}
163+
return ip + " " + domain, nil
164+
/*
165+
ca := &castat{
166+
CommonName: rootCA.Subject.CommonName,
167+
Cert: strings.Replace(string(rootCert.PEMEncoded()), "\n", "\\n", -1),
168+
}
169+
masq := &masquerade{
170+
Domain: domain,
171+
IpAddress: ip,
172+
RootCA: ca,
173+
}
174+
if verifyMasquerade(masq) {
175+
return domain, nil
176+
} else {
177+
return "", fmt.Errorf("Failed to verify masquerade for %v (%v)", domain, ip)
178+
}
179+
*/
180+
}
181+
182+
func findVerifiedChain(dnsNames []string, conn *tls.Conn, cfg *tls.Config) (string, []*x509.Certificate) {
183+
for _, isWildcard := range []bool{false, true} {
184+
for _, domain := range dnsNames {
185+
if strings.HasPrefix(domain, "*") == isWildcard {
186+
if isWildcard {
187+
domain = "www" + domain[1:]
188+
}
189+
verifiedChains, err := verifyServerCerts(conn, domain, cfg)
190+
if err == nil {
191+
return domain, verifiedChains[0]
192+
}
193+
}
194+
}
195+
}
196+
return "", nil
197+
}
198+
199+
func enumerateRange(cidr string, ch chan<- string, wg *sync.WaitGroup) {
200+
defer wg.Done()
201+
cidrIP, ipnet, err := net.ParseCIDR(cidr)
202+
if err != nil {
203+
log.Fatal(err)
204+
}
205+
for ip := cidrIP.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
206+
if ip.IsGlobalUnicast() {
207+
ch <- ip.String()
208+
}
209+
}
210+
}
211+
212+
func inc(ip net.IP) {
213+
for j := len(ip) - 1; j >= 0; j-- {
214+
ip[j]++
215+
if ip[j] > 0 {
216+
break
217+
}
218+
}
219+
}
220+
221+
// XXX: copy&paste from tlsdialer.go
222+
func verifyServerCerts(conn *tls.Conn, serverName string, config *tls.Config) ([][]*x509.Certificate, error) {
223+
certs := conn.ConnectionState().PeerCertificates
224+
225+
opts := x509.VerifyOptions{
226+
Roots: config.RootCAs,
227+
CurrentTime: time.Now(),
228+
DNSName: serverName,
229+
Intermediates: x509.NewCertPool(),
230+
}
231+
232+
for i, cert := range certs {
233+
if i == 0 {
234+
continue
235+
}
236+
opts.Intermediates.AddCert(cert)
237+
}
238+
return certs[0].Verify(opts)
239+
}

0 commit comments

Comments
 (0)
Please sign in to comment.