github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/dns/operators_test.go (about)

     1  package dns
     2  
     3  import (
     4  	"net"
     5  	"strconv"
     6  	"testing"
     7  
     8  	"github.com/miekg/dns"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/projectdiscovery/nuclei/v2/pkg/model"
    12  	"github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity"
    13  	"github.com/projectdiscovery/nuclei/v2/pkg/operators"
    14  	"github.com/projectdiscovery/nuclei/v2/pkg/operators/extractors"
    15  	"github.com/projectdiscovery/nuclei/v2/pkg/operators/matchers"
    16  	"github.com/projectdiscovery/nuclei/v2/pkg/output"
    17  	"github.com/projectdiscovery/nuclei/v2/pkg/testutils"
    18  )
    19  
    20  func TestResponseToDSLMap(t *testing.T) {
    21  	options := testutils.DefaultOptions
    22  
    23  	recursion := false
    24  	testutils.Init(options)
    25  	templateID := "testing-dns"
    26  	request := &Request{
    27  		RequestType: DNSRequestTypeHolder{DNSRequestType: A},
    28  		Class:       "INET",
    29  		Retries:     5,
    30  		ID:          templateID,
    31  		Recursion:   &recursion,
    32  		Name:        "{{FQDN}}",
    33  	}
    34  	executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
    35  		ID:   templateID,
    36  		Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
    37  	})
    38  	err := request.Compile(executerOpts)
    39  	require.Nil(t, err, "could not compile dns request")
    40  
    41  	req := new(dns.Msg)
    42  	req.Question = append(req.Question, dns.Question{Name: "one.one.one.one.", Qtype: dns.TypeA, Qclass: dns.ClassINET})
    43  
    44  	resp := new(dns.Msg)
    45  	resp.Rcode = dns.RcodeSuccess
    46  	resp.Answer = append(resp.Answer, &dns.A{A: net.ParseIP("1.1.1.1"), Hdr: dns.RR_Header{Name: "one.one.one.one.", Rrtype: dns.TypeA}}, &dns.A{A: net.ParseIP("2.2.2.2"), Hdr: dns.RR_Header{Name: "one.one.one.one.", Rrtype: dns.TypeA}}, &dns.A{A: net.ParseIP("3.3.3.3"), Hdr: dns.RR_Header{Name: "one.one.one.one.", Rrtype: dns.TypeA}})
    47  
    48  	event := request.responseToDSLMap(req, resp, "one.one.one.one", "one.one.one.one", nil)
    49  	require.Len(t, event, 15, "could not get correct number of items in dsl map")
    50  	require.Equal(t, dns.RcodeSuccess, event["rcode"], "could not get correct rcode")
    51  	require.ElementsMatch(t, []string{net.ParseIP("1.1.1.1").String(), net.ParseIP("2.2.2.2").String(), net.ParseIP("3.3.3.3").String()}, event["a"], "could not get correct a record")
    52  }
    53  
    54  func TestDNSOperatorMatch(t *testing.T) {
    55  	options := testutils.DefaultOptions
    56  
    57  	recursion := false
    58  	testutils.Init(options)
    59  	templateID := "testing-dns"
    60  	request := &Request{
    61  		RequestType: DNSRequestTypeHolder{DNSRequestType: A},
    62  		Class:       "INET",
    63  		Retries:     5,
    64  		ID:          templateID,
    65  		Recursion:   &recursion,
    66  		Name:        "{{FQDN}}",
    67  	}
    68  	executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
    69  		ID:   templateID,
    70  		Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
    71  	})
    72  	err := request.Compile(executerOpts)
    73  	require.Nil(t, err, "could not compile dns request")
    74  
    75  	req := new(dns.Msg)
    76  	req.Question = append(req.Question, dns.Question{Name: "one.one.one.one.", Qtype: dns.TypeA, Qclass: dns.ClassINET})
    77  
    78  	resp := new(dns.Msg)
    79  	resp.Rcode = dns.RcodeSuccess
    80  	resp.Answer = append(resp.Answer, &dns.A{A: net.ParseIP("1.1.1.1"), Hdr: dns.RR_Header{Name: "one.one.one.one."}})
    81  
    82  	event := request.responseToDSLMap(req, resp, "one.one.one.one", "one.one.one.one", nil)
    83  
    84  	t.Run("valid", func(t *testing.T) {
    85  		matcher := &matchers.Matcher{
    86  			Part:  "raw",
    87  			Type:  matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},
    88  			Words: []string{"1.1.1.1"},
    89  		}
    90  		err = matcher.CompileMatchers()
    91  		require.Nil(t, err, "could not compile matcher")
    92  
    93  		isMatch, matched := request.Match(event, matcher)
    94  		require.True(t, isMatch, "could not match valid response")
    95  		require.Equal(t, matcher.Words, matched)
    96  	})
    97  
    98  	t.Run("rcode", func(t *testing.T) {
    99  		matcher := &matchers.Matcher{
   100  			Part:   "rcode",
   101  			Type:   matchers.MatcherTypeHolder{MatcherType: matchers.StatusMatcher},
   102  			Status: []int{dns.RcodeSuccess},
   103  		}
   104  		err = matcher.CompileMatchers()
   105  		require.Nil(t, err, "could not compile rcode matcher")
   106  
   107  		isMatched, matched := request.Match(event, matcher)
   108  		require.True(t, isMatched, "could not match valid rcode response")
   109  		require.Equal(t, []string{}, matched)
   110  	})
   111  
   112  	t.Run("negative", func(t *testing.T) {
   113  		matcher := &matchers.Matcher{
   114  			Part:     "raw",
   115  			Type:     matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},
   116  			Negative: true,
   117  			Words:    []string{"random"},
   118  		}
   119  		err := matcher.CompileMatchers()
   120  		require.Nil(t, err, "could not compile negative matcher")
   121  
   122  		isMatched, matched := request.Match(event, matcher)
   123  		require.True(t, isMatched, "could not match valid negative response matcher")
   124  		require.Equal(t, []string{}, matched)
   125  	})
   126  
   127  	t.Run("invalid", func(t *testing.T) {
   128  		matcher := &matchers.Matcher{
   129  			Part:  "raw",
   130  			Type:  matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},
   131  			Words: []string{"random"},
   132  		}
   133  		err := matcher.CompileMatchers()
   134  		require.Nil(t, err, "could not compile matcher")
   135  
   136  		isMatched, matched := request.Match(event, matcher)
   137  		require.False(t, isMatched, "could match invalid response matcher")
   138  		require.Equal(t, []string{}, matched)
   139  	})
   140  
   141  	t.Run("caseInsensitive", func(t *testing.T) {
   142  		req := new(dns.Msg)
   143  		req.Question = append(req.Question, dns.Question{Name: "ONE.ONE.ONE.ONE.", Qtype: dns.TypeA, Qclass: dns.ClassINET})
   144  
   145  		resp := new(dns.Msg)
   146  		resp.Rcode = dns.RcodeSuccess
   147  		resp.Answer = append(resp.Answer, &dns.A{A: net.ParseIP("1.1.1.1"), Hdr: dns.RR_Header{Name: "ONE.ONE.ONE.ONE."}})
   148  
   149  		event := request.responseToDSLMap(req, resp, "ONE.ONE.ONE.ONE", "ONE.ONE.ONE.ONE", nil)
   150  
   151  		matcher := &matchers.Matcher{
   152  			Part:            "raw",
   153  			Type:            matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},
   154  			Words:           []string{"one.ONE.one.ONE"},
   155  			CaseInsensitive: true,
   156  		}
   157  		err = matcher.CompileMatchers()
   158  		require.Nil(t, err, "could not compile matcher")
   159  
   160  		isMatch, matched := request.Match(event, matcher)
   161  		require.True(t, isMatch, "could not match valid response")
   162  		require.Equal(t, []string{"one.one.one.one"}, matched)
   163  	})
   164  }
   165  
   166  func TestDNSOperatorExtract(t *testing.T) {
   167  	options := testutils.DefaultOptions
   168  
   169  	recursion := false
   170  	testutils.Init(options)
   171  	templateID := "testing-dns"
   172  	request := &Request{
   173  		RequestType: DNSRequestTypeHolder{DNSRequestType: A},
   174  		Class:       "INET",
   175  		Retries:     5,
   176  		ID:          templateID,
   177  		Recursion:   &recursion,
   178  		Name:        "{{FQDN}}",
   179  	}
   180  	executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
   181  		ID:   templateID,
   182  		Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
   183  	})
   184  	err := request.Compile(executerOpts)
   185  	require.Nil(t, err, "could not compile dns request")
   186  
   187  	req := new(dns.Msg)
   188  	req.Question = append(req.Question, dns.Question{Name: "one.one.one.one.", Qtype: dns.TypeA, Qclass: dns.ClassINET})
   189  
   190  	resp := new(dns.Msg)
   191  	resp.Rcode = dns.RcodeSuccess
   192  	resp.Answer = append(resp.Answer, &dns.A{A: net.ParseIP("1.1.1.1"), Hdr: dns.RR_Header{Name: "one.one.one.one."}})
   193  
   194  	event := request.responseToDSLMap(req, resp, "one.one.one.one", "one.one.one.one", nil)
   195  
   196  	t.Run("extract", func(t *testing.T) {
   197  		extractor := &extractors.Extractor{
   198  			Part:  "raw",
   199  			Type:  extractors.ExtractorTypeHolder{ExtractorType: extractors.RegexExtractor},
   200  			Regex: []string{"[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+"},
   201  		}
   202  		err = extractor.CompileExtractors()
   203  		require.Nil(t, err, "could not compile extractor")
   204  
   205  		data := request.Extract(event, extractor)
   206  		require.Greater(t, len(data), 0, "could not extractor valid response")
   207  		require.Equal(t, map[string]struct{}{"1.1.1.1": {}}, data, "could not extract correct data")
   208  	})
   209  
   210  	t.Run("kval", func(t *testing.T) {
   211  		extractor := &extractors.Extractor{
   212  			Type: extractors.ExtractorTypeHolder{ExtractorType: extractors.KValExtractor},
   213  			KVal: []string{"rcode"},
   214  		}
   215  		err = extractor.CompileExtractors()
   216  		require.Nil(t, err, "could not compile kval extractor")
   217  
   218  		data := request.Extract(event, extractor)
   219  		require.Greater(t, len(data), 0, "could not extractor kval valid response")
   220  		require.Equal(t, map[string]struct{}{strconv.Itoa(dns.RcodeSuccess): {}}, data, "could not extract correct kval data")
   221  	})
   222  }
   223  
   224  func TestDNSMakeResult(t *testing.T) {
   225  	options := testutils.DefaultOptions
   226  
   227  	recursion := false
   228  	testutils.Init(options)
   229  	templateID := "testing-dns"
   230  	executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
   231  		ID:   templateID,
   232  		Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
   233  	})
   234  	request := &Request{
   235  		RequestType: DNSRequestTypeHolder{DNSRequestType: A},
   236  		Class:       "INET",
   237  		Retries:     5,
   238  		ID:          templateID,
   239  		Recursion:   &recursion,
   240  		Name:        "{{FQDN}}",
   241  		Operators: operators.Operators{
   242  			Matchers: []*matchers.Matcher{{
   243  				Name:  "test",
   244  				Part:  "raw",
   245  				Type:  matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},
   246  				Words: []string{"1.1.1.1"},
   247  			}},
   248  			Extractors: []*extractors.Extractor{{
   249  				Part:  "raw",
   250  				Type:  extractors.ExtractorTypeHolder{ExtractorType: extractors.RegexExtractor},
   251  				Regex: []string{"[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+"},
   252  			}},
   253  		},
   254  		options: executerOpts,
   255  	}
   256  	err := request.Compile(executerOpts)
   257  	require.Nil(t, err, "could not compile dns request")
   258  
   259  	req := new(dns.Msg)
   260  	req.Question = append(req.Question, dns.Question{Name: "one.one.one.one.", Qtype: dns.TypeA, Qclass: dns.ClassINET})
   261  
   262  	resp := new(dns.Msg)
   263  	resp.Rcode = dns.RcodeSuccess
   264  	resp.Answer = append(resp.Answer, &dns.A{A: net.ParseIP("1.1.1.1"), Hdr: dns.RR_Header{Name: "one.one.one.one."}})
   265  
   266  	event := request.responseToDSLMap(req, resp, "one.one.one.one", "one.one.one.one", nil)
   267  	finalEvent := &output.InternalWrappedEvent{InternalEvent: event}
   268  	if request.CompiledOperators != nil {
   269  		result, ok := request.CompiledOperators.Execute(event, request.Match, request.Extract, false)
   270  		if ok && result != nil {
   271  			finalEvent.OperatorsResult = result
   272  			finalEvent.Results = request.MakeResultEvent(finalEvent)
   273  		}
   274  	}
   275  	require.Equal(t, 1, len(finalEvent.Results), "could not get correct number of results")
   276  	resultEvent := finalEvent.Results[0]
   277  	require.Equal(t, "test", resultEvent.MatcherName, "could not get correct matcher name of results")
   278  	require.Equal(t, "1.1.1.1", resultEvent.ExtractedResults[0], "could not get correct extracted results")
   279  	require.Equal(t, "one.one.one.one", resultEvent.Matched, "could not get matched value")
   280  }