Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/dns/dns.go
2070 views
1
package dns
2
3
import (
4
"strings"
5
6
"github.com/miekg/dns"
7
"github.com/pkg/errors"
8
9
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
10
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
11
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions"
12
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
13
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/replacer"
14
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/dns/dnsclientpool"
15
"github.com/projectdiscovery/retryabledns"
16
fileutil "github.com/projectdiscovery/utils/file"
17
)
18
19
// Request contains a DNS protocol request to be made from a template
20
type Request struct {
21
// Operators for the current request go here.
22
operators.Operators `yaml:",inline"`
23
24
// ID is the optional id of the request
25
ID string `yaml:"id,omitempty" json:"id,omitempty" jsonschema:"title=id of the dns request,description=ID is the optional ID of the DNS Request"`
26
27
// description: |
28
// Name is the Hostname to make DNS request for.
29
//
30
// Generally, it is set to {{FQDN}} which is the domain we get from input.
31
// examples:
32
// - value: "\"{{FQDN}}\""
33
Name string `yaml:"name,omitempty" json:"name,omitempty" jsonschema:"title=hostname to make dns request for,description=Name is the Hostname to make DNS request for"`
34
// description: |
35
// RequestType is the type of DNS request to make.
36
RequestType DNSRequestTypeHolder `yaml:"type,omitempty" json:"type,omitempty" jsonschema:"title=type of dns request to make,description=Type is the type of DNS request to make,enum=A,enum=NS,enum=DS,enum=CNAME,enum=SOA,enum=PTR,enum=MX,enum=TXT,enum=AAAA"`
37
// description: |
38
// Class is the class of the DNS request.
39
//
40
// Usually it's enough to just leave it as INET.
41
// values:
42
// - "inet"
43
// - "csnet"
44
// - "chaos"
45
// - "hesiod"
46
// - "none"
47
// - "any"
48
Class string `yaml:"class,omitempty" json:"class,omitempty" jsonschema:"title=class of DNS request,description=Class is the class of the DNS request,enum=inet,enum=csnet,enum=chaos,enum=hesiod,enum=none,enum=any"`
49
// description: |
50
// Retries is the number of retries for the DNS request
51
// examples:
52
// - name: Use a retry of 3 to 5 generally
53
// value: 5
54
Retries int `yaml:"retries,omitempty" json:"retries,omitempty" jsonschema:"title=retries for dns request,description=Retries is the number of retries for the DNS request"`
55
// description: |
56
// Trace performs a trace operation for the target.
57
Trace bool `yaml:"trace,omitempty" json:"trace,omitempty" jsonschema:"title=trace operation,description=Trace performs a trace operation for the target."`
58
// description: |
59
// TraceMaxRecursion is the number of max recursion allowed for trace operations
60
// examples:
61
// - name: Use a retry of 100 to 150 generally
62
// value: 100
63
TraceMaxRecursion int `yaml:"trace-max-recursion,omitempty" json:"trace-max-recursion,omitempty" jsonschema:"title=trace-max-recursion level for dns request,description=TraceMaxRecursion is the number of max recursion allowed for trace operations"`
64
65
// description: |
66
// Attack is the type of payload combinations to perform.
67
//
68
// Batteringram is inserts the same payload into all defined payload positions at once, pitchfork combines multiple payload sets and clusterbomb generates
69
// permutations and combinations for all payloads.
70
AttackType generators.AttackTypeHolder `yaml:"attack,omitempty" json:"attack,omitempty" jsonschema:"title=attack is the payload combination,description=Attack is the type of payload combinations to perform,enum=batteringram,enum=pitchfork,enum=clusterbomb"`
71
// description: |
72
// Payloads contains any payloads for the current request.
73
//
74
// Payloads support both key-values combinations where a list
75
// of payloads is provided, or optionally a single file can also
76
// be provided as payload which will be read on run-time.
77
Payloads map[string]interface{} `yaml:"payloads,omitempty" json:"payloads,omitempty" jsonschema:"title=payloads for the network request,description=Payloads contains any payloads for the current request"`
78
// description: |
79
// Threads to use when sending iterating over payloads
80
// examples:
81
// - name: Send requests using 10 concurrent threads
82
// value: 10
83
Threads int `yaml:"threads,omitempty" json:"threads,omitempty" jsonschema:"title=threads for sending requests,description=Threads specifies number of threads to use sending requests. This enables Connection Pooling"`
84
generator *generators.PayloadGenerator
85
86
CompiledOperators *operators.Operators `yaml:"-" json:"-"`
87
dnsClient *retryabledns.Client
88
options *protocols.ExecutorOptions
89
90
// cache any variables that may be needed for operation.
91
class uint16
92
question uint16
93
94
// description: |
95
// Recursion determines if resolver should recurse all records to get fresh results.
96
Recursion *bool `yaml:"recursion,omitempty" json:"recursion,omitempty" jsonschema:"title=recurse all servers,description=Recursion determines if resolver should recurse all records to get fresh results"`
97
// Resolvers to use for the dns requests
98
Resolvers []string `yaml:"resolvers,omitempty" json:"resolvers,omitempty" jsonschema:"title=Resolvers,description=Define resolvers to use within the template"`
99
}
100
101
// RequestPartDefinitions contains a mapping of request part definitions and their
102
// description. Multiple definitions are separated by commas.
103
// Definitions not having a name (generated on runtime) are prefixed & suffixed by <>.
104
var RequestPartDefinitions = map[string]string{
105
"template-id": "ID of the template executed",
106
"template-info": "Info Block of the template executed",
107
"template-path": "Path of the template executed",
108
"host": "Host is the input to the template",
109
"matched": "Matched is the input which was matched upon",
110
"request": "Request contains the DNS request in text format",
111
"type": "Type is the type of request made",
112
"rcode": "Rcode field returned for the DNS request",
113
"question": "Question contains the DNS question field",
114
"extra": "Extra contains the DNS response extra field",
115
"answer": "Answer contains the DNS response answer field",
116
"ns": "NS contains the DNS response NS field",
117
"raw,body,all": "Raw contains the raw DNS response (default)",
118
"trace": "Trace contains trace data for DNS request if enabled",
119
}
120
121
func (request *Request) GetCompiledOperators() []*operators.Operators {
122
return []*operators.Operators{request.CompiledOperators}
123
}
124
125
// GetID returns the unique ID of the request if any.
126
func (request *Request) GetID() string {
127
return request.ID
128
}
129
130
// Options returns executer options for http request
131
func (r *Request) Options() *protocols.ExecutorOptions {
132
return r.options
133
}
134
135
// Compile compiles the protocol request for further execution.
136
func (request *Request) Compile(options *protocols.ExecutorOptions) error {
137
if request.Retries == 0 {
138
request.Retries = 3
139
}
140
if request.Recursion == nil {
141
recursion := true
142
request.Recursion = &recursion
143
}
144
// Create a dns client for the class
145
client, err := request.getDnsClient(options, nil)
146
if err != nil {
147
return errors.Wrap(err, "could not get dns client")
148
}
149
request.dnsClient = client
150
151
if len(request.Matchers) > 0 || len(request.Extractors) > 0 {
152
compiled := &request.Operators
153
compiled.ExcludeMatchers = options.ExcludeMatchers
154
compiled.TemplateID = options.TemplateID
155
if err := compiled.Compile(); err != nil {
156
return errors.Wrap(err, "could not compile operators")
157
}
158
request.CompiledOperators = compiled
159
}
160
request.class = classToInt(request.Class)
161
request.options = options
162
request.question = questionTypeToInt(request.RequestType.String())
163
for name, payload := range options.Options.Vars.AsMap() {
164
payloadStr, ok := payload.(string)
165
// check if inputs contains the payload
166
if ok && fileutil.FileExists(payloadStr) {
167
if request.Payloads == nil {
168
request.Payloads = make(map[string]interface{})
169
}
170
request.Payloads[name] = payloadStr
171
}
172
}
173
174
if len(request.Payloads) > 0 {
175
request.generator, err = generators.New(request.Payloads, request.AttackType.Value, request.options.TemplatePath, request.options.Catalog, request.options.Options.AttackType, request.options.Options)
176
if err != nil {
177
return errors.Wrap(err, "could not parse payloads")
178
}
179
// default to 20 threads for payload requests
180
request.Threads = options.GetThreadsForNPayloadRequests(request.Requests(), request.Threads)
181
}
182
return nil
183
}
184
185
func (request *Request) getDnsClient(options *protocols.ExecutorOptions, metadata map[string]interface{}) (*retryabledns.Client, error) {
186
dnsClientOptions := &dnsclientpool.Configuration{
187
Retries: request.Retries,
188
Proxy: options.Options.AliveSocksProxy,
189
}
190
if len(request.Resolvers) > 0 {
191
if len(request.Resolvers) > 0 {
192
for _, resolver := range request.Resolvers {
193
if expressions.ContainsUnresolvedVariables(resolver) != nil {
194
var err error
195
resolver, err = expressions.Evaluate(resolver, metadata)
196
if err != nil {
197
return nil, errors.Wrap(err, "could not resolve resolvers expressions")
198
}
199
dnsClientOptions.Resolvers = append(dnsClientOptions.Resolvers, resolver)
200
}
201
}
202
}
203
dnsClientOptions.Resolvers = request.Resolvers
204
}
205
return dnsclientpool.Get(options.Options, dnsClientOptions)
206
}
207
208
// Requests returns the total number of requests the YAML rule will perform
209
func (request *Request) Requests() int {
210
if request.generator != nil {
211
payloadRequests := request.generator.NewIterator().Total()
212
return payloadRequests
213
}
214
215
return 1
216
}
217
218
// Make returns the request to be sent for the protocol
219
func (request *Request) Make(host string, vars map[string]interface{}) (*dns.Msg, error) {
220
// Build a request on the specified URL
221
req := new(dns.Msg)
222
req.Id = dns.Id()
223
req.RecursionDesired = *request.Recursion
224
225
var q dns.Question
226
final := replacer.Replace(request.Name, vars)
227
228
q.Name = dns.Fqdn(final)
229
q.Qclass = request.class
230
q.Qtype = request.question
231
req.Question = append(req.Question, q)
232
233
req.SetEdns0(4096, false)
234
235
switch request.question {
236
case dns.TypeTXT:
237
req.AuthenticatedData = true
238
}
239
240
return req, nil
241
}
242
243
// questionTypeToInt converts DNS question type to internal representation
244
func questionTypeToInt(questionType string) uint16 {
245
questionType = strings.TrimSpace(strings.ToUpper(questionType))
246
question := dns.TypeA
247
248
switch questionType {
249
case "A":
250
question = dns.TypeA
251
case "NS":
252
question = dns.TypeNS
253
case "CNAME":
254
question = dns.TypeCNAME
255
case "SOA":
256
question = dns.TypeSOA
257
case "PTR":
258
question = dns.TypePTR
259
case "MX":
260
question = dns.TypeMX
261
case "TXT":
262
question = dns.TypeTXT
263
case "DS":
264
question = dns.TypeDS
265
case "AAAA":
266
question = dns.TypeAAAA
267
case "CAA":
268
question = dns.TypeCAA
269
case "TLSA":
270
question = dns.TypeTLSA
271
case "ANY":
272
question = dns.TypeANY
273
case "SRV":
274
question = dns.TypeSRV
275
}
276
return question
277
}
278
279
// classToInt converts a dns class name to its internal representation
280
func classToInt(class string) uint16 {
281
class = strings.TrimSpace(strings.ToUpper(class))
282
result := dns.ClassINET
283
284
switch class {
285
case "INET":
286
result = dns.ClassINET
287
case "CSNET":
288
result = dns.ClassCSNET
289
case "CHAOS":
290
result = dns.ClassCHAOS
291
case "HESIOD":
292
result = dns.ClassHESIOD
293
case "NONE":
294
result = dns.ClassNONE
295
case "ANY":
296
result = dns.ClassANY
297
}
298
return uint16(result)
299
}
300
301
// UpdateOptions replaces this request's options with a new copy
302
func (r *Request) UpdateOptions(opts *protocols.ExecutorOptions) {
303
r.options.ApplyNewEngineOptions(opts)
304
}
305
306