Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/dns/operators.go
2070 views
1
package dns
2
3
import (
4
"bytes"
5
"fmt"
6
"strings"
7
"time"
8
9
"github.com/miekg/dns"
10
11
"github.com/projectdiscovery/nuclei/v3/pkg/model"
12
"github.com/projectdiscovery/nuclei/v3/pkg/operators/extractors"
13
"github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers"
14
"github.com/projectdiscovery/nuclei/v3/pkg/output"
15
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
16
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
17
"github.com/projectdiscovery/nuclei/v3/pkg/types"
18
"github.com/projectdiscovery/retryabledns"
19
)
20
21
// Match matches a generic data response against a given matcher
22
func (request *Request) Match(data map[string]interface{}, matcher *matchers.Matcher) (bool, []string) {
23
item, ok := request.getMatchPart(matcher.Part, data)
24
if !ok && matcher.Type.MatcherType != matchers.DSLMatcher {
25
return false, []string{}
26
}
27
28
switch matcher.GetType() {
29
case matchers.StatusMatcher:
30
statusCode, ok := item.(int)
31
if !ok {
32
return false, []string{}
33
}
34
return matcher.Result(matcher.MatchStatusCode(statusCode)), []string{}
35
case matchers.SizeMatcher:
36
return matcher.Result(matcher.MatchSize(len(types.ToString(item)))), []string{}
37
case matchers.WordsMatcher:
38
return matcher.ResultWithMatchedSnippet(matcher.MatchWords(types.ToString(item), data))
39
case matchers.RegexMatcher:
40
return matcher.ResultWithMatchedSnippet(matcher.MatchRegex(types.ToString(item)))
41
case matchers.BinaryMatcher:
42
return matcher.ResultWithMatchedSnippet(matcher.MatchBinary(types.ToString(item)))
43
case matchers.DSLMatcher:
44
return matcher.Result(matcher.MatchDSL(data)), []string{}
45
case matchers.XPathMatcher:
46
return matcher.Result(matcher.MatchXPath(types.ToString(item))), []string{}
47
}
48
return false, []string{}
49
}
50
51
// Extract performs extracting operation for an extractor on model and returns true or false.
52
func (request *Request) Extract(data map[string]interface{}, extractor *extractors.Extractor) map[string]struct{} {
53
item, ok := request.getMatchPart(extractor.Part, data)
54
if !ok && !extractors.SupportsMap(extractor) {
55
return nil
56
}
57
58
switch extractor.GetType() {
59
case extractors.RegexExtractor:
60
return extractor.ExtractRegex(types.ToString(item))
61
case extractors.KValExtractor:
62
return extractor.ExtractKval(data)
63
case extractors.DSLExtractor:
64
return extractor.ExtractDSL(data)
65
}
66
return nil
67
}
68
69
func (request *Request) getMatchPart(part string, data output.InternalEvent) (interface{}, bool) {
70
switch part {
71
case "body", "all", "":
72
part = "raw"
73
}
74
75
item, ok := data[part]
76
if !ok {
77
return "", false
78
}
79
80
return item, true
81
}
82
83
// responseToDSLMap converts a DNS response to a map for use in DSL matching
84
func (request *Request) responseToDSLMap(req, resp *dns.Msg, host, matched string, traceData *retryabledns.TraceData) output.InternalEvent {
85
ret := output.InternalEvent{
86
"host": host,
87
"matched": matched,
88
"request": req.String(),
89
"rcode": resp.Rcode,
90
"question": questionToString(resp.Question),
91
"extra": rrToString(resp.Extra),
92
"answer": rrToString(resp.Answer),
93
"ns": rrToString(resp.Ns),
94
"raw": resp.String(),
95
"template-id": request.options.TemplateID,
96
"template-info": request.options.TemplateInfo,
97
"template-path": request.options.TemplatePath,
98
"type": request.Type().String(),
99
"trace": traceToString(traceData, false),
100
}
101
if len(resp.Answer) > 0 {
102
ret = generators.MergeMaps(ret, recordsKeyValue(resp.Answer))
103
}
104
return ret
105
}
106
107
// MakeResultEvent creates a result event from internal wrapped event
108
func (request *Request) MakeResultEvent(wrapped *output.InternalWrappedEvent) []*output.ResultEvent {
109
return protocols.MakeDefaultResultEvent(request, wrapped)
110
}
111
112
func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent) *output.ResultEvent {
113
data := &output.ResultEvent{
114
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
115
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
116
Info: wrapped.InternalEvent["template-info"].(model.Info),
117
TemplateVerifier: request.options.TemplateVerifier,
118
Type: types.ToString(wrapped.InternalEvent["type"]),
119
Host: types.ToString(wrapped.InternalEvent["host"]),
120
Matched: types.ToString(wrapped.InternalEvent["matched"]),
121
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
122
MatcherStatus: true,
123
Timestamp: time.Now(),
124
Request: types.ToString(wrapped.InternalEvent["request"]),
125
Response: types.ToString(wrapped.InternalEvent["raw"]),
126
TemplateEncoded: request.options.EncodeTemplate(),
127
Error: types.ToString(wrapped.InternalEvent["error"]),
128
}
129
return data
130
}
131
132
func rrToString(resourceRecords []dns.RR) string { // TODO rewrite with generics when available
133
buffer := &bytes.Buffer{}
134
for _, resourceRecord := range resourceRecords {
135
buffer.WriteString(resourceRecord.String())
136
}
137
return buffer.String()
138
}
139
140
func questionToString(resourceRecords []dns.Question) string {
141
buffer := &bytes.Buffer{}
142
for _, resourceRecord := range resourceRecords {
143
buffer.WriteString(resourceRecord.String())
144
}
145
return buffer.String()
146
}
147
148
func traceToString(traceData *retryabledns.TraceData, withSteps bool) string {
149
buffer := &bytes.Buffer{}
150
if traceData != nil {
151
for i, dnsRecord := range traceData.DNSData {
152
if withSteps {
153
fmt.Fprintf(buffer, "request %d to resolver %s:\n", i, strings.Join(dnsRecord.Resolver, ","))
154
}
155
_, _ = fmt.Fprintf(buffer, "%s\n", dnsRecord.Raw)
156
}
157
}
158
return buffer.String()
159
}
160
161
func recordsKeyValue(resourceRecords []dns.RR) output.InternalEvent {
162
var oe = make(output.InternalEvent)
163
for _, resourceRecord := range resourceRecords {
164
key := strings.ToLower(dns.TypeToString[resourceRecord.Header().Rrtype])
165
value := strings.TrimSuffix(strings.ReplaceAll(resourceRecord.String(), resourceRecord.Header().String(), ""), ".")
166
167
// if the key is already present, we need to convert the value to a slice
168
// if the key has slice, then append the value to the slice
169
if previous, ok := oe[key]; ok {
170
switch v := previous.(type) {
171
case string:
172
oe[key] = []string{v, value}
173
case []string:
174
oe[key] = append(v, value)
175
}
176
continue
177
}
178
oe[key] = value
179
}
180
return oe
181
}
182
183