Merge branch 'master' into feature-dsl-query

dev
Mzack9999 2020-04-28 18:44:13 +02:00 committed by GitHub
commit 8b5a2ff828
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 151 additions and 84 deletions

View File

@ -18,12 +18,12 @@ We have also [open-sourced a dedicated repository](https://github.com/projectdis
- [Features](#features)
- [Usage](#usage)
- [Installation Instructions](#installation-instructions)
- [Prerequisite](#prerequisite)
- [From Binary](#from-binary)
- [From Source](#from-source)
- [Running nuclei](#running-nuclei)
- [1. Running nuclei with single template](#1-running-nuclei-with-a-single-template)
- [2. Running nuclei with multiple template](#2-running-nuclei-with-multiple-templates)
- [1. Running nuclei with a single template.](#1-running-nuclei-with-a-single-template)
- [2. Running nuclei with multiple templates.](#2-running-nuclei-with-multiple-templates)
- [3. Automating nuclei with subfinder and any other similar tool.](#3-automating-nuclei-with-subfinder-and-any-other-similar-tool)
- [Thanks](#thanks)
# Features
@ -46,19 +46,21 @@ nuclei -h
This will display help for the tool. Here are all the switches it supports.
| Flag | Description | Example |
|----------|-------------------------------------------------------|----------------------------|
| -c | Number of concurrent requests (default 10) | nuclei -c 100 |
| -l | List of urls to run templates | nuclei -l urls.txt |
| -t | Templates input file/files to check across hosts | nuclei -t git-core.yaml |
| -t | Templates input file/files to check across hosts | nuclei -t "path/*.yaml" |
| -nC | Don't Use colors in output | nuclei -nC |
| -o | File to save output result (optional) | nuclei -o output.txt |
| -silent | Show only found results in output | nuclei -silent |
| -retries | Number of times to retry a failed request (default 1) | nuclei -retries 1 |
| -timeout | Seconds to wait before timeout (default 5) | nuclei -timeout 5 |
| -v | Show Verbose output | nuclei -v |
| -version | Show version of nuclei | nuclei -version |
| Flag | Description | Example |
|-------------------|-------------------------------------------------------|----------------------------------------------------|
| -c | Number of concurrent requests (default 10) | nuclei -c 100 |
| -l | List of urls to run templates | nuclei -l urls.txt |
| -t | Templates input file/files to check across hosts | nuclei -t git-core.yaml |
| -t | Templates input file/files to check across hosts | nuclei -t "path/*.yaml" |
| -nC | Don't Use colors in output | nuclei -nC |
| -o | File to save output result (optional) | nuclei -o output.txt |
| -silent | Show only found results in output | nuclei -silent |
| -retries | Number of times to retry a failed request (default 1) | nuclei -retries 1 |
| -timeout | Seconds to wait before timeout (default 5) | nuclei -timeout 5 |
| -v | Show Verbose output | nuclei -v |
| -version | Show version of nuclei | nuclei -version |
| -proxy-url | Proxy URL | nuclei -proxy-url http://this.is.a.proxy:8080 |
| -proxy-socks-url | Proxy Socks URL | nuclei -proxy-socks-url this.is.a.proxy.socks:9050 |
# Installation Instructions

1
go.mod
View File

@ -11,5 +11,6 @@ require (
github.com/projectdiscovery/retryabledns v1.0.4
github.com/projectdiscovery/retryablehttp-go v1.0.1
github.com/valyala/fasttemplate v1.1.0
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0
gopkg.in/yaml.v2 v2.2.8
)

6
go.sum
View File

@ -29,13 +29,15 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U=
golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe h1:6fAMxZRR6sl1Uq8U61gxU+kPTs2tR8uOySCbBP7BN/M=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -10,16 +10,18 @@ import (
// Options contains the configuration options for tuning
// the template requesting process.
type Options struct {
Templates string // Signature specifies the template/templates to use
Targets string // Targets specifies the targets to scan using templates.
Threads int // Thread controls the number of concurrent requests to make.
Timeout int // Timeout is the seconds to wait for a response from the server.
Retries int // Retries is the number of times to retry the request
Output string // Output is the file to write found subdomains to.
Silent bool // Silent suppresses any extra text and only writes found URLs on screen.
Version bool // Version specifies if we should just show version and exit
Verbose bool // Verbose flag indicates whether to show verbose output or not
NoColor bool // No-Color disables the colored output.
Templates string // Signature specifies the template/templates to use
Targets string // Targets specifies the targets to scan using templates.
Threads int // Thread controls the number of concurrent requests to make.
Timeout int // Timeout is the seconds to wait for a response from the server.
Retries int // Retries is the number of times to retry the request
Output string // Output is the file to write found subdomains to.
ProxyURL string // ProxyURL is the URL for the proxy server
ProxySocksURL string // ProxySocksURL is the URL for the proxy socks server
Silent bool // Silent suppresses any extra text and only writes found URLs on screen.
Version bool // Version specifies if we should just show version and exit
Verbose bool // Verbose flag indicates whether to show verbose output or not
NoColor bool // No-Color disables the colored output.
Stdin bool // Stdin specifies whether stdin input was given to the process
}
@ -31,6 +33,8 @@ func ParseOptions() *Options {
flag.StringVar(&options.Templates, "t", "", "Template input file/files to run on host")
flag.StringVar(&options.Targets, "l", "", "List of URLs to run templates on")
flag.StringVar(&options.Output, "o", "", "File to write output to (optional)")
flag.StringVar(&options.ProxyURL, "proxy-url", "", "URL of the proxy server")
flag.StringVar(&options.ProxySocksURL, "proxy-socks-url", "", "URL of the proxy socks server")
flag.BoolVar(&options.Silent, "silent", false, "Show only results in output")
flag.BoolVar(&options.Version, "version", false, "Show version of nuclei")
flag.BoolVar(&options.Verbose, "v", false, "Show Verbose output")

View File

@ -139,9 +139,6 @@ func (r *Runner) processTemplateWithList(template *templates.Template, request i
}
gologger.Infof("%s\n", message)
limiter := make(chan struct{}, r.options.Threads)
wg := &sync.WaitGroup{}
var writer *bufio.Writer
if r.output != nil {
writer = bufio.NewWriter(r.output)
@ -150,6 +147,7 @@ func (r *Runner) processTemplateWithList(template *templates.Template, request i
var httpExecutor *executor.HTTPExecutor
var dnsExecutor *executor.DNSExecutor
var err error
// Create an executor based on the request type.
switch value := request.(type) {
@ -160,14 +158,23 @@ func (r *Runner) processTemplateWithList(template *templates.Template, request i
Writer: writer,
})
case *requests.HTTPRequest:
httpExecutor = executor.NewHTTPExecutor(&executor.HTTPOptions{
Template: template,
HTTPRequest: value,
Writer: writer,
Timeout: r.options.Timeout,
Retries: r.options.Retries,
httpExecutor, err = executor.NewHTTPExecutor(&executor.HTTPOptions{
Template: template,
HTTPRequest: value,
Writer: writer,
Timeout: r.options.Timeout,
Retries: r.options.Retries,
ProxyURL: r.options.ProxyURL,
ProxySocksURL: r.options.ProxySocksURL,
})
}
if err != nil {
gologger.Warningf("Could not create http client: %s\n", err)
return
}
limiter := make(chan struct{}, r.options.Threads)
wg := &sync.WaitGroup{}
scanner := bufio.NewScanner(reader)
for scanner.Scan() {

View File

@ -6,6 +6,7 @@ import (
"io"
"io/ioutil"
"net/http"
"net/url"
"sync"
"time"
@ -15,6 +16,7 @@ import (
"github.com/projectdiscovery/nuclei/pkg/requests"
"github.com/projectdiscovery/nuclei/pkg/templates"
"github.com/projectdiscovery/retryablehttp-go"
"golang.org/x/net/proxy"
)
// HTTPExecutor is client for performing HTTP requests
@ -29,49 +31,30 @@ type HTTPExecutor struct {
// HTTPOptions contains configuration options for the HTTP executor.
type HTTPOptions struct {
Template *templates.Template
HTTPRequest *requests.HTTPRequest
Writer *bufio.Writer
Timeout int
Retries int
Template *templates.Template
HTTPRequest *requests.HTTPRequest
Writer *bufio.Writer
Timeout int
Retries int
ProxyURL string
ProxySocksURL string
}
// NewHTTPExecutor creates a new HTTP executor from a template
// and a HTTP request query.
func NewHTTPExecutor(options *HTTPOptions) *HTTPExecutor {
retryablehttpOptions := retryablehttp.DefaultOptionsSpraying
retryablehttpOptions.RetryWaitMax = 10 * time.Second
retryablehttpOptions.RetryMax = options.Retries
followRedirects := options.HTTPRequest.Redirects
maxRedirects := options.HTTPRequest.MaxRedirects
func NewHTTPExecutor(options *HTTPOptions) (*HTTPExecutor, error) {
var proxyURL *url.URL
var err error
if options.ProxyURL != "" {
proxyURL, err = url.Parse(options.ProxyURL)
}
if err != nil {
return nil, err
}
// 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(options.Timeout) * time.Second,
CheckRedirect: func(_ *http.Request, requests []*http.Request) error {
if !followRedirects {
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 := makeHTTPClient(proxyURL, options)
client.CheckRetry = retryablehttp.HostSprayRetryPolicy()
executer := &HTTPExecutor{
@ -81,7 +64,7 @@ func NewHTTPExecutor(options *HTTPOptions) *HTTPExecutor {
outputMutex: &sync.Mutex{},
writer: options.Writer,
}
return executer
return executer, nil
}
// ExecuteHTTP executes the HTTP request on a URL
@ -146,7 +129,9 @@ mainLoop:
if part == extractors.AllPart || part == extractors.HeaderPart && headers == "" {
headers = headersToString(resp.Header)
}
extractorResults = append(extractorResults, extractor.Extract(body, headers)...)
for match := range extractor.Extract(body, headers) {
extractorResults = append(extractorResults, match)
}
}
// Write a final string of output if matcher type is
@ -164,3 +149,58 @@ func (e *HTTPExecutor) Close() {
e.writer.Flush()
e.outputMutex.Unlock()
}
// makeHTTPClient creates a http client
func makeHTTPClient(proxyURL *url.URL, options *HTTPOptions) *retryablehttp.Client {
retryablehttpOptions := retryablehttp.DefaultOptionsSpraying
retryablehttpOptions.RetryWaitMax = 10 * time.Second
retryablehttpOptions.RetryMax = options.Retries
followRedirects := options.HTTPRequest.Redirects
maxRedirects := options.HTTPRequest.MaxRedirects
transport := &http.Transport{
MaxIdleConnsPerHost: -1,
TLSClientConfig: &tls.Config{
Renegotiation: tls.RenegotiateOnceAsClient,
InsecureSkipVerify: true,
},
DisableKeepAlives: true,
}
// Attempts to overwrite the dial function with the socks proxied version
if options.ProxySocksURL != "" {
dialer, err := proxy.SOCKS5("tcp", options.ProxySocksURL, nil, proxy.Direct)
if err == nil {
transport.Dial = dialer.Dial
}
}
if proxyURL != nil {
transport.Proxy = http.ProxyURL(proxyURL)
}
return retryablehttp.NewWithHTTPClient(&http.Client{
Transport: transport,
Timeout: time.Duration(options.Timeout) * time.Second,
CheckRedirect: makeCheckRedirectFunc(followRedirects, maxRedirects),
}, retryablehttpOptions)
}
type checkRedirectFunc func(_ *http.Request, requests []*http.Request) error
func makeCheckRedirectFunc(followRedirects bool, maxRedirects int) checkRedirectFunc {
return func(_ *http.Request, requests []*http.Request) error {
if !followRedirects {
return http.ErrUseLastResponse
}
if maxRedirects == 0 {
if len(requests) > 10 {
return http.ErrUseLastResponse
}
return nil
}
if len(requests) > maxRedirects {
return http.ErrUseLastResponse
}
return nil
}
}

View File

@ -94,7 +94,9 @@ func (e *DNSExecutor) ExecuteDNS(URL string) error {
// next task which is extraction of input from matchers.
var extractorResults []string
for _, extractor := range e.dnsRequest.Extractors {
extractorResults = append(extractorResults, extractor.ExtractDNS(resp.String())...)
for match := range extractor.ExtractDNS(resp.String()) {
extractorResults = append(extractorResults, match)
}
}
// Write a final string of output if matcher type is

View File

@ -1,7 +1,7 @@
package extractors
// Extract extracts response from the parts of request using a regex
func (e *Extractor) Extract(body, headers string) []string {
func (e *Extractor) Extract(body, headers string) map[string]struct{} {
// Match the parts as required for regex check
if e.part == BodyPart {
return e.extractRegex(body)
@ -17,16 +17,20 @@ func (e *Extractor) Extract(body, headers string) []string {
}
// ExtractDNS extracts response from dns message using a regex
func (e *Extractor) ExtractDNS(msg string) []string {
func (e *Extractor) ExtractDNS(msg string) map[string]struct{} {
// Match the parts as required for regex check
return e.extractRegex(msg)
}
// extractRegex extracts text from a corpus and returns it
func (e *Extractor) extractRegex(corpus string) []string {
results := []string{}
func (e *Extractor) extractRegex(corpus string) map[string]struct{} {
results := make(map[string]struct{})
for _, regex := range e.regexCompiled {
results = append(results, regex.FindAllString(corpus, -1)...)
matches := regex.FindAllString(corpus, -1)
for _, match := range matches {
results[match] = struct{}{}
}
}
return results
}

View File

@ -117,7 +117,7 @@ func (m *Matcher) matchWords(corpus string) bool {
// Iterate over all the words accepted as valid
for i, word := range m.Words {
// Continue if the word doesn't match
if !strings.Contains(corpus, word) {
if strings.Index(corpus, word) == -1 {
// If we are in an AND request and a match failed,
// return false as the AND condition fails on any single mismatch.
if m.condition == ANDCondition {

View File

@ -79,7 +79,12 @@ func (r *HTTPRequest) MakeHTTPRequest(baseURL string) ([]*retryablehttp.Request,
// Set the header values requested
for header, value := range r.Headers {
req.Header.Set(header, value)
t := fasttemplate.New(value, "{{", "}}")
val := t.ExecuteString(map[string]interface{}{
"BaseURL": baseURL,
"Hostname": hostname,
})
req.Header.Set(header, val)
}
// Set some headers only if the header wasn't supplied by the user