Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/protocols.go
2843 views
1
package protocols
2
3
import (
4
"context"
5
"encoding/base64"
6
"sync/atomic"
7
8
"github.com/projectdiscovery/fastdialer/fastdialer"
9
"github.com/projectdiscovery/gologger"
10
"github.com/projectdiscovery/ratelimit"
11
mapsutil "github.com/projectdiscovery/utils/maps"
12
stringsutil "github.com/projectdiscovery/utils/strings"
13
14
"github.com/logrusorgru/aurora"
15
16
"github.com/projectdiscovery/nuclei/v3/pkg/authprovider"
17
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
18
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/frequency"
19
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/stats"
20
"github.com/projectdiscovery/nuclei/v3/pkg/input"
21
"github.com/projectdiscovery/nuclei/v3/pkg/js/compiler"
22
"github.com/projectdiscovery/nuclei/v3/pkg/loader/parser"
23
"github.com/projectdiscovery/nuclei/v3/pkg/model"
24
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
25
"github.com/projectdiscovery/nuclei/v3/pkg/operators/extractors"
26
"github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers"
27
"github.com/projectdiscovery/nuclei/v3/pkg/output"
28
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
29
"github.com/projectdiscovery/nuclei/v3/pkg/projectfile"
30
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
31
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/globalmatchers"
32
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/hosterrorscache"
33
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
34
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/excludematchers"
35
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/variables"
36
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/headless/engine"
37
"github.com/projectdiscovery/nuclei/v3/pkg/reporting"
38
"github.com/projectdiscovery/nuclei/v3/pkg/scan"
39
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
40
"github.com/projectdiscovery/nuclei/v3/pkg/types"
41
unitutils "github.com/projectdiscovery/utils/unit"
42
)
43
44
var (
45
MaxTemplateFileSizeForEncoding = unitutils.Mega
46
)
47
48
// Executer is an interface implemented any protocol based request executer.
49
type Executer interface {
50
// Compile compiles the execution generators preparing any requests possible.
51
Compile() error
52
// Requests returns the total number of requests the rule will perform
53
Requests() int
54
// Execute executes the protocol group and returns true or false if results were found.
55
Execute(ctx *scan.ScanContext) (bool, error)
56
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
57
ExecuteWithResults(ctx *scan.ScanContext) ([]*output.ResultEvent, error)
58
}
59
60
// TemplateVerification holds cached verification information for a template.
61
type TemplateVerification struct {
62
Verified bool
63
Verifier string
64
}
65
66
// ExecutorOptions contains the configuration options for executer clients
67
type ExecutorOptions struct {
68
// TemplateID is the ID of the template for the request
69
TemplateID string
70
// TemplatePath is the path of the template for the request
71
TemplatePath string
72
// TemplateInfo contains information block of the template request
73
TemplateInfo model.Info
74
// TemplateVerifier is the verifier for the template
75
TemplateVerifier string
76
// TemplateVerificationCallback returns cached verification info for a template path.
77
// If it returns nil, verification should be computed normally.
78
TemplateVerificationCallback func(templatePath string) *TemplateVerification
79
// RawTemplate is the raw template for the request
80
RawTemplate []byte
81
// Output is a writer interface for writing output events from executer.
82
Output output.Writer
83
// Options contains configuration options for the executer.
84
Options *types.Options
85
// IssuesClient is a client for nuclei issue tracker reporting
86
IssuesClient reporting.Client
87
// Progress is a progress client for scan reporting
88
Progress progress.Progress
89
// RateLimiter is a rate-limiter for limiting sent number of requests.
90
RateLimiter *ratelimit.Limiter
91
// Catalog is a template catalog implementation for nuclei
92
Catalog catalog.Catalog
93
// ProjectFile is the project file for nuclei
94
ProjectFile *projectfile.ProjectFile
95
// Browser is a browser engine for running headless templates
96
Browser *engine.Browser
97
// Interactsh is a client for interactsh oob polling server
98
Interactsh *interactsh.Client
99
// HostErrorsCache is an optional cache for handling host errors
100
HostErrorsCache hosterrorscache.CacheInterface
101
// Stop execution once first match is found (Assigned while parsing templates)
102
// Note: this is different from Options.StopAtFirstMatch (Assigned from CLI option)
103
StopAtFirstMatch bool
104
// Variables is a list of variables from template
105
Variables variables.Variable
106
// Constants is a list of constants from template
107
Constants map[string]interface{}
108
// ExcludeMatchers is the list of matchers to exclude
109
ExcludeMatchers *excludematchers.ExcludeMatchers
110
// InputHelper is a helper for input normalization
111
InputHelper *input.Helper
112
// FuzzParamsFrequency is a cache for parameter frequency
113
FuzzParamsFrequency *frequency.Tracker
114
// FuzzStatsDB is a database for fuzzing stats
115
FuzzStatsDB *stats.Tracker
116
117
Operators []*operators.Operators // only used by offlinehttp module
118
119
// DoNotCache bool disables optional caching of the templates structure
120
DoNotCache bool
121
122
Colorizer aurora.Aurora
123
WorkflowLoader model.WorkflowLoader
124
ResumeCfg *types.ResumeCfg
125
// ProtocolType is the type of the template
126
ProtocolType templateTypes.ProtocolType
127
// Flow is execution flow for the template (written in javascript)
128
Flow string
129
// IsMultiProtocol is true if template has more than one protocol
130
IsMultiProtocol bool
131
// templateStore is a map which contains template context for each scan (i.e input * template-id pair)
132
templateCtxStore *mapsutil.SyncLockMap[string, *contextargs.Context]
133
// JsCompiler is abstracted javascript compiler which adds node modules and provides execution
134
// environment for javascript templates
135
JsCompiler *compiler.Compiler
136
// AuthProvider is a provider for auth strategies
137
AuthProvider authprovider.AuthProvider
138
//TemporaryDirectory is the directory to store temporary files
139
TemporaryDirectory string
140
Parser parser.Parser
141
// ExportReqURLPattern exports the request URL pattern
142
// in ResultEvent it contains the exact url pattern (ex: {{BaseURL}}/{{randstr}}/xyz) used in the request
143
ExportReqURLPattern bool
144
// GlobalMatchers is the storage for global matchers with http passive templates
145
GlobalMatchers *globalmatchers.Storage
146
// Logger is the shared logging instance
147
Logger *gologger.Logger
148
// CustomFastdialer is a fastdialer dialer instance
149
CustomFastdialer *fastdialer.Dialer
150
}
151
152
// todo: centralizing components is not feasible with current clogged architecture
153
// a possible approach could be an internal event bus with pub-subs? This would be less invasive than
154
// reworking dep injection from scratch
155
func (e *ExecutorOptions) RateLimitTake() {
156
// The code below can race and there isn't a great way to fix this without adding an idempotent
157
// function to the rate limiter implementation. For now, stick with whatever rate is already set.
158
/*
159
if e.RateLimiter.GetLimit() != uint(e.Options.RateLimit) {
160
e.RateLimiter.SetLimit(uint(e.Options.RateLimit))
161
e.RateLimiter.SetDuration(e.Options.RateLimitDuration)
162
}
163
*/
164
if e.RateLimiter != nil {
165
e.RateLimiter.Take()
166
}
167
}
168
169
// GetThreadsForNPayloadRequests returns the number of threads to use as default for
170
// given max-request of payloads
171
func (e *ExecutorOptions) GetThreadsForNPayloadRequests(totalRequests int, currentThreads int) int {
172
if currentThreads > 0 {
173
return currentThreads
174
}
175
176
return e.Options.PayloadConcurrency
177
}
178
179
// CreateTemplateCtxStore creates template context store (which contains templateCtx for every scan)
180
func (e *ExecutorOptions) CreateTemplateCtxStore() {
181
e.templateCtxStore = &mapsutil.SyncLockMap[string, *contextargs.Context]{
182
Map: make(map[string]*contextargs.Context),
183
ReadOnly: atomic.Bool{},
184
}
185
}
186
187
// RemoveTemplateCtx removes template context of given scan from store
188
func (e *ExecutorOptions) RemoveTemplateCtx(input *contextargs.MetaInput) {
189
scanId := input.GetScanHash(e.TemplateID)
190
if e.templateCtxStore != nil {
191
e.templateCtxStore.Delete(scanId)
192
}
193
}
194
195
// HasTemplateCtx returns true if template context exists for given input
196
func (e *ExecutorOptions) HasTemplateCtx(input *contextargs.MetaInput) bool {
197
scanId := input.GetScanHash(e.TemplateID)
198
if e.templateCtxStore != nil {
199
return e.templateCtxStore.Has(scanId)
200
}
201
return false
202
}
203
204
// GetTemplateCtx returns template context for given input
205
func (e *ExecutorOptions) GetTemplateCtx(input *contextargs.MetaInput) *contextargs.Context {
206
scanId := input.GetScanHash(e.TemplateID)
207
if e.templateCtxStore == nil {
208
// if template context store is not initialized create it
209
e.CreateTemplateCtxStore()
210
}
211
// get template context from store
212
templateCtx, ok := e.templateCtxStore.Get(scanId)
213
if !ok {
214
// if template context does not exist create new and add it to store and return it
215
templateCtx = contextargs.New(context.Background())
216
templateCtx.MetaInput = input
217
_ = e.templateCtxStore.Set(scanId, templateCtx)
218
}
219
return templateCtx
220
}
221
222
// AddTemplateVars adds vars to template context with given template type as prefix
223
// this method is no-op if template is not multi protocol
224
func (e *ExecutorOptions) AddTemplateVars(input *contextargs.MetaInput, reqType templateTypes.ProtocolType, reqID string, vars map[string]interface{}) {
225
// if we want to disable adding response variables and other variables to template context
226
// this is the statement that does it . template context is currently only enabled for
227
// multiprotocol and flow templates
228
if !e.IsMultiProtocol && e.Flow == "" {
229
// no-op if not multi protocol template or flow template
230
return
231
}
232
templateCtx := e.GetTemplateCtx(input)
233
for k, v := range vars {
234
if stringsutil.HasPrefixAny(k, templateTypes.SupportedProtocolsStrings()...) {
235
// this was inherited from previous protocols no need to modify it we can directly set it or omit
236
templateCtx.Set(k, v)
237
continue
238
}
239
if !stringsutil.EqualFoldAny(k, "template-id", "template-info", "template-path") {
240
if reqID != "" {
241
k = reqID + "_" + k
242
} else if reqType < templateTypes.InvalidProtocol {
243
k = reqType.String() + "_" + k
244
}
245
templateCtx.Set(k, v)
246
}
247
}
248
}
249
250
// AddTemplateVar adds given var to template context with given template type as prefix
251
// this method is no-op if template is not multi protocol
252
func (e *ExecutorOptions) AddTemplateVar(input *contextargs.MetaInput, templateType templateTypes.ProtocolType, reqID string, key string, value interface{}) {
253
if !e.IsMultiProtocol && e.Flow == "" {
254
// no-op if not multi protocol template or flow template
255
return
256
}
257
templateCtx := e.GetTemplateCtx(input)
258
if stringsutil.HasPrefixAny(key, templateTypes.SupportedProtocolsStrings()...) {
259
// this was inherited from previous protocols no need to modify it we can directly set it or omit
260
templateCtx.Set(key, value)
261
return
262
}
263
if reqID != "" {
264
key = reqID + "_" + key
265
} else if templateType < templateTypes.InvalidProtocol {
266
key = templateType.String() + "_" + key
267
}
268
templateCtx.Set(key, value)
269
}
270
271
// Copy returns a copy of the executeroptions structure
272
func (e *ExecutorOptions) Copy() *ExecutorOptions {
273
copy := &ExecutorOptions{
274
TemplateID: e.TemplateID,
275
TemplatePath: e.TemplatePath,
276
TemplateInfo: e.TemplateInfo,
277
TemplateVerifier: e.TemplateVerifier,
278
TemplateVerificationCallback: e.TemplateVerificationCallback,
279
RawTemplate: e.RawTemplate,
280
Output: e.Output,
281
Options: e.Options,
282
IssuesClient: e.IssuesClient,
283
Progress: e.Progress,
284
RateLimiter: e.RateLimiter,
285
Catalog: e.Catalog,
286
ProjectFile: e.ProjectFile,
287
Browser: e.Browser,
288
Interactsh: e.Interactsh,
289
HostErrorsCache: e.HostErrorsCache,
290
StopAtFirstMatch: e.StopAtFirstMatch,
291
Variables: e.Variables,
292
Constants: e.Constants,
293
ExcludeMatchers: e.ExcludeMatchers,
294
InputHelper: e.InputHelper,
295
FuzzParamsFrequency: e.FuzzParamsFrequency,
296
FuzzStatsDB: e.FuzzStatsDB,
297
Operators: e.Operators,
298
DoNotCache: e.DoNotCache,
299
Colorizer: e.Colorizer,
300
WorkflowLoader: e.WorkflowLoader,
301
ResumeCfg: e.ResumeCfg,
302
ProtocolType: e.ProtocolType,
303
Flow: e.Flow,
304
IsMultiProtocol: e.IsMultiProtocol,
305
JsCompiler: e.JsCompiler,
306
AuthProvider: e.AuthProvider,
307
TemporaryDirectory: e.TemporaryDirectory,
308
Parser: e.Parser,
309
ExportReqURLPattern: e.ExportReqURLPattern,
310
GlobalMatchers: e.GlobalMatchers,
311
Logger: e.Logger,
312
}
313
copy.CreateTemplateCtxStore()
314
return copy
315
}
316
317
// Request is an interface implemented any protocol based request generator.
318
type Request interface {
319
// Compile compiles the request generators preparing any requests possible.
320
Compile(options *ExecutorOptions) error
321
// Requests returns the total number of requests the rule will perform
322
Requests() int
323
// GetID returns the ID for the request if any. IDs are used for multi-request
324
// condition matching. So, two requests can be sent and their match can
325
// be evaluated from the third request by using the IDs for both requests.
326
GetID() string
327
// Match performs matching operation for a matcher on model and returns:
328
// true and a list of matched snippets if the matcher type is supports it
329
// otherwise false and an empty string slice
330
Match(data map[string]interface{}, matcher *matchers.Matcher) (bool, []string)
331
// Extract performs extracting operation for an extractor on model and returns true or false.
332
Extract(data map[string]interface{}, matcher *extractors.Extractor) map[string]struct{}
333
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
334
ExecuteWithResults(input *contextargs.Context, dynamicValues, previous output.InternalEvent, callback OutputEventCallback) error
335
// MakeResultEventItem creates a result event from internal wrapped event. Intended to be used by MakeResultEventItem internally
336
MakeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent
337
// MakeResultEvent creates a flat list of result events from an internal wrapped event, based on successful matchers and extracted data
338
MakeResultEvent(wrapped *output.InternalWrappedEvent) []*output.ResultEvent
339
// GetCompiledOperators returns a list of the compiled operators
340
GetCompiledOperators() []*operators.Operators
341
// Type returns the type of the protocol request
342
Type() templateTypes.ProtocolType
343
}
344
345
// OutputEventCallback is a callback event for any results found during scanning.
346
type OutputEventCallback func(result *output.InternalWrappedEvent)
347
348
func MakeDefaultResultEvent(request Request, wrapped *output.InternalWrappedEvent) []*output.ResultEvent {
349
// Note: operator result is generated if something was successful match/extract/dynamic-extract
350
// but results should not be generated if
351
// 1. no match was found and some dynamic values were extracted
352
// 2. if something was extracted (matchers exist but no match was found)
353
if len(wrapped.OperatorsResult.DynamicValues) > 0 && !wrapped.OperatorsResult.Matched {
354
return nil
355
}
356
// check if something was extracted (except dynamic values)
357
extracted := len(wrapped.OperatorsResult.Extracts) > 0 || len(wrapped.OperatorsResult.OutputExtracts) > 0
358
if extracted && len(wrapped.OperatorsResult.Operators.Matchers) > 0 && !wrapped.OperatorsResult.Matched {
359
// if extracted and matchers exist but no match was found then don't generate result
360
return nil
361
}
362
363
results := make([]*output.ResultEvent, 0, len(wrapped.OperatorsResult.Matches)+1)
364
365
// If we have multiple matchers with names, write each of them separately.
366
if len(wrapped.OperatorsResult.Matches) > 0 {
367
for matcherNames := range wrapped.OperatorsResult.Matches {
368
data := request.MakeResultEventItem(wrapped)
369
data.MatcherName = matcherNames
370
results = append(results, data)
371
}
372
} else if len(wrapped.OperatorsResult.Extracts) > 0 {
373
for k, v := range wrapped.OperatorsResult.Extracts {
374
data := request.MakeResultEventItem(wrapped)
375
data.ExtractorName = k
376
data.ExtractedResults = v
377
results = append(results, data)
378
}
379
} else {
380
data := request.MakeResultEventItem(wrapped)
381
results = append(results, data)
382
}
383
return results
384
}
385
386
// MakeDefaultExtractFunc performs extracting operation for an extractor on model and returns true or false.
387
func MakeDefaultExtractFunc(data map[string]interface{}, extractor *extractors.Extractor) map[string]struct{} {
388
part := extractor.Part
389
if part == "" {
390
part = "response"
391
}
392
393
item, ok := data[part]
394
if !ok && !extractors.SupportsMap(extractor) {
395
return nil
396
}
397
itemStr := types.ToString(item)
398
399
switch extractor.GetType() {
400
case extractors.RegexExtractor:
401
return extractor.ExtractRegex(itemStr)
402
case extractors.KValExtractor:
403
return extractor.ExtractKval(data)
404
case extractors.JSONExtractor:
405
return extractor.ExtractJSON(itemStr)
406
case extractors.XPathExtractor:
407
return extractor.ExtractXPath(itemStr)
408
case extractors.DSLExtractor:
409
return extractor.ExtractDSL(data)
410
}
411
return nil
412
}
413
414
// MakeDefaultMatchFunc performs matching operation for a matcher on model and returns true or false.
415
func MakeDefaultMatchFunc(data map[string]interface{}, matcher *matchers.Matcher) (bool, []string) {
416
part := matcher.Part
417
if part == "" {
418
part = "response"
419
}
420
421
partItem, ok := data[part]
422
if !ok && matcher.Type.MatcherType != matchers.DSLMatcher {
423
return false, nil
424
}
425
item := types.ToString(partItem)
426
427
switch matcher.GetType() {
428
case matchers.SizeMatcher:
429
result := matcher.Result(matcher.MatchSize(len(item)))
430
return result, nil
431
case matchers.WordsMatcher:
432
return matcher.ResultWithMatchedSnippet(matcher.MatchWords(item, nil))
433
case matchers.RegexMatcher:
434
return matcher.ResultWithMatchedSnippet(matcher.MatchRegex(item))
435
case matchers.BinaryMatcher:
436
return matcher.ResultWithMatchedSnippet(matcher.MatchBinary(item))
437
case matchers.DSLMatcher:
438
return matcher.Result(matcher.MatchDSL(data)), nil
439
case matchers.XPathMatcher:
440
return matcher.Result(matcher.MatchXPath(item)), []string{}
441
}
442
return false, nil
443
}
444
445
func (e *ExecutorOptions) EncodeTemplate() string {
446
if !e.Options.OmitTemplate && len(e.RawTemplate) <= MaxTemplateFileSizeForEncoding {
447
return base64.StdEncoding.EncodeToString(e.RawTemplate)
448
}
449
return ""
450
}
451
452
// ApplyNewEngineOptions updates an existing ExecutorOptions with options from a new engine. This
453
// handles things like the ExecutionID that need to be updated.
454
func (e *ExecutorOptions) ApplyNewEngineOptions(n *ExecutorOptions) {
455
// TODO: cached code|headless templates have nil ExecuterOptions if -code or -headless are not enabled
456
if e == nil || n == nil || n.Options == nil {
457
return
458
}
459
460
e.Options = n.Options.Copy()
461
e.Output = n.Output
462
e.IssuesClient = n.IssuesClient
463
e.Progress = n.Progress
464
e.RateLimiter = n.RateLimiter
465
e.Catalog = n.Catalog
466
e.ProjectFile = n.ProjectFile
467
e.Browser = n.Browser
468
e.Interactsh = n.Interactsh
469
e.HostErrorsCache = n.HostErrorsCache
470
e.InputHelper = n.InputHelper
471
e.FuzzParamsFrequency = n.FuzzParamsFrequency
472
e.FuzzStatsDB = n.FuzzStatsDB
473
e.DoNotCache = n.DoNotCache
474
e.Colorizer = n.Colorizer
475
e.WorkflowLoader = n.WorkflowLoader
476
e.ResumeCfg = n.ResumeCfg
477
e.JsCompiler = n.JsCompiler
478
e.AuthProvider = n.AuthProvider
479
e.TemporaryDirectory = n.TemporaryDirectory
480
e.Parser = n.Parser
481
e.ExportReqURLPattern = n.ExportReqURLPattern
482
e.GlobalMatchers = n.GlobalMatchers
483
e.Logger = n.Logger
484
e.CustomFastdialer = n.CustomFastdialer
485
}
486
487