Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/dns/request.go
2070 views
1
package dns
2
3
import (
4
"encoding/hex"
5
"fmt"
6
maps0 "maps"
7
"strings"
8
"sync"
9
10
"github.com/miekg/dns"
11
"github.com/pkg/errors"
12
"go.uber.org/multierr"
13
"golang.org/x/exp/maps"
14
15
"github.com/projectdiscovery/gologger"
16
"github.com/projectdiscovery/nuclei/v3/pkg/output"
17
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
18
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
19
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions"
20
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
21
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/eventcreator"
22
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/responsehighlighter"
23
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/vardump"
24
protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
25
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
26
"github.com/projectdiscovery/retryabledns"
27
iputil "github.com/projectdiscovery/utils/ip"
28
syncutil "github.com/projectdiscovery/utils/sync"
29
)
30
31
var _ protocols.Request = &Request{}
32
33
// Type returns the type of the protocol request
34
func (request *Request) Type() templateTypes.ProtocolType {
35
return templateTypes.DNSProtocol
36
}
37
38
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
39
func (request *Request) ExecuteWithResults(input *contextargs.Context, metadata, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
40
var err error
41
domain, err := request.parseDNSInput(input.MetaInput.Input)
42
if err != nil {
43
return errors.Wrap(err, "could not build request")
44
}
45
46
vars := protocolutils.GenerateDNSVariables(domain)
47
// optionvars are vars passed from CLI or env variables
48
optionVars := generators.BuildPayloadFromOptions(request.options.Options)
49
// merge with metadata (eg. from workflow context)
50
if request.options.HasTemplateCtx(input.MetaInput) {
51
vars = generators.MergeMaps(vars, metadata, optionVars, request.options.GetTemplateCtx(input.MetaInput).GetAll())
52
}
53
variablesMap := request.options.Variables.Evaluate(vars)
54
vars = generators.MergeMaps(vars, variablesMap, request.options.Constants)
55
56
// if request threads matches global payload concurrency we follow it
57
shouldFollowGlobal := request.Threads == request.options.Options.PayloadConcurrency
58
59
if request.generator != nil {
60
iterator := request.generator.NewIterator()
61
swg, err := syncutil.New(syncutil.WithSize(request.Threads))
62
if err != nil {
63
return err
64
}
65
var multiErr error
66
m := &sync.Mutex{}
67
68
for {
69
value, ok := iterator.Value()
70
if !ok {
71
break
72
}
73
74
select {
75
case <-input.Context().Done():
76
return input.Context().Err()
77
default:
78
}
79
80
// resize check point - nop if there are no changes
81
if shouldFollowGlobal && swg.Size != request.options.Options.PayloadConcurrency {
82
if err := swg.Resize(input.Context(), request.options.Options.PayloadConcurrency); err != nil {
83
return err
84
}
85
}
86
87
value = generators.MergeMaps(vars, value)
88
swg.Add()
89
go func(newVars map[string]interface{}) {
90
defer swg.Done()
91
if err := request.execute(input, domain, metadata, previous, newVars, callback); err != nil {
92
m.Lock()
93
multiErr = multierr.Append(multiErr, err)
94
m.Unlock()
95
}
96
}(value)
97
}
98
swg.Wait()
99
if multiErr != nil {
100
return multiErr
101
}
102
} else {
103
value := maps.Clone(vars)
104
return request.execute(input, domain, metadata, previous, value, callback)
105
}
106
return nil
107
}
108
109
func (request *Request) execute(input *contextargs.Context, domain string, metadata, previous output.InternalEvent, vars map[string]interface{}, callback protocols.OutputEventCallback) error {
110
var err error
111
if vardump.EnableVarDump {
112
gologger.Debug().Msgf("DNS Protocol request variables: %s\n", vardump.DumpVariables(vars))
113
}
114
115
// Compile each request for the template based on the URL
116
compiledRequest, err := request.Make(domain, vars)
117
if err != nil {
118
request.options.Output.Request(request.options.TemplatePath, domain, request.Type().String(), err)
119
request.options.Progress.IncrementFailedRequestsBy(1)
120
return errors.Wrap(err, "could not build request")
121
}
122
123
dnsClient := request.dnsClient
124
if varErr := expressions.ContainsUnresolvedVariables(request.Resolvers...); varErr != nil {
125
if dnsClient, varErr = request.getDnsClient(request.options, metadata); varErr != nil {
126
gologger.Warning().Msgf("[%s] Could not make dns request for %s: %v\n", request.options.TemplateID, domain, varErr)
127
return nil
128
}
129
}
130
question := domain
131
if len(compiledRequest.Question) > 0 {
132
question = compiledRequest.Question[0].Name
133
}
134
// remove the last dot
135
domain = strings.TrimSuffix(domain, ".")
136
question = strings.TrimSuffix(question, ".")
137
138
requestString := compiledRequest.String()
139
if varErr := expressions.ContainsUnresolvedVariables(requestString); varErr != nil {
140
gologger.Warning().Msgf("[%s] Could not make dns request for %s: %v\n", request.options.TemplateID, question, varErr)
141
return nil
142
}
143
if request.options.Options.Debug || request.options.Options.DebugRequests || request.options.Options.StoreResponse {
144
msg := fmt.Sprintf("[%s] Dumped DNS request for %s", request.options.TemplateID, question)
145
if request.options.Options.Debug || request.options.Options.DebugRequests {
146
gologger.Info().Str("domain", domain).Msg(msg)
147
gologger.Print().Msgf("%s", requestString)
148
}
149
if request.options.Options.StoreResponse {
150
request.options.Output.WriteStoreDebugData(domain, request.options.TemplateID, request.Type().String(), fmt.Sprintf("%s\n%s", msg, requestString))
151
}
152
}
153
154
request.options.RateLimitTake()
155
156
// Send the request to the target servers
157
response, err := dnsClient.Do(compiledRequest)
158
if err != nil {
159
request.options.Output.Request(request.options.TemplatePath, domain, request.Type().String(), err)
160
request.options.Progress.IncrementFailedRequestsBy(1)
161
} else {
162
request.options.Progress.IncrementRequests()
163
}
164
if response == nil {
165
return errors.Wrap(err, "could not send dns request")
166
}
167
168
request.options.Output.Request(request.options.TemplatePath, domain, request.Type().String(), err)
169
gologger.Verbose().Msgf("[%s] Sent DNS request to %s\n", request.options.TemplateID, question)
170
171
// perform trace if necessary
172
var traceData *retryabledns.TraceData
173
if request.Trace {
174
traceData, err = request.dnsClient.Trace(domain, request.question, request.TraceMaxRecursion)
175
if err != nil {
176
request.options.Output.Request(request.options.TemplatePath, domain, "dns", err)
177
}
178
}
179
180
// Create the output event
181
outputEvent := request.responseToDSLMap(compiledRequest, response, domain, question, traceData)
182
// expose response variables in proto_var format
183
// this is no-op if the template is not a multi protocol template
184
request.options.AddTemplateVars(input.MetaInput, request.Type(), request.ID, outputEvent)
185
maps0.Copy(outputEvent, previous)
186
maps0.Copy(outputEvent, vars)
187
// add variables from template context before matching/extraction
188
if request.options.HasTemplateCtx(input.MetaInput) {
189
outputEvent = generators.MergeMaps(outputEvent, request.options.GetTemplateCtx(input.MetaInput).GetAll())
190
}
191
event := eventcreator.CreateEvent(request, outputEvent, request.options.Options.Debug || request.options.Options.DebugResponse)
192
193
dumpResponse(event, request, request.options, response.String(), question)
194
if request.Trace {
195
dumpTraceData(event, request.options, traceToString(traceData, true), question)
196
}
197
198
callback(event)
199
return err
200
}
201
202
func (request *Request) parseDNSInput(host string) (string, error) {
203
isIP := iputil.IsIP(host)
204
switch {
205
case request.question == dns.TypePTR && isIP:
206
var err error
207
host, err = dns.ReverseAddr(host)
208
if err != nil {
209
return "", err
210
}
211
default:
212
if isIP {
213
return "", errors.New("cannot use IP address as DNS input")
214
}
215
host = dns.Fqdn(host)
216
}
217
return host, nil
218
}
219
220
func dumpResponse(event *output.InternalWrappedEvent, request *Request, _ *protocols.ExecutorOptions, response, domain string) {
221
cliOptions := request.options.Options
222
if cliOptions.Debug || cliOptions.DebugResponse || cliOptions.StoreResponse {
223
hexDump := false
224
if responsehighlighter.HasBinaryContent(response) {
225
hexDump = true
226
response = hex.Dump([]byte(response))
227
}
228
highlightedResponse := responsehighlighter.Highlight(event.OperatorsResult, response, cliOptions.NoColor, hexDump)
229
msg := fmt.Sprintf("[%s] Dumped DNS response for %s\n\n%s", request.options.TemplateID, domain, highlightedResponse)
230
if cliOptions.Debug || cliOptions.DebugResponse {
231
gologger.Debug().Msg(msg)
232
}
233
if cliOptions.StoreResponse {
234
request.options.Output.WriteStoreDebugData(domain, request.options.TemplateID, request.Type().String(), msg)
235
}
236
}
237
}
238
239
func dumpTraceData(event *output.InternalWrappedEvent, requestOptions *protocols.ExecutorOptions, traceData, domain string) {
240
cliOptions := requestOptions.Options
241
if cliOptions.Debug || cliOptions.DebugResponse {
242
hexDump := false
243
if responsehighlighter.HasBinaryContent(traceData) {
244
hexDump = true
245
traceData = hex.Dump([]byte(traceData))
246
}
247
highlightedResponse := responsehighlighter.Highlight(event.OperatorsResult, traceData, cliOptions.NoColor, hexDump)
248
gologger.Debug().Msgf("[%s] Dumped DNS Trace data for %s\n\n%s", requestOptions.TemplateID, domain, highlightedResponse)
249
}
250
}
251
252