mirror of https://github.com/daffainfo/nuclei.git
Merge branch 'master' into feature-dsl-query
commit
8b5a2ff828
34
README.md
34
README.md
|
@ -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
1
go.mod
|
@ -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
6
go.sum
|
@ -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=
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue