/
zoomeye.go
148 lines (136 loc) · 3.9 KB
/
zoomeye.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package zoomeye
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"github.com/projectdiscovery/subfinder/pkg/subscraping"
)
// zoomAuth holds the ZoomEye credentials
type zoomAuth struct {
User string `json:"username"`
Pass string `json:"password"`
}
type loginResp struct {
JWT string `json:"access_token"`
}
// search results
type zoomeyeResults struct {
Matches []struct {
Site string `json:"site"`
Domains []string `json:"domains"`
} `json:"matches"`
}
// Source is the passive scraping agent
type Source struct{}
// Run function returns all subdomains found with the service
func (s *Source) Run(ctx context.Context, domain string, session *subscraping.Session) <-chan subscraping.Result {
results := make(chan subscraping.Result)
go func() {
if session.Keys.ZoomEyeUsername == "" || session.Keys.ZoomEyePassword == "" {
close(results)
return
}
jwt, err := doLogin(session)
if err != nil {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
close(results)
return
}
// check if jwt is null
if jwt == "" {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: errors.New("could not log into zoomeye")}
close(results)
return
}
headers := map[string]string{
"Authorization": fmt.Sprintf("JWT %s", jwt),
"Accept": "application/json",
"Content-Type": "application/json",
}
for currentPage := 0; currentPage <= 100; currentPage++ {
api := fmt.Sprintf("https://api.zoomeye.org/web/search?query=hostname:%s&page=%d", domain, currentPage)
resp, err := session.Get(ctx, api, "", headers)
if err != nil {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
close(results)
return
}
// if response is non-200 & current page is 0 return an error
if resp.StatusCode != 200 && currentPage == 0 {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: fmt.Errorf("invalid status code received: %d", resp.StatusCode)}
io.Copy(ioutil.Discard, resp.Body)
resp.Body.Close()
close(results)
return
}
// otherwise we hit 403 when there are no more results
if resp.StatusCode != 200 {
io.Copy(ioutil.Discard, resp.Body)
resp.Body.Close()
close(results)
return
}
defer resp.Body.Close()
res := &zoomeyeResults{}
err = json.NewDecoder(resp.Body).Decode(res)
if err != nil {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Error, Error: err}
resp.Body.Close()
close(results)
return
}
resp.Body.Close()
for _, r := range res.Matches {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Subdomain, Value: r.Site}
for _, domain := range r.Domains {
results <- subscraping.Result{Source: s.Name(), Type: subscraping.Subdomain, Value: domain}
}
}
currentPage++
}
close(results)
}()
return results
}
// doLogin performs authentication on the ZoomEye API
func doLogin(session *subscraping.Session) (string, error) {
creds := &zoomAuth{
User: session.Keys.ZoomEyeUsername,
Pass: session.Keys.ZoomEyePassword,
}
body, err := json.Marshal(&creds)
if err != nil {
return "", err
}
req, err := http.NewRequest("POST", "https://api.zoomeye.org/user/login", bytes.NewBuffer(body))
if err != nil {
return "", err
}
req.Header.Add("Content-Type", "application/json")
resp, err := session.Client.Do(req)
if err != nil {
return "", err
}
// if not 200, bad credentials
if resp.StatusCode != 200 {
io.Copy(ioutil.Discard, resp.Body)
resp.Body.Close()
return "", fmt.Errorf("login failed, non-200 response from zoomeye")
}
defer resp.Body.Close()
login := &loginResp{}
err = json.NewDecoder(resp.Body).Decode(login)
if err != nil {
return "", err
}
return login.JWT, nil
}
// Name returns the name of the source
func (s *Source) Name() string {
return "zoomeye"
}