mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-22 19:15:26 +00:00
Added executor + refactor
This commit is contained in:
parent
f9d249fa75
commit
4be566192f
@ -7,15 +7,6 @@ import (
|
|||||||
"github.com/projectdiscovery/gologger"
|
"github.com/projectdiscovery/gologger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultResolvers contains the list of resolvers known to be trusted.
|
|
||||||
var DefaultResolvers = []string{
|
|
||||||
"1.1.1.1:53", // Cloudflare
|
|
||||||
"1.0.0.1:53", // Cloudflare
|
|
||||||
"8.8.8.8:53", // Google
|
|
||||||
"8.8.4.4:53", // Google
|
|
||||||
"9.9.9.9:53", // Quad9
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options contains the configuration options for tuning
|
// Options contains the configuration options for tuning
|
||||||
// the template requesting process.
|
// the template requesting process.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
|||||||
@ -165,59 +165,7 @@ func (r *Runner) processTemplateWithList(template *templates.Template, request i
|
|||||||
func (r *Runner) sendRequest(template *templates.Template, request interface{}, URL string, writer *bufio.Writer, httpclient *retryablehttp.Client, dnsclient *retryabledns.Client) {
|
func (r *Runner) sendRequest(template *templates.Template, request interface{}, URL string, writer *bufio.Writer, httpclient *retryablehttp.Client, dnsclient *retryabledns.Client) {
|
||||||
switch request.(type) {
|
switch request.(type) {
|
||||||
case *requests.HTTPRequest:
|
case *requests.HTTPRequest:
|
||||||
if !isURL(URL) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
httpRequest := request.(*requests.HTTPRequest)
|
|
||||||
|
|
||||||
// Compile each request for the template based on the URL
|
|
||||||
compiledRequest, err := httpRequest.MakeHTTPRequest(URL)
|
|
||||||
if err != nil {
|
|
||||||
gologger.Warningf("[%s] Could not make request %s: %s\n", template.ID, URL, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the request to the target servers
|
|
||||||
for _, req := range compiledRequest {
|
|
||||||
resp, err := httpclient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
if resp != nil {
|
|
||||||
resp.Body.Close()
|
|
||||||
}
|
|
||||||
gologger.Warningf("[%s] Could not send request %s: %s\n", template.ID, URL, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
io.Copy(ioutil.Discard, resp.Body)
|
|
||||||
resp.Body.Close()
|
|
||||||
gologger.Warningf("[%s] Could not read body %s: %s\n", template.ID, URL, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
resp.Body.Close()
|
|
||||||
|
|
||||||
body := unsafeToString(data)
|
|
||||||
|
|
||||||
var headers string
|
|
||||||
for _, matcher := range httpRequest.Matchers {
|
|
||||||
// Only build the headers string if the matcher asks for it
|
|
||||||
part := matcher.GetPart()
|
|
||||||
if part == matchers.AllPart || part == matchers.HeaderPart && headers == "" {
|
|
||||||
headers = headersToString(resp.Header)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the matcher matched
|
|
||||||
if matcher.Match(resp, body, headers) {
|
|
||||||
// If there is an extractor, run it.
|
|
||||||
var extractorResults []string
|
|
||||||
for _, extractor := range httpRequest.Extractors {
|
|
||||||
part := extractor.GetPart()
|
|
||||||
if part == extractors.AllPart || part == extractors.HeaderPart && headers == "" {
|
|
||||||
headers = headersToString(resp.Header)
|
|
||||||
}
|
|
||||||
extractorResults = append(extractorResults, extractor.Extract(body, headers)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// All the matchers matched, print the output on the screen
|
// All the matchers matched, print the output on the screen
|
||||||
output := buildOutputHTTP(template, req, extractorResults, matcher)
|
output := buildOutputHTTP(template, req, extractorResults, matcher)
|
||||||
@ -232,40 +180,6 @@ func (r *Runner) sendRequest(template *templates.Template, request interface{},
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *requests.DNSRequest:
|
case *requests.DNSRequest:
|
||||||
// eventually extracts dns from url
|
|
||||||
var domain string = URL
|
|
||||||
if isURL(URL) {
|
|
||||||
domain = extractDomain(URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
dnsRequest := request.(*requests.DNSRequest)
|
|
||||||
|
|
||||||
// Compile each request for the template based on the URL
|
|
||||||
compiledRequest, err := dnsRequest.MakeDNSRequest(domain)
|
|
||||||
if err != nil {
|
|
||||||
gologger.Warningf("[%s] Could not make request %s: %s\n", template.ID, domain, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the request to the target servers
|
|
||||||
resp, err := dnsclient.Do(compiledRequest)
|
|
||||||
if err != nil {
|
|
||||||
gologger.Warningf("[%s] Could not send request %s: %s\n", template.ID, domain, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, matcher := range dnsRequest.Matchers {
|
|
||||||
// Check if the matcher matched
|
|
||||||
if !matcher.MatchDNS(resp) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is an extractor, run it.
|
|
||||||
var extractorResults []string
|
|
||||||
for _, extractor := range dnsRequest.Extractors {
|
|
||||||
extractorResults = append(extractorResults, extractor.ExtractDNS(resp.String())...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// All the matchers matched, print the output on the screen
|
// All the matchers matched, print the output on the screen
|
||||||
output := buildOutputDNS(template, domain, extractorResults)
|
output := buildOutputDNS(template, domain, extractorResults)
|
||||||
@ -278,117 +192,3 @@ func (r *Runner) sendRequest(template *templates.Template, request interface{},
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildOutputHTTP builds an output text for writing results
|
|
||||||
func buildOutputHTTP(template *templates.Template, req *retryablehttp.Request, extractorResults []string, matcher *matchers.Matcher) string {
|
|
||||||
builder := &strings.Builder{}
|
|
||||||
builder.WriteRune('[')
|
|
||||||
builder.WriteString(template.ID)
|
|
||||||
if len(matcher.Name) > 0 {
|
|
||||||
builder.WriteString(":")
|
|
||||||
builder.WriteString(matcher.Name)
|
|
||||||
}
|
|
||||||
builder.WriteString("] ")
|
|
||||||
|
|
||||||
// Escape the URL by replacing all % with %%
|
|
||||||
URL := req.URL.String()
|
|
||||||
escapedURL := strings.Replace(URL, "%", "%%", -1)
|
|
||||||
builder.WriteString(escapedURL)
|
|
||||||
|
|
||||||
// If any extractors, write the results
|
|
||||||
if len(extractorResults) > 0 {
|
|
||||||
builder.WriteString(" [")
|
|
||||||
for i, result := range extractorResults {
|
|
||||||
builder.WriteString(result)
|
|
||||||
if i != len(extractorResults)-1 {
|
|
||||||
builder.WriteRune(',')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.WriteString("]")
|
|
||||||
}
|
|
||||||
builder.WriteRune('\n')
|
|
||||||
|
|
||||||
return builder.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildOutput builds an output text for writing results
|
|
||||||
func buildOutputDNS(template *templates.Template, domain string, extractorResults []string) string {
|
|
||||||
builder := &strings.Builder{}
|
|
||||||
builder.WriteRune('[')
|
|
||||||
builder.WriteString(template.ID)
|
|
||||||
builder.WriteString("] [dns] ")
|
|
||||||
|
|
||||||
builder.WriteString(domain)
|
|
||||||
|
|
||||||
// If any extractors, write the results
|
|
||||||
if len(extractorResults) > 0 {
|
|
||||||
builder.WriteString(" [")
|
|
||||||
for i, result := range extractorResults {
|
|
||||||
builder.WriteString(result)
|
|
||||||
if i != len(extractorResults)-1 {
|
|
||||||
builder.WriteRune(',')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.WriteString("]")
|
|
||||||
}
|
|
||||||
builder.WriteRune('\n')
|
|
||||||
|
|
||||||
return builder.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeHTTPClient creates a HTTP client with configurable redirect field
|
|
||||||
func (r *Runner) makeHTTPClientByRequest(request interface{}) *retryablehttp.Client {
|
|
||||||
|
|
||||||
redirects := false
|
|
||||||
maxRedirects := 0
|
|
||||||
// Request is HTTP
|
|
||||||
if httpRequest, ok := request.(requests.HTTPRequest); ok {
|
|
||||||
redirects = httpRequest.Redirects
|
|
||||||
maxRedirects = httpRequest.MaxRedirects
|
|
||||||
}
|
|
||||||
|
|
||||||
retryablehttpOptions := retryablehttp.DefaultOptionsSpraying
|
|
||||||
retryablehttpOptions.RetryWaitMax = 10 * time.Second
|
|
||||||
retryablehttpOptions.RetryMax = r.options.Retries
|
|
||||||
|
|
||||||
// Create the HTTP Client
|
|
||||||
client := retryablehttp.NewWithHTTPClient(&http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
MaxIdleConnsPerHost: -1,
|
|
||||||
TLSClientConfig: &tls.Config{
|
|
||||||
Renegotiation: tls.RenegotiateOnceAsClient,
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
},
|
|
||||||
DisableKeepAlives: true,
|
|
||||||
},
|
|
||||||
Timeout: time.Duration(r.options.Timeout) * time.Second,
|
|
||||||
CheckRedirect: func(_ *http.Request, requests []*http.Request) error {
|
|
||||||
if !redirects {
|
|
||||||
return http.ErrUseLastResponse
|
|
||||||
}
|
|
||||||
if maxRedirects == 0 {
|
|
||||||
if len(requests) > 10 {
|
|
||||||
return http.ErrUseLastResponse
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if len(requests) > maxRedirects {
|
|
||||||
return http.ErrUseLastResponse
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}, retryablehttpOptions)
|
|
||||||
client.CheckRetry = retryablehttp.HostSprayRetryPolicy()
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeHTTPClient creates a HTTP client with configurable redirect field
|
|
||||||
func (r *Runner) makeDNSClientByRequest(request interface{}) *retryabledns.Client {
|
|
||||||
retries := r.options.Retries
|
|
||||||
if dnsRequest, ok := request.(*requests.DNSRequest); ok {
|
|
||||||
retries = dnsRequest.Retries
|
|
||||||
}
|
|
||||||
|
|
||||||
dnsClient, _ := retryabledns.New(DefaultResolvers, retries)
|
|
||||||
return dnsClient
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,38 +1,11 @@
|
|||||||
package runner
|
package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/asaskevich/govalidator"
|
"github.com/asaskevich/govalidator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// unsafeToString converts byte slice to string with zero allocations
|
|
||||||
func unsafeToString(bs []byte) string {
|
|
||||||
return *(*string)(unsafe.Pointer(&bs))
|
|
||||||
}
|
|
||||||
|
|
||||||
// headersToString converts http headers to string
|
|
||||||
func headersToString(headers http.Header) string {
|
|
||||||
builder := &strings.Builder{}
|
|
||||||
|
|
||||||
for header, values := range headers {
|
|
||||||
builder.WriteString(header)
|
|
||||||
builder.WriteString(": ")
|
|
||||||
|
|
||||||
for i, value := range values {
|
|
||||||
builder.WriteString(value)
|
|
||||||
if i != len(values)-1 {
|
|
||||||
builder.WriteRune(',')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.WriteRune('\n')
|
|
||||||
}
|
|
||||||
return builder.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// isURL tests a string to determine if it is a well-structured url or not.
|
// isURL tests a string to determine if it is a well-structured url or not.
|
||||||
func isURL(toTest string) bool {
|
func isURL(toTest string) bool {
|
||||||
_, err := url.ParseRequestURI(toTest)
|
_, err := url.ParseRequestURI(toTest)
|
||||||
@ -44,17 +17,17 @@ func isURL(toTest string) bool {
|
|||||||
if err != nil || u.Scheme == "" || u.Host == "" {
|
if err != nil || u.Scheme == "" || u.Host == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractDomain extracts the domain name of a URL
|
||||||
func extractDomain(URL string) string {
|
func extractDomain(URL string) string {
|
||||||
u, err := url.Parse(URL)
|
u, err := url.Parse(URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
hostname := u.Hostname()
|
||||||
return u.Hostname()
|
return hostname
|
||||||
}
|
}
|
||||||
|
|
||||||
// isDNS tests a string to determine if it is a well-structured dns or not
|
// isDNS tests a string to determine if it is a well-structured dns or not
|
||||||
127
pkg/executor/executer_http.go
Normal file
127
pkg/executor/executer_http.go
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/projectdiscovery/gologger"
|
||||||
|
"github.com/projectdiscovery/nuclei/pkg/extractors"
|
||||||
|
"github.com/projectdiscovery/nuclei/pkg/matchers"
|
||||||
|
"github.com/projectdiscovery/nuclei/pkg/requests"
|
||||||
|
"github.com/projectdiscovery/nuclei/pkg/templates"
|
||||||
|
"github.com/projectdiscovery/retryablehttp-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPExecutor is client for performing HTTP requests
|
||||||
|
// for a template.
|
||||||
|
type HTTPExecutor struct {
|
||||||
|
httpClient *retryablehttp.Client
|
||||||
|
template *templates.Template
|
||||||
|
httpRequest *requests.HTTPRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHTTPExecutor creates a new HTTP executor from a template
|
||||||
|
// and a HTTP request query.
|
||||||
|
func NewHTTPExecutor(template *templates.Template, httpRequest *requests.HTTPRequest) *HTTPExecutor {
|
||||||
|
retryablehttpOptions := retryablehttp.DefaultOptionsSpraying
|
||||||
|
retryablehttpOptions.RetryWaitMax = 10 * time.Second
|
||||||
|
retryablehttpOptions.RetryMax = r.options.Retries
|
||||||
|
|
||||||
|
// Create the HTTP Client
|
||||||
|
client := retryablehttp.NewWithHTTPClient(&http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
MaxIdleConnsPerHost: -1,
|
||||||
|
TLSClientConfig: &tls.Config{
|
||||||
|
Renegotiation: tls.RenegotiateOnceAsClient,
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
DisableKeepAlives: true,
|
||||||
|
},
|
||||||
|
Timeout: time.Duration(r.options.Timeout) * time.Second,
|
||||||
|
CheckRedirect: func(_ *http.Request, requests []*http.Request) error {
|
||||||
|
if !httpRequest.Redirects {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
}
|
||||||
|
if httpRequest.MaxRedirects == 0 {
|
||||||
|
if len(requests) > 10 {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(requests) > httpRequest.MaxRedirects {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}, retryablehttpOptions)
|
||||||
|
client.CheckRetry = retryablehttp.HostSprayRetryPolicy()
|
||||||
|
|
||||||
|
executer := &HTTPExecutor{
|
||||||
|
httpClient: client,
|
||||||
|
template: template,
|
||||||
|
httpRequest: httpRequest,
|
||||||
|
}
|
||||||
|
return executer
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecuteHTTP executes the HTTP request on a URL
|
||||||
|
func (e *HTTPExecutor) ExecuteHTTP(URL string) {
|
||||||
|
if !isURL(URL) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile each request for the template based on the URL
|
||||||
|
compiledRequest, err := e.httpRequest.MakeHTTPRequest(URL)
|
||||||
|
if err != nil {
|
||||||
|
gologger.Warningf("[%s] Could not make request %s: %s\n", e.template.ID, URL, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the request to the target servers
|
||||||
|
for _, req := range compiledRequest {
|
||||||
|
resp, err := e.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
if resp != nil {
|
||||||
|
resp.Body.Close()
|
||||||
|
}
|
||||||
|
gologger.Warningf("[%s] Could not send request %s: %s\n", e.template.ID, URL, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
io.Copy(ioutil.Discard, resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
gologger.Warningf("[%s] Could not read body %s: %s\n", e.template.ID, URL, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
body := unsafeToString(data)
|
||||||
|
|
||||||
|
var headers string
|
||||||
|
for _, matcher := range e.httpRequest.Matchers {
|
||||||
|
// Only build the headers string if the matcher asks for it
|
||||||
|
part := matcher.GetPart()
|
||||||
|
if part == matchers.AllPart || part == matchers.HeaderPart && headers == "" {
|
||||||
|
headers = headersToString(resp.Header)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the matcher matched
|
||||||
|
if matcher.Match(resp, body, headers) {
|
||||||
|
// If there is an extractor, run it.
|
||||||
|
var extractorResults []string
|
||||||
|
for _, extractor := range e.httpRequest.Extractors {
|
||||||
|
part := extractor.GetPart()
|
||||||
|
if part == extractors.AllPart || part == extractors.HeaderPart && headers == "" {
|
||||||
|
headers = headersToString(resp.Header)
|
||||||
|
}
|
||||||
|
extractorResults = append(extractorResults, extractor.Extract(body, headers)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
pkg/executor/executor_dns.go
Normal file
75
pkg/executor/executor_dns.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/projectdiscovery/gologger"
|
||||||
|
"github.com/projectdiscovery/nuclei/pkg/requests"
|
||||||
|
"github.com/projectdiscovery/nuclei/pkg/templates"
|
||||||
|
retryabledns "github.com/projectdiscovery/retryabledns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DNSExecutor is a client for performing a DNS request
|
||||||
|
// for a template.
|
||||||
|
type DNSExecutor struct {
|
||||||
|
dnsClient *retryabledns.Client
|
||||||
|
template *templates.Template
|
||||||
|
dnsRequest *requests.DNSRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultResolvers contains the list of resolvers known to be trusted.
|
||||||
|
var DefaultResolvers = []string{
|
||||||
|
"1.1.1.1:53", // Cloudflare
|
||||||
|
"1.0.0.1:53", // Cloudflare
|
||||||
|
"8.8.8.8:53", // Google
|
||||||
|
"8.8.4.4:53", // Google
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSExecutor creates a new DNS executor from a template
|
||||||
|
// and a DNS request query.
|
||||||
|
func NewDNSExecutor(template *templates.Template, dnsRequest *requests.DNSRequest) *DNSExecutor {
|
||||||
|
dnsClient := retryabledns.New(DefaultResolvers, dnsRequest.Retries)
|
||||||
|
|
||||||
|
executer := &DNSExecutor{
|
||||||
|
dnsClient: dnsClient,
|
||||||
|
template: template,
|
||||||
|
dnsRequest: dnsRequest,
|
||||||
|
}
|
||||||
|
return executer
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecuteDNS executes the DNS request on a URL
|
||||||
|
func (e *DNSExecutor) ExecuteDNS(URL string) {
|
||||||
|
// Parse the URL and return domain if URL.
|
||||||
|
var domain string
|
||||||
|
if isURL(URL) {
|
||||||
|
domain = extractDomain(URL)
|
||||||
|
} else {
|
||||||
|
domain = URL
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile each request for the template based on the URL
|
||||||
|
compiledRequest, err := e.dnsRequest.MakeDNSRequest(URL)
|
||||||
|
if err != nil {
|
||||||
|
gologger.Warningf("[%s] Could not make request %s: %s\n", e.template.ID, domain, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the request to the target servers
|
||||||
|
resp, err := e.dnsClient.Do(compiledRequest)
|
||||||
|
if err != nil {
|
||||||
|
gologger.Warningf("[%s] Could not send request %s: %s\n", e.template.ID, domain, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, matcher := range e.dnsRequest.Matchers {
|
||||||
|
// Check if the matcher matched
|
||||||
|
if !matcher.MatchDNS(resp) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is an extractor, run it.
|
||||||
|
var extractorResults []string
|
||||||
|
for _, extractor := range e.dnsRequest.Extractors {
|
||||||
|
extractorResults = append(extractorResults, extractor.ExtractDNS(resp.String())...)
|
||||||
|
}
|
||||||
|
}
|
||||||
31
pkg/executor/http_utils.go
Normal file
31
pkg/executor/http_utils.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// unsafeToString converts byte slice to string with zero allocations
|
||||||
|
func unsafeToString(bs []byte) string {
|
||||||
|
return *(*string)(unsafe.Pointer(&bs))
|
||||||
|
}
|
||||||
|
|
||||||
|
// headersToString converts http headers to string
|
||||||
|
func headersToString(headers http.Header) string {
|
||||||
|
builder := &strings.Builder{}
|
||||||
|
|
||||||
|
for header, values := range headers {
|
||||||
|
builder.WriteString(header)
|
||||||
|
builder.WriteString(": ")
|
||||||
|
|
||||||
|
for i, value := range values {
|
||||||
|
builder.WriteString(value)
|
||||||
|
if i != len(values)-1 {
|
||||||
|
builder.WriteRune(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.WriteRune('\n')
|
||||||
|
}
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
30
pkg/executor/output_dns.go
Normal file
30
pkg/executor/output_dns.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// buildOutput builds an output text for writing results
|
||||||
|
func (e *DNSExecutor) buildOutputDNS(domain string, extractorResults []string) string {
|
||||||
|
builder := &strings.Builder{}
|
||||||
|
builder.WriteRune('[')
|
||||||
|
builder.WriteString(e.template.ID)
|
||||||
|
builder.WriteString("] [dns] ")
|
||||||
|
|
||||||
|
builder.WriteString(domain)
|
||||||
|
|
||||||
|
// If any extractors, write the results
|
||||||
|
if len(extractorResults) > 0 {
|
||||||
|
builder.WriteString(" [")
|
||||||
|
for i, result := range extractorResults {
|
||||||
|
builder.WriteString(result)
|
||||||
|
if i != len(extractorResults)-1 {
|
||||||
|
builder.WriteRune(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.WriteString("]")
|
||||||
|
}
|
||||||
|
builder.WriteRune('\n')
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
41
pkg/executor/output_http.go
Normal file
41
pkg/executor/output_http.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/projectdiscovery/nuclei/pkg/matchers"
|
||||||
|
"github.com/projectdiscovery/retryablehttp-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// buildOutputHTTP builds an output text for writing results
|
||||||
|
func (e *HTTPExecutor) buildOutputHTTP(req *retryablehttp.Request, extractorResults []string, matcher *matchers.Matcher) string {
|
||||||
|
builder := &strings.Builder{}
|
||||||
|
|
||||||
|
builder.WriteRune('[')
|
||||||
|
builder.WriteString(e.template.ID)
|
||||||
|
if len(matcher.Name) > 0 {
|
||||||
|
builder.WriteString(":")
|
||||||
|
builder.WriteString(matcher.Name)
|
||||||
|
}
|
||||||
|
builder.WriteString("] [http] ")
|
||||||
|
|
||||||
|
// Escape the URL by replacing all % with %%
|
||||||
|
URL := req.URL.String()
|
||||||
|
escapedURL := strings.Replace(URL, "%", "%%", -1)
|
||||||
|
builder.WriteString(escapedURL)
|
||||||
|
|
||||||
|
// If any extractors, write the results
|
||||||
|
if len(extractorResults) > 0 {
|
||||||
|
builder.WriteString(" [")
|
||||||
|
for i, result := range extractorResults {
|
||||||
|
builder.WriteString(result)
|
||||||
|
if i != len(extractorResults)-1 {
|
||||||
|
builder.WriteRune(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.WriteString("]")
|
||||||
|
}
|
||||||
|
builder.WriteRune('\n')
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
package requests
|
package requests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
@ -29,7 +28,6 @@ type DNSRequest struct {
|
|||||||
|
|
||||||
// MakeDNSRequest creates a *dns.Request from a request template
|
// MakeDNSRequest creates a *dns.Request from a request template
|
||||||
func (r *DNSRequest) MakeDNSRequest(domain string) (*dns.Msg, error) {
|
func (r *DNSRequest) MakeDNSRequest(domain string) (*dns.Msg, error) {
|
||||||
|
|
||||||
domain = dns.Fqdn(domain)
|
domain = dns.Fqdn(domain)
|
||||||
|
|
||||||
// Build a request on the specified URL
|
// Build a request on the specified URL
|
||||||
@ -40,28 +38,16 @@ func (r *DNSRequest) MakeDNSRequest(domain string) (*dns.Msg, error) {
|
|||||||
var q dns.Question
|
var q dns.Question
|
||||||
|
|
||||||
t := fasttemplate.New(r.Name, "{{", "}}")
|
t := fasttemplate.New(r.Name, "{{", "}}")
|
||||||
q.Name = dns.Fqdn(t.ExecuteString(map[string]interface{}{
|
q.Name = dns.Fqdn(t.ExecuteString(map[string]interface{}{"FQDN": domain}))
|
||||||
"FQDN": domain,
|
q.Qclass = toQClass(r.Class)
|
||||||
}))
|
q.Qtype = toQType(r.Type)
|
||||||
|
|
||||||
qclass, err := toQClass(r.Class)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
q.Qclass = qclass
|
|
||||||
|
|
||||||
qtype, err := toQType(r.Type)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
q.Qtype = qtype
|
|
||||||
|
|
||||||
req.Question = append(req.Question, q)
|
req.Question = append(req.Question, q)
|
||||||
|
|
||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toQType(ttype string) (rtype uint16, err error) {
|
func toQType(ttype string) (rtype uint16) {
|
||||||
ttype = strings.TrimSpace(strings.ToUpper(ttype))
|
ttype = strings.TrimSpace(strings.ToUpper(ttype))
|
||||||
|
|
||||||
switch ttype {
|
switch ttype {
|
||||||
@ -82,14 +68,12 @@ func toQType(ttype string) (rtype uint16, err error) {
|
|||||||
case "AAAA":
|
case "AAAA":
|
||||||
rtype = dns.TypeAAAA
|
rtype = dns.TypeAAAA
|
||||||
default:
|
default:
|
||||||
rtype = dns.TypeNone
|
rtype = dns.TypeA
|
||||||
err = fmt.Errorf("incorrect type")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func toQClass(tclass string) (rclass uint16, err error) {
|
func toQClass(tclass string) (rclass uint16) {
|
||||||
tclass = strings.TrimSpace(strings.ToUpper(tclass))
|
tclass = strings.TrimSpace(strings.ToUpper(tclass))
|
||||||
|
|
||||||
switch tclass {
|
switch tclass {
|
||||||
@ -106,8 +90,8 @@ func toQClass(tclass string) (rclass uint16, err error) {
|
|||||||
case "ANY":
|
case "ANY":
|
||||||
rclass = dns.ClassANY
|
rclass = dns.ClassANY
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("incorrect class")
|
// Use INET by default.
|
||||||
|
rclass = dns.ClassINET
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,9 @@ type HTTPRequest struct {
|
|||||||
// Matchers contains the detection mechanism for the request to identify
|
// Matchers contains the detection mechanism for the request to identify
|
||||||
// whether the request was successful
|
// whether the request was successful
|
||||||
Matchers []*matchers.Matcher `yaml:"matchers,omitempty"`
|
Matchers []*matchers.Matcher `yaml:"matchers,omitempty"`
|
||||||
|
// MatchersCondition is the condition of the matchers
|
||||||
|
// whether to use AND or OR. Default is OR.
|
||||||
|
MatchersCondition string `yaml:"matchers-condition,omitempty"`
|
||||||
// Extractors contains the extraction mechanism for the request to identify
|
// Extractors contains the extraction mechanism for the request to identify
|
||||||
// and extract parts of the response.
|
// and extract parts of the response.
|
||||||
Extractors []*extractors.Extractor `yaml:"extractors,omitempty"`
|
Extractors []*extractors.Extractor `yaml:"extractors,omitempty"`
|
||||||
|
|||||||
@ -10,9 +10,9 @@ type Template struct {
|
|||||||
ID string `yaml:"id"`
|
ID string `yaml:"id"`
|
||||||
// Info contains information about the template
|
// Info contains information about the template
|
||||||
Info Info `yaml:"info"`
|
Info Info `yaml:"info"`
|
||||||
// Request contains the http request to make in the template
|
// RequestHTTP contains the http request to make in the template
|
||||||
RequestsHTTP []*requests.HTTPRequest `yaml:"requests"`
|
RequestsHTTP []*requests.HTTPRequest `yaml:"requests"`
|
||||||
// Request contains the dns request to make in the template
|
// RequestDNS contains the dns request to make in the template
|
||||||
RequestsDNS []*requests.DNSRequest `yaml:"dns"`
|
RequestsDNS []*requests.DNSRequest `yaml:"dns"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user