2020-12-21 14:31:32 +05:30
package dns
import (
"strings"
"github.com/miekg/dns"
2020-12-25 02:24:55 +05:30
"github.com/pkg/errors"
2021-09-07 17:31:46 +03:00
2020-12-25 02:24:55 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/operators"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
2021-11-18 14:52:11 +01:00
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/expressions"
2023-05-11 03:26:29 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
2020-12-24 12:56:28 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/replacer"
2020-12-25 12:55:46 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/dns/dnsclientpool"
2020-12-25 02:24:55 +05:30
"github.com/projectdiscovery/retryabledns"
2023-05-11 03:26:29 +05:30
fileutil "github.com/projectdiscovery/utils/file"
2020-12-21 14:31:32 +05:30
)
// Request contains a DNS protocol request to be made from a template
type Request struct {
2021-02-26 13:13:11 +05:30
// Operators for the current request go here.
operators . Operators ` yaml:",inline" `
2021-09-07 17:31:46 +03:00
// ID is the optional id of the request
2023-02-07 16:10:40 +08:00
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" `
2021-01-16 14:10:24 +05:30
2021-07-27 16:03:56 +05:30
// description: |
// Name is the Hostname to make DNS request for.
//
// Generally, it is set to {{FQDN}} which is the domain we get from input.
// examples:
// - value: "\"{{FQDN}}\""
2023-02-07 16:10:40 +08:00
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" `
2021-07-27 16:03:56 +05:30
// description: |
2021-11-03 19:53:45 +05:30
// RequestType is the type of DNS request to make.
2023-02-07 16:10:40 +08:00
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" `
2021-07-27 16:03:56 +05:30
// description: |
// Class is the class of the DNS request.
//
// Usually it's enough to just leave it as INET.
// values:
2021-08-26 23:27:42 +05:30
// - "inet"
// - "csnet"
// - "chaos"
// - "hesiod"
// - "none"
// - "any"
2023-02-07 16:10:40 +08:00
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" `
2021-07-27 16:03:56 +05:30
// description: |
// Retries is the number of retries for the DNS request
// examples:
// - name: Use a retry of 3 to 5 generally
// value: 5
2023-02-07 16:10:40 +08:00
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" `
2021-11-18 14:52:11 +01:00
// description: |
// Trace performs a trace operation for the target.
2023-02-07 16:10:40 +08:00
Trace bool ` yaml:"trace,omitempty" json:"trace,omitempty" jsonschema:"title=trace operation,description=Trace performs a trace operation for the target." `
2021-11-18 14:52:11 +01:00
// description: |
// TraceMaxRecursion is the number of max recursion allowed for trace operations
// examples:
// - name: Use a retry of 100 to 150 generally
// value: 100
TraceMaxRecursion int ` yaml:"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" `
2020-12-21 14:31:32 +05:30
2023-05-11 03:26:29 +05:30
// description: |
// Attack is the type of payload combinations to perform.
//
// Batteringram is inserts the same payload into all defined payload positions at once, pitchfork combines multiple payload sets and clusterbomb generates
// permutations and combinations for all payloads.
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" `
// description: |
// Payloads contains any payloads for the current request.
//
// Payloads support both key-values combinations where a list
// of payloads is provided, or optionally a single file can also
// be provided as payload which will be read on run-time.
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" `
generator * generators . PayloadGenerator
2021-08-04 14:20:48 +05:30
CompiledOperators * operators . Operators ` yaml:"-" `
2021-02-26 13:13:11 +05:30
dnsClient * retryabledns . Client
2023-05-31 16:58:10 -04:00
options * protocols . ExecutorOptions
2020-12-25 02:24:55 +05:30
2020-12-21 14:31:32 +05:30
// cache any variables that may be needed for operation.
2021-02-26 13:13:11 +05:30
class uint16
question uint16
2021-07-27 16:03:56 +05:30
// description: |
// Recursion determines if resolver should recurse all records to get fresh results.
2023-02-07 16:10:40 +08:00
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" `
2021-10-04 15:31:14 +02:00
// Resolvers to use for the dns requests
2023-02-07 16:10:40 +08:00
Resolvers [ ] string ` yaml:"resolvers,omitempty" json:"resolvers,omitempty" jsonschema:"title=Resolvers,description=Define resolvers to use within the template" `
2020-12-21 14:31:32 +05:30
}
2021-11-26 16:23:54 +05:30
// RequestPartDefinitions contains a mapping of request part definitions and their
// description. Multiple definitions are separated by commas.
// Definitions not having a name (generated on runtime) are prefixed & suffixed by <>.
var RequestPartDefinitions = map [ string ] string {
"template-id" : "ID of the template executed" ,
"template-info" : "Info Block of the template executed" ,
"template-path" : "Path of the template executed" ,
"host" : "Host is the input to the template" ,
"matched" : "Matched is the input which was matched upon" ,
"request" : "Request contains the DNS request in text format" ,
"type" : "Type is the type of request made" ,
"rcode" : "Rcode field returned for the DNS request" ,
"question" : "Question contains the DNS question field" ,
"extra" : "Extra contains the DNS response extra field" ,
"answer" : "Answer contains the DNS response answer field" ,
"ns" : "NS contains the DNS response NS field" ,
"raw,body,all" : "Raw contains the raw DNS response (default)" ,
"trace" : "Trace contains trace data for DNS request if enabled" ,
}
2021-10-01 16:52:38 +03:00
func ( request * Request ) GetCompiledOperators ( ) [ ] * operators . Operators {
return [ ] * operators . Operators { request . CompiledOperators }
}
2021-01-16 14:10:24 +05:30
// GetID returns the unique ID of the request if any.
2021-10-01 14:30:04 +03:00
func ( request * Request ) GetID ( ) string {
return request . ID
2021-01-16 14:10:24 +05:30
}
2023-01-17 13:01:20 +05:30
// Options returns executer options for http request
2023-05-31 16:58:10 -04:00
func ( r * Request ) Options ( ) * protocols . ExecutorOptions {
2023-01-17 13:01:20 +05:30
return r . options
}
2020-12-21 14:31:32 +05:30
// Compile compiles the protocol request for further execution.
2023-05-31 16:58:10 -04:00
func ( request * Request ) Compile ( options * protocols . ExecutorOptions ) error {
2021-11-23 13:09:22 +05:30
if request . Retries == 0 {
request . Retries = 3
}
if request . Recursion == nil {
2021-12-02 11:19:37 +01:00
recursion := true
request . Recursion = & recursion
2021-11-23 13:09:22 +05:30
}
2021-10-04 15:31:14 +02:00
dnsClientOptions := & dnsclientpool . Configuration {
2021-10-01 14:30:04 +03:00
Retries : request . Retries ,
2021-10-04 15:31:14 +02:00
}
2021-10-05 13:20:21 +03:00
if len ( request . Resolvers ) > 0 {
dnsClientOptions . Resolvers = request . Resolvers
2021-10-04 15:31:14 +02:00
}
// Create a dns client for the class
2021-11-18 14:52:11 +01:00
client , err := request . getDnsClient ( options , nil )
2020-12-25 02:24:55 +05:30
if err != nil {
return errors . Wrap ( err , "could not get dns client" )
}
2021-10-01 14:30:04 +03:00
request . dnsClient = client
2020-12-25 02:24:55 +05:30
2021-10-01 14:30:04 +03:00
if len ( request . Matchers ) > 0 || len ( request . Extractors ) > 0 {
compiled := & request . Operators
2022-06-24 23:09:27 +05:30
compiled . ExcludeMatchers = options . ExcludeMatchers
compiled . TemplateID = options . TemplateID
2020-12-30 13:26:55 +05:30
if err := compiled . Compile ( ) ; err != nil {
2020-12-25 02:24:55 +05:30
return errors . Wrap ( err , "could not compile operators" )
}
2021-10-01 14:30:04 +03:00
request . CompiledOperators = compiled
2020-12-25 02:24:55 +05:30
}
2021-10-01 14:30:04 +03:00
request . class = classToInt ( request . Class )
request . options = options
2021-11-18 07:47:34 -06:00
request . question = questionTypeToInt ( request . RequestType . String ( ) )
2023-05-11 03:26:29 +05:30
for name , payload := range options . Options . Vars . AsMap ( ) {
payloadStr , ok := payload . ( string )
// check if inputs contains the payload
if ok && fileutil . FileExists ( payloadStr ) {
if request . Payloads == nil {
request . Payloads = make ( map [ string ] interface { } )
}
request . Payloads [ name ] = payloadStr
}
}
if len ( request . Payloads ) > 0 {
2023-08-31 18:03:01 +05:30
request . generator , err = generators . New ( request . Payloads , request . AttackType . Value , request . options . TemplatePath , request . options . Catalog , request . options . Options . AttackType , request . options . Options )
2023-05-11 03:26:29 +05:30
if err != nil {
return errors . Wrap ( err , "could not parse payloads" )
}
}
2020-12-21 14:31:32 +05:30
return nil
}
2023-05-31 16:58:10 -04:00
func ( request * Request ) getDnsClient ( options * protocols . ExecutorOptions , metadata map [ string ] interface { } ) ( * retryabledns . Client , error ) {
2021-11-18 14:52:11 +01:00
dnsClientOptions := & dnsclientpool . Configuration {
Retries : request . Retries ,
}
if len ( request . Resolvers ) > 0 {
if len ( request . Resolvers ) > 0 {
for _ , resolver := range request . Resolvers {
if expressions . ContainsUnresolvedVariables ( resolver ) != nil {
var err error
resolver , err = expressions . Evaluate ( resolver , metadata )
if err != nil {
return nil , errors . Wrap ( err , "could not resolve resolvers expressions" )
}
dnsClientOptions . Resolvers = append ( dnsClientOptions . Resolvers , resolver )
}
}
}
dnsClientOptions . Resolvers = request . Resolvers
}
return dnsclientpool . Get ( options . Options , dnsClientOptions )
}
2020-12-21 14:31:32 +05:30
// Requests returns the total number of requests the YAML rule will perform
2021-10-01 14:30:04 +03:00
func ( request * Request ) Requests ( ) int {
2023-05-11 03:26:29 +05:30
if request . generator != nil {
payloadRequests := request . generator . NewIterator ( ) . Total ( )
return payloadRequests
}
2020-12-21 14:31:32 +05:30
return 1
}
// Make returns the request to be sent for the protocol
2022-04-02 02:14:00 +05:30
func ( request * Request ) Make ( host string , vars map [ string ] interface { } ) ( * dns . Msg , error ) {
2020-12-21 14:31:32 +05:30
// Build a request on the specified URL
req := new ( dns . Msg )
req . Id = dns . Id ( )
2021-11-23 13:09:22 +05:30
req . RecursionDesired = * request . Recursion
2020-12-21 14:31:32 +05:30
var q dns . Question
2022-03-31 00:54:35 +05:30
final := replacer . Replace ( request . Name , vars )
2020-12-21 14:31:32 +05:30
2021-02-04 04:48:45 +05:30
q . Name = dns . Fqdn ( final )
2021-10-01 14:30:04 +03:00
q . Qclass = request . class
q . Qtype = request . question
2020-12-21 14:31:32 +05:30
req . Question = append ( req . Question , q )
2021-09-26 07:22:00 +02:00
req . SetEdns0 ( 4096 , false )
2021-10-01 14:30:04 +03:00
switch request . question {
2021-09-26 07:22:00 +02:00
case dns . TypeTXT :
req . AuthenticatedData = true
}
2020-12-21 14:31:32 +05:30
return req , nil
}
// questionTypeToInt converts DNS question type to internal representation
2021-02-26 13:13:11 +05:30
func questionTypeToInt ( questionType string ) uint16 {
questionType = strings . TrimSpace ( strings . ToUpper ( questionType ) )
2020-12-21 14:31:32 +05:30
question := dns . TypeA
2021-02-26 13:13:11 +05:30
switch questionType {
2020-12-21 14:31:32 +05:30
case "A" :
question = dns . TypeA
case "NS" :
question = dns . TypeNS
case "CNAME" :
question = dns . TypeCNAME
case "SOA" :
question = dns . TypeSOA
case "PTR" :
question = dns . TypePTR
case "MX" :
question = dns . TypeMX
case "TXT" :
question = dns . TypeTXT
2021-07-21 16:48:01 +02:00
case "DS" :
question = dns . TypeDS
2020-12-21 14:31:32 +05:30
case "AAAA" :
question = dns . TypeAAAA
2022-02-02 07:54:15 +01:00
case "CAA" :
question = dns . TypeCAA
2022-12-28 22:42:02 +07:00
case "TLSA" :
question = dns . TypeTLSA
2023-05-03 21:50:06 +05:30
case "ANY" :
question = dns . TypeANY
2020-12-21 14:31:32 +05:30
}
2021-02-26 13:13:11 +05:30
return question
2020-12-21 14:31:32 +05:30
}
2021-09-07 17:31:46 +03:00
// classToInt converts a dns class name to its internal representation
2020-12-21 14:31:32 +05:30
func classToInt ( class string ) uint16 {
class = strings . TrimSpace ( strings . ToUpper ( class ) )
result := dns . ClassINET
switch class {
case "INET" :
result = dns . ClassINET
case "CSNET" :
result = dns . ClassCSNET
case "CHAOS" :
result = dns . ClassCHAOS
case "HESIOD" :
result = dns . ClassHESIOD
case "NONE" :
result = dns . ClassNONE
case "ANY" :
result = dns . ClassANY
}
return uint16 ( result )
}