Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/common/uncover/uncover.go
2072 views
1
package uncover
2
3
import (
4
"context"
5
"fmt"
6
"runtime"
7
"strings"
8
9
"github.com/projectdiscovery/gologger"
10
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
11
"github.com/projectdiscovery/uncover"
12
"github.com/projectdiscovery/uncover/sources"
13
mapsutil "github.com/projectdiscovery/utils/maps"
14
sliceutil "github.com/projectdiscovery/utils/slice"
15
stringsutil "github.com/projectdiscovery/utils/strings"
16
)
17
18
// returns csv string of uncover supported agents
19
func GetUncoverSupportedAgents() string {
20
u, _ := uncover.New(&uncover.Options{})
21
return strings.Join(u.AllAgents(), ",")
22
}
23
24
// GetTargetsFromUncover returns targets from uncover
25
func GetTargetsFromUncover(ctx context.Context, outputFormat string, opts *uncover.Options) (chan string, error) {
26
u, err := uncover.New(opts)
27
if err != nil {
28
return nil, err
29
}
30
resChan, err := u.Execute(ctx)
31
if err != nil {
32
return nil, err
33
}
34
outputChan := make(chan string) // buffered channel
35
go func() {
36
defer close(outputChan)
37
for {
38
select {
39
case <-ctx.Done():
40
return
41
case res, ok := <-resChan:
42
if !ok {
43
return
44
}
45
if res.Error != nil {
46
// only log in verbose mode
47
gologger.Verbose().Msgf("uncover: %v", res.Error)
48
continue
49
}
50
outputChan <- processUncoverOutput(res, outputFormat)
51
}
52
}
53
}()
54
return outputChan, nil
55
}
56
57
// processUncoverOutput returns output string depending on uncover field
58
func processUncoverOutput(result sources.Result, outputFormat string) string {
59
if (result.IP == "" || result.Port == 0) && stringsutil.ContainsAny(outputFormat, "ip", "port") {
60
// if ip or port is not present, fallback to using host
61
outputFormat = "host"
62
}
63
replacer := strings.NewReplacer(
64
"ip", result.IP,
65
"host", result.Host,
66
"port", fmt.Sprint(result.Port),
67
"url", result.Url,
68
)
69
return replacer.Replace(outputFormat)
70
}
71
72
// GetUncoverTargetsFromMetadata returns targets from uncover metadata
73
func GetUncoverTargetsFromMetadata(ctx context.Context, templates []*templates.Template, outputFormat string, opts *uncover.Options) chan string {
74
// contains map[engine]queries
75
queriesMap := make(map[string][]string)
76
for _, template := range templates {
77
innerLoop:
78
for k, v := range template.Info.Metadata {
79
if !strings.HasSuffix(k, "-query") {
80
// this is not a query
81
// query keys are like shodan-query, fofa-query, etc
82
continue innerLoop
83
}
84
engine := strings.TrimSuffix(k, "-query")
85
if queriesMap[engine] == nil {
86
queriesMap[engine] = []string{}
87
}
88
switch v := v.(type) {
89
case []interface{}:
90
qs := queriesMap[engine]
91
for _, vv := range v {
92
qs = append(qs, fmt.Sprint(vv))
93
}
94
queriesMap[engine] = qs
95
default:
96
queriesMap[engine] = append(queriesMap[engine], fmt.Sprint(v))
97
}
98
}
99
}
100
for engine, queries := range queriesMap {
101
queriesMap[engine] = sliceutil.Dedupe(queries)
102
}
103
keys := mapsutil.GetKeys(queriesMap)
104
gologger.Info().Msgf("Running uncover queries from template against: %s", strings.Join(keys, ","))
105
result := make(chan string, runtime.NumCPU())
106
go func() {
107
defer close(result)
108
// unfortunately uncover doesn't support execution of map[engine]queries
109
// if queries are given they are executed against all engines which is not what we want
110
// TODO: add support for map[engine]queries in uncover
111
// Note below implementation is intentionally sequential to avoid burning all the API keys
112
counter := 0
113
outerLoop:
114
for eng, queries := range queriesMap {
115
if opts.Limit > 0 && counter >= opts.Limit {
116
break
117
}
118
// create new uncover options for each engine
119
uncoverOpts := &uncover.Options{
120
Agents: []string{eng},
121
Queries: queries,
122
Limit: opts.Limit,
123
MaxRetry: opts.MaxRetry,
124
Timeout: opts.Timeout,
125
RateLimit: opts.RateLimit,
126
RateLimitUnit: opts.RateLimitUnit,
127
}
128
ch, err := GetTargetsFromUncover(ctx, outputFormat, uncoverOpts)
129
if err != nil {
130
gologger.Error().Msgf("Could not get targets using %v engine from uncover: %s", eng, err)
131
return
132
}
133
134
innerLoop:
135
for {
136
select {
137
case <-ctx.Done():
138
return
139
case res, ok := <-ch:
140
if !ok {
141
continue outerLoop
142
}
143
result <- res
144
counter++
145
if opts.Limit > 0 && counter >= opts.Limit {
146
break innerLoop
147
}
148
}
149
}
150
}
151
}()
152
return result
153
}
154
155