mirror of https://github.com/daffainfo/nuclei.git
merging master
commit
173b55af92
|
@ -59,7 +59,6 @@ linters:
|
|||
- bodyclose
|
||||
- deadcode
|
||||
- dogsled
|
||||
- dupl
|
||||
- errcheck
|
||||
- exhaustive
|
||||
- gochecknoinits
|
||||
|
|
59
README.md
59
README.md
|
@ -36,6 +36,7 @@ We have also [open-sourced a template repository](https://github.com/projectdisc
|
|||
- [Running with multiple templates.](#running-with-multiple-templates)
|
||||
- [Running with subfinder](#running-with-subfinder)
|
||||
- [Running in Docker](#running-in-docker-container)
|
||||
- [Template exclusion](#template-exclusion)
|
||||
- [Thanks](#thanks)
|
||||
|
||||
|
||||
|
@ -76,8 +77,8 @@ This will display help for the tool. Here are all the switches it supports.
|
|||
| -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 |
|
||||
| -rl | Rate-Limit of requests per specified target | nuclei -rl 100 |
|
||||
| -severity |Filter templates based on their severity and only run the matching ones| nuclei -severity critical, low |
|
||||
| -exclude |Template input dir/file/files to exclude | nuclei -exclude panels, tokens |
|
||||
| -severity |Run templates based on severity | nuclei -severity critical, low |
|
||||
| -exclude |Template input dir/file/files to exclude | nuclei -exclude panels -exclude tokens |
|
||||
| -debug | Allow debugging of request/responses. | nuclei -debug |
|
||||
| -update-templates | Download and updates nuclei templates | nuclei -update-templates |
|
||||
| -update-directory | Directory for storing nuclei-templates(optional) | nuclei -update-directory templates |
|
||||
|
@ -113,7 +114,7 @@ nuclei requires **go1.14+** to install successfully. Run the following command t
|
|||
### From Github
|
||||
|
||||
```sh
|
||||
▶ git clone https://github.com/projectdiscovery/nuclei.git; cd nuclei/v2/cmd/nuclei/; go build; mv nuclei /usr/local/bin/; nuclei -h
|
||||
▶ git clone https://github.com/projectdiscovery/nuclei.git; cd nuclei/v2/cmd/nuclei/; go build; mv nuclei /usr/local/bin/; nuclei -version
|
||||
```
|
||||
|
||||
## Nuclei templates
|
||||
|
@ -148,6 +149,13 @@ You can also pass the list of urls at standard input (STDIN). This allows for ea
|
|||
▶ cat urls.txt | nuclei -t files/git-core.yaml -o results.txt
|
||||
```
|
||||
|
||||
💡 Nuclei accepts list of URLs as input, for example here is how `urls.txt` looks like:-
|
||||
|
||||
```
|
||||
https://test.some-site.com
|
||||
http://vuls-testing.com
|
||||
https://test.com
|
||||
```
|
||||
### Running with multiple templates.
|
||||
|
||||
This will run the tool against all the urls in `urls.txt` with all the templates in the `cves` and `files` directory and returns the matched results.
|
||||
|
@ -184,8 +192,53 @@ For example, this will run the tool against all the hosts in `urls.txt` and outp
|
|||
|
||||
Remember to change `/path-to-nuclei-templates` to the real path on your host file system.
|
||||
|
||||
### Template Exclusion
|
||||
|
||||
[Nuclei-templates](https://github.com/projectdiscovery/nuclei-templates) includes multiple checks including many that are useful for attack surface mapping and not necessarily a security issue, in cases where you only looking to scan few specific templates or directory, here are few options / flags to filter or exclude them from running.
|
||||
|
||||
#### Running templates based on severity
|
||||
|
||||
You can run the templates based on the specific severity of the template, single and multiple severity can be used for scan.
|
||||
|
||||
```sh
|
||||
nuclei -l urls.txt -t cves/ -severity critical, medium
|
||||
```
|
||||
|
||||
The above example will run all the templates under `cves` directory with `critical` and `medium` severity.
|
||||
|
||||
```sh
|
||||
nuclei -l urls.txt -t panels/ -t technologies -severity info
|
||||
```
|
||||
|
||||
The above example will run all the templates under `panels` and `technologies` directory with **severity** marked as `info`
|
||||
|
||||
#### Running templates with exclusion
|
||||
|
||||
We do not suggest running all the nuclei-templates directory at once, in case of doing so, one can make use of `exclude` flag to exclude specific directory or templates to ignore from scanning.
|
||||
|
||||
```sh
|
||||
nuclei -l urls.txt -t nuclei-templates -exclude panels/ -exclude technologies -exclude files/wp-xmlrpc.yaml
|
||||
```
|
||||
|
||||
Note:- both directory and specific templates case be excluded from scan as shared in the above example.
|
||||
|
||||
#### Using `.nuclei-ignore` file for template exclusion
|
||||
|
||||
Since release of nuclei [v2.1.1](https://github.com/projectdiscovery/nuclei/releases/tag/v2.1.1), we have added support of `.nuclei-ignore` file that works along with `update-templates` flag of nuclei, in **.nuclei-ignore** file, you can define all the template directory or template path that you wanted to exclude from all the nuclei scans, to start using this feature, make sure you installed nuclei templates using `nuclei -update-templates` flag, now you can add/update/remove templates in the file that you wanted to exclude from running.
|
||||
|
||||
```
|
||||
nano ~/nuclei-templates/.nuclei-ignore
|
||||
```
|
||||
|
||||
Default **nuclei-ignore** list can be accessed from [here](https://github.com/projectdiscovery/nuclei-templates/blob/master/.nuclei-ignore), in case you don't want to exclude anything, simply remove the `.nuclei-ignore` file.
|
||||
|
||||
* * *
|
||||
|
||||
# 📋 Notes
|
||||
- Progress bar is experimental feature, might not work in few cases.
|
||||
- Progress bar doesn't work with workflows, numbers are not accurate due to conditional execution.
|
||||
|
||||
|
||||
## Thanks
|
||||
|
||||
nuclei is made with 🖤 by the [projectdiscovery](https://projectdiscovery.io) team. Community contributions have made the project what it is. See the **[Thanks.md](https://github.com/projectdiscovery/nuclei/blob/master/THANKS.md)** file for more details.
|
||||
|
|
|
@ -27,7 +27,7 @@ const nucleiConfigFilename = ".nuclei-config.json"
|
|||
var reVersion = regexp.MustCompile(`\d+\.\d+\.\d+`)
|
||||
|
||||
// readConfiguration reads the nuclei configuration file from disk.
|
||||
func (r *Runner) readConfiguration() (*nucleiConfig, error) {
|
||||
func readConfiguration() (*nucleiConfig, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -13,20 +13,6 @@ import (
|
|||
// Options contains the configuration options for tuning
|
||||
// the template requesting process.
|
||||
type Options struct {
|
||||
Templates multiStringFlag // Signature specifies the template/templates to use
|
||||
ExcludedTemplates multiStringFlag // Signature specifies the template/templates to exclude
|
||||
CustomHeaders requests.CustomHeaders // Custom global headers
|
||||
Severity string // Filter templates based on their severity and only run the matching ones.
|
||||
Target string // Target is a single URL/Domain to scan usng a template
|
||||
Targets string // Targets specifies the targets to scan using templates.
|
||||
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
|
||||
TemplatesDirectory string // TemplatesDirectory is the directory to use for storing 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
|
||||
RateLimit int // Rate-Limit of requests per specified target
|
||||
Debug bool // Debug mode allows debugging request/responses for the engine
|
||||
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
|
||||
|
@ -36,13 +22,28 @@ type Options struct {
|
|||
JSON bool // JSON writes json output to files
|
||||
JSONRequests bool // write requests/responses for matches in JSON output
|
||||
EnableProgressBar bool // Enable progrss bar
|
||||
TemplatesVersion bool // Show the templates installed version
|
||||
TemplateList bool // List available templates
|
||||
Stdin bool // Stdin specifies whether stdin input was given to the process
|
||||
StopAtFirstMatch bool // Stop processing template at first full match (this may break chained requests)
|
||||
NoMeta bool // Don't display metadata for the matches
|
||||
BulkSize int // Number of targets analyzed in parallel for each template
|
||||
TemplateThreads int // Number of templates executed in parallel
|
||||
TemplateThreads int // Number of templates executed in parallel
|
||||
Project bool // Nuclei uses project folder to avoid sending same HTTP request multiple times
|
||||
ProjectPath string // Nuclei uses a user defined project folder
|
||||
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
|
||||
RateLimit int // Rate-Limit of requests per specified target
|
||||
Severity string // Filter templates based on their severity and only run the matching ones.
|
||||
Target string // Target is a single URL/Domain to scan usng a template
|
||||
Targets string // Targets specifies the targets to scan using templates.
|
||||
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
|
||||
TemplatesDirectory string // TemplatesDirectory is the directory to use for storing templates
|
||||
Templates multiStringFlag // Signature specifies the template/templates to use
|
||||
ExcludedTemplates multiStringFlag // Signature specifies the template/templates to exclude
|
||||
CustomHeaders requests.CustomHeaders // Custom global headers
|
||||
}
|
||||
|
||||
type multiStringFlag []string
|
||||
|
@ -82,13 +83,14 @@ func ParseOptions() *Options {
|
|||
flag.BoolVar(&options.JSONRequests, "json-requests", false, "Write requests/responses for matches in JSON output")
|
||||
flag.BoolVar(&options.EnableProgressBar, "pbar", false, "Enable the progress bar")
|
||||
flag.BoolVar(&options.TemplateList, "tl", false, "List available templates")
|
||||
flag.IntVar(&options.RateLimit, "rate-limit", -1, "Per Target Rate-Limit")
|
||||
flag.IntVar(&options.RateLimit, "rate-limit", 150, "Rate-Limit Per Target (maximum requests/second")
|
||||
flag.BoolVar(&options.StopAtFirstMatch, "stop-at-first-match", false, "Stop processing http requests at first match (this may break template/workflow logic)")
|
||||
flag.IntVar(&options.BulkSize, "bulk-size", 25, "Number of hosts analyzed in parallel per template")
|
||||
flag.IntVar(&options.TemplateThreads, "c", 10, "Number of templates executed in parallel")
|
||||
flag.IntVar(&options.BulkSize, "bulk-size", 25, "Maximum Number of hosts analyzed in parallel per template")
|
||||
flag.IntVar(&options.TemplateThreads, "c", 10, "Maximum Number of templates executed in parallel")
|
||||
flag.BoolVar(&options.Project, "project", false, "Use a project folder to avoid sending same request multiple times")
|
||||
flag.StringVar(&options.ProjectPath, "project-path", "", "Use a user defined project folder, temporary folder is used if not specified but enabled")
|
||||
|
||||
flag.BoolVar(&options.NoMeta, "no-meta", false, "Don't display metadata for the matches")
|
||||
flag.BoolVar(&options.TemplatesVersion, "templates-version", false, "Shows the installed nuclei-templates version")
|
||||
flag.Parse()
|
||||
|
||||
// Check if stdin pipe was given
|
||||
|
@ -104,6 +106,14 @@ func ParseOptions() *Options {
|
|||
gologger.Infof("Current Version: %s\n", Version)
|
||||
os.Exit(0)
|
||||
}
|
||||
if options.TemplatesVersion {
|
||||
config, err := readConfiguration()
|
||||
if err != nil {
|
||||
gologger.Fatalf("Could not read template configuration: %s\n", err)
|
||||
}
|
||||
gologger.Infof("Current nuclei-templates version: %s (%s)\n", config.CurrentVersion, config.TemplatesDirectory)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// Validate the options passed by the user and if any
|
||||
// invalid options have been used, exit.
|
||||
|
|
|
@ -45,6 +45,7 @@ func (r *Runner) processTemplateWithList(p progress.IProgress, template *templat
|
|||
Writer: r.output,
|
||||
JSON: r.options.JSON,
|
||||
JSONRequests: r.options.JSONRequests,
|
||||
NoMeta: r.options.NoMeta,
|
||||
ColoredOutput: !r.options.NoColor,
|
||||
Colorizer: r.colorizer,
|
||||
Decolorizer: r.decolorizer,
|
||||
|
@ -62,6 +63,7 @@ func (r *Runner) processTemplateWithList(p progress.IProgress, template *templat
|
|||
CustomHeaders: r.options.CustomHeaders,
|
||||
JSON: r.options.JSON,
|
||||
JSONRequests: r.options.JSONRequests,
|
||||
NoMeta: r.options.NoMeta,
|
||||
CookieReuse: value.CookieReuse,
|
||||
ColoredOutput: !r.options.NoColor,
|
||||
Colorizer: &r.colorizer,
|
||||
|
|
|
@ -136,16 +136,16 @@ func (r *Runner) getParsedTemplatesFor(templatePaths []string, severities string
|
|||
switch tp := t.(type) {
|
||||
case *templates.Template:
|
||||
// only include if severity matches or no severity filtering
|
||||
sev := strings.ToLower(tp.Info.Severity)
|
||||
sev := strings.ToLower(tp.Info["severity"])
|
||||
if !filterBySeverity || hasMatchingSeverity(sev, allSeverities) {
|
||||
parsedTemplates = append(parsedTemplates, tp)
|
||||
gologger.Infof("%s\n", r.templateLogMsg(tp.ID, tp.Info.Name, tp.Info.Author, tp.Info.Severity))
|
||||
gologger.Infof("%s\n", r.templateLogMsg(tp.ID, tp.Info["name"], tp.Info["author"], tp.Info["severity"]))
|
||||
} else {
|
||||
gologger.Warningf("Excluding template %s due to severity filter (%s not in [%s])", tp.ID, sev, severities)
|
||||
}
|
||||
case *workflows.Workflow:
|
||||
parsedTemplates = append(parsedTemplates, tp)
|
||||
gologger.Infof("%s\n", r.templateLogMsg(tp.ID, tp.Info.Name, tp.Info.Author, tp.Info.Severity))
|
||||
gologger.Infof("%s\n", r.templateLogMsg(tp.ID, tp.Info["name"], tp.Info["author"], tp.Info["severity"]))
|
||||
workflowCount++
|
||||
default:
|
||||
gologger.Errorf("Could not parse file '%s': %s\n", match, err)
|
||||
|
@ -198,9 +198,9 @@ func (r *Runner) logAvailableTemplate(tplPath string) {
|
|||
if t != nil {
|
||||
switch tp := t.(type) {
|
||||
case *templates.Template:
|
||||
gologger.Silentf("%s\n", r.templateLogMsg(tp.ID, tp.Info.Name, tp.Info.Author, tp.Info.Severity))
|
||||
gologger.Silentf("%s\n", r.templateLogMsg(tp.ID, tp.Info["name"], tp.Info["author"], tp.Info["severity"]))
|
||||
case *workflows.Workflow:
|
||||
gologger.Silentf("%s\n", r.templateLogMsg(tp.ID, tp.Info.Name, tp.Info.Author, tp.Info.Severity))
|
||||
gologger.Silentf("%s\n", r.templateLogMsg(tp.ID, tp.Info["name"], tp.Info["author"], tp.Info["severity"]))
|
||||
default:
|
||||
gologger.Errorf("Could not parse file '%s': %s\n", tplPath, err)
|
||||
}
|
||||
|
|
|
@ -39,12 +39,10 @@ func (r *Runner) updateTemplates() error {
|
|||
|
||||
templatesConfigFile := path.Join(home, nucleiConfigFilename)
|
||||
if _, statErr := os.Stat(templatesConfigFile); !os.IsNotExist(statErr) {
|
||||
config, readErr := r.readConfiguration()
|
||||
|
||||
if readErr != nil {
|
||||
config, readErr := readConfiguration()
|
||||
if err != nil {
|
||||
return readErr
|
||||
}
|
||||
|
||||
r.templatesConfig = config
|
||||
}
|
||||
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
package executer
|
||||
|
||||
import "net/url"
|
||||
|
||||
// isURL tests a string to determine if it is a well-structured url or not.
|
||||
func isURL(toTest string) bool {
|
||||
_, err := url.ParseRequestURI(toTest)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
u, err := url.Parse(toTest)
|
||||
if err != nil || u.Scheme == "" || u.Host == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// extractDomain extracts the domain name of a URL
|
||||
func extractDomain(theURL string) string {
|
||||
u, err := url.Parse(theURL)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
hostname := u.Hostname()
|
||||
|
||||
return hostname
|
||||
}
|
|
@ -25,6 +25,7 @@ type DNSExecuter struct {
|
|||
debug bool
|
||||
jsonOutput bool
|
||||
jsonRequest bool
|
||||
noMeta bool
|
||||
Results bool
|
||||
dnsClient *retryabledns.Client
|
||||
template *templates.Template
|
||||
|
@ -49,6 +50,7 @@ type DNSOptions struct {
|
|||
Debug bool
|
||||
JSON bool
|
||||
JSONRequests bool
|
||||
NoMeta bool
|
||||
Template *templates.Template
|
||||
DNSRequest *requests.DNSRequest
|
||||
Writer *bufwriter.Writer
|
||||
|
@ -64,6 +66,7 @@ func NewDNSExecuter(options *DNSOptions) *DNSExecuter {
|
|||
|
||||
executer := &DNSExecuter{
|
||||
debug: options.Debug,
|
||||
noMeta: options.NoMeta,
|
||||
jsonOutput: options.JSON,
|
||||
jsonRequest: options.JSONRequests,
|
||||
dnsClient: dnsClient,
|
||||
|
|
|
@ -57,6 +57,7 @@ type HTTPExecuter struct {
|
|||
Results bool
|
||||
jsonOutput bool
|
||||
jsonRequest bool
|
||||
noMeta bool
|
||||
stopAtFirstMatch bool
|
||||
}
|
||||
|
||||
|
@ -76,6 +77,7 @@ type HTTPOptions struct {
|
|||
Debug bool
|
||||
JSON bool
|
||||
JSONRequests bool
|
||||
NoMeta bool
|
||||
CookieReuse bool
|
||||
ColoredOutput bool
|
||||
StopAtFirstMatch bool
|
||||
|
@ -122,6 +124,7 @@ func NewHTTPExecuter(options *HTTPOptions) (*HTTPExecuter, error) {
|
|||
debug: options.Debug,
|
||||
jsonOutput: options.JSON,
|
||||
jsonRequest: options.JSONRequests,
|
||||
noMeta: options.NoMeta,
|
||||
httpClient: client,
|
||||
rawHTTPClient: rawClient,
|
||||
template: options.Template,
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
package executer
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type jsonOutput struct {
|
||||
Template string `json:"template"`
|
||||
Type string `json:"type"`
|
||||
Matched string `json:"matched"`
|
||||
MatcherName string `json:"matcher_name,omitempty"`
|
||||
ExtractedResults []string `json:"extracted_results,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Severity string `json:"severity"`
|
||||
Author string `json:"author"`
|
||||
Description string `json:"description"`
|
||||
Request string `json:"request,omitempty"`
|
||||
Response string `json:"response,omitempty"`
|
||||
Meta map[string]interface{} `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
// 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('\n')
|
||||
builder.WriteString(header)
|
||||
builder.WriteString(": ")
|
||||
}
|
||||
}
|
||||
|
||||
builder.WriteRune('\n')
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
|
@ -14,36 +14,32 @@ import (
|
|||
// nolint:interfacer // dns.Msg is out of current scope
|
||||
func (e *DNSExecuter) writeOutputDNS(domain string, req, resp *dns.Msg, matcher *matchers.Matcher, extractorResults []string) {
|
||||
if e.jsonOutput {
|
||||
output := jsonOutput{
|
||||
Template: e.template.ID,
|
||||
Type: "dns",
|
||||
Matched: domain,
|
||||
Name: e.template.Info.Name,
|
||||
Severity: e.template.Info.Severity,
|
||||
Author: e.template.Info.Author,
|
||||
Description: e.template.Info.Description,
|
||||
}
|
||||
output := make(jsonOutput)
|
||||
output["matched"] = domain
|
||||
|
||||
if matcher != nil && len(matcher.Name) > 0 {
|
||||
output.MatcherName = matcher.Name
|
||||
}
|
||||
|
||||
if len(extractorResults) > 0 {
|
||||
output.ExtractedResults = extractorResults
|
||||
}
|
||||
|
||||
if e.jsonRequest {
|
||||
output.Request = req.String()
|
||||
output.Response = resp.String()
|
||||
if !e.noMeta {
|
||||
output["template"] = e.template.ID
|
||||
output["type"] = "dns"
|
||||
for k, v := range e.template.Info {
|
||||
output[k] = v
|
||||
}
|
||||
if matcher != nil && len(matcher.Name) > 0 {
|
||||
output["matcher_name"] = matcher.Name
|
||||
}
|
||||
if len(extractorResults) > 0 {
|
||||
output["extracted_results"] = extractorResults
|
||||
}
|
||||
if e.jsonRequest {
|
||||
output["request"] = req.String()
|
||||
output["response"] = resp.String()
|
||||
}
|
||||
}
|
||||
|
||||
data, err := jsoniter.Marshal(output)
|
||||
if err != nil {
|
||||
gologger.Warningf("Could not marshal json output: %s\n", err)
|
||||
}
|
||||
|
||||
gologger.Silentf("%s", string(data))
|
||||
|
||||
if e.writer != nil {
|
||||
if err := e.writer.Write(data); err != nil {
|
||||
gologger.Errorf("Could not write output data: %s\n", err)
|
||||
|
@ -56,28 +52,29 @@ func (e *DNSExecuter) writeOutputDNS(domain string, req, resp *dns.Msg, matcher
|
|||
builder := &strings.Builder{}
|
||||
colorizer := e.colorizer
|
||||
|
||||
builder.WriteRune('[')
|
||||
builder.WriteString(colorizer.Colorizer.BrightGreen(e.template.ID).String())
|
||||
if !e.noMeta {
|
||||
builder.WriteRune('[')
|
||||
builder.WriteString(colorizer.Colorizer.BrightGreen(e.template.ID).String())
|
||||
|
||||
if matcher != nil && len(matcher.Name) > 0 {
|
||||
builder.WriteString(":")
|
||||
builder.WriteString(colorizer.Colorizer.BrightGreen(matcher.Name).Bold().String())
|
||||
}
|
||||
if matcher != nil && len(matcher.Name) > 0 {
|
||||
builder.WriteString(":")
|
||||
builder.WriteString(colorizer.Colorizer.BrightGreen(matcher.Name).Bold().String())
|
||||
}
|
||||
|
||||
builder.WriteString("] [")
|
||||
builder.WriteString(colorizer.Colorizer.BrightBlue("dns").String())
|
||||
builder.WriteString("] ")
|
||||
|
||||
if e.template.Info.Severity != "" {
|
||||
builder.WriteString("[")
|
||||
builder.WriteString(colorizer.GetColorizedSeverity(e.template.Info.Severity))
|
||||
builder.WriteString("] [")
|
||||
builder.WriteString(colorizer.Colorizer.BrightBlue("dns").String())
|
||||
builder.WriteString("] ")
|
||||
}
|
||||
|
||||
if e.template.Info["severity"] != "" {
|
||||
builder.WriteString("[")
|
||||
builder.WriteString(colorizer.GetColorizedSeverity(e.template.Info["severity"]))
|
||||
builder.WriteString("] ")
|
||||
}
|
||||
}
|
||||
builder.WriteString(domain)
|
||||
|
||||
// If any extractors, write the results
|
||||
if len(extractorResults) > 0 {
|
||||
if len(extractorResults) > 0 && !e.noMeta {
|
||||
builder.WriteString(" [")
|
||||
|
||||
for i, result := range extractorResults {
|
||||
|
@ -87,10 +84,8 @@ func (e *DNSExecuter) writeOutputDNS(domain string, req, resp *dns.Msg, matcher
|
|||
builder.WriteRune(',')
|
||||
}
|
||||
}
|
||||
|
||||
builder.WriteString("]")
|
||||
}
|
||||
|
||||
builder.WriteRune('\n')
|
||||
|
||||
// Write output to screen as well as any output file
|
||||
|
|
|
@ -14,59 +14,55 @@ import (
|
|||
// writeOutputHTTP writes http output to streams
|
||||
func (e *HTTPExecuter) writeOutputHTTP(req *requests.HTTPRequest, resp *http.Response, body string, matcher *matchers.Matcher, extractorResults []string, meta map[string]interface{}) {
|
||||
var URL string
|
||||
// rawhttp
|
||||
if req.RawRequest != nil {
|
||||
URL = req.RawRequest.FullURL
|
||||
}
|
||||
// retryablehttp
|
||||
if req.Request != nil {
|
||||
URL = req.Request.URL.String()
|
||||
}
|
||||
|
||||
if e.jsonOutput {
|
||||
output := jsonOutput{
|
||||
Template: e.template.ID,
|
||||
Type: "http",
|
||||
Matched: URL,
|
||||
Name: e.template.Info.Name,
|
||||
Severity: e.template.Info.Severity,
|
||||
Author: e.template.Info.Author,
|
||||
Description: e.template.Info.Description,
|
||||
Meta: meta,
|
||||
}
|
||||
output := make(jsonOutput)
|
||||
|
||||
if matcher != nil && len(matcher.Name) > 0 {
|
||||
output.MatcherName = matcher.Name
|
||||
}
|
||||
|
||||
if len(extractorResults) > 0 {
|
||||
output.ExtractedResults = extractorResults
|
||||
}
|
||||
|
||||
// TODO: URL should be an argument
|
||||
if e.jsonRequest {
|
||||
dumpedRequest, err := requests.Dump(req, URL)
|
||||
if err != nil {
|
||||
gologger.Warningf("could not dump request: %s\n", err)
|
||||
} else {
|
||||
output.Request = string(dumpedRequest)
|
||||
output["matched"] = URL
|
||||
if !e.noMeta {
|
||||
output["template"] = e.template.ID
|
||||
output["type"] = "http"
|
||||
if len(meta) > 0 {
|
||||
output["meta"] = meta
|
||||
}
|
||||
for k, v := range e.template.Info {
|
||||
output[k] = v
|
||||
}
|
||||
if matcher != nil && len(matcher.Name) > 0 {
|
||||
output["matcher_name"] = matcher.Name
|
||||
}
|
||||
if len(extractorResults) > 0 {
|
||||
output["extracted_results"] = extractorResults
|
||||
}
|
||||
|
||||
dumpedResponse, err := httputil.DumpResponse(resp, false)
|
||||
// TODO: URL should be an argument
|
||||
if e.jsonRequest {
|
||||
dumpedRequest, err := requests.Dump(req, URL)
|
||||
if err != nil {
|
||||
gologger.Warningf("could not dump request: %s\n", err)
|
||||
} else {
|
||||
output["request"] = string(dumpedRequest)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
gologger.Warningf("could not dump response: %s\n", err)
|
||||
} else {
|
||||
output.Response = string(dumpedResponse) + body
|
||||
dumpedResponse, err := httputil.DumpResponse(resp, false)
|
||||
if err != nil {
|
||||
gologger.Warningf("could not dump response: %s\n", err)
|
||||
} else {
|
||||
output["response"] = string(dumpedResponse) + body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data, err := jsoniter.Marshal(output)
|
||||
|
||||
if err != nil {
|
||||
gologger.Warningf("Could not marshal json output: %s\n", err)
|
||||
}
|
||||
|
||||
gologger.Silentf("%s", string(data))
|
||||
|
||||
if e.writer != nil {
|
||||
|
@ -75,35 +71,35 @@ func (e *HTTPExecuter) writeOutputHTTP(req *requests.HTTPRequest, resp *http.Res
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
builder := &strings.Builder{}
|
||||
colorizer := e.colorizer
|
||||
|
||||
builder.WriteRune('[')
|
||||
builder.WriteString(colorizer.Colorizer.BrightGreen(e.template.ID).String())
|
||||
if !e.noMeta {
|
||||
builder.WriteRune('[')
|
||||
builder.WriteString(colorizer.Colorizer.BrightGreen(e.template.ID).String())
|
||||
|
||||
if matcher != nil && len(matcher.Name) > 0 {
|
||||
builder.WriteString(":")
|
||||
builder.WriteString(colorizer.Colorizer.BrightGreen(matcher.Name).Bold().String())
|
||||
}
|
||||
if matcher != nil && len(matcher.Name) > 0 {
|
||||
builder.WriteString(":")
|
||||
builder.WriteString(colorizer.Colorizer.BrightGreen(matcher.Name).Bold().String())
|
||||
}
|
||||
|
||||
builder.WriteString("] [")
|
||||
builder.WriteString(colorizer.Colorizer.BrightBlue("http").String())
|
||||
builder.WriteString("] ")
|
||||
|
||||
if e.template.Info.Severity != "" {
|
||||
builder.WriteString("[")
|
||||
builder.WriteString(colorizer.GetColorizedSeverity(e.template.Info.Severity))
|
||||
builder.WriteString("] [")
|
||||
builder.WriteString(colorizer.Colorizer.BrightBlue("http").String())
|
||||
builder.WriteString("] ")
|
||||
}
|
||||
|
||||
if e.template.Info["severity"] != "" {
|
||||
builder.WriteString("[")
|
||||
builder.WriteString(colorizer.GetColorizedSeverity(e.template.Info["severity"]))
|
||||
builder.WriteString("] ")
|
||||
}
|
||||
}
|
||||
builder.WriteString(URL)
|
||||
|
||||
// If any extractors, write the results
|
||||
if len(extractorResults) > 0 {
|
||||
if len(extractorResults) > 0 && !e.noMeta {
|
||||
builder.WriteString(" [")
|
||||
|
||||
for i, result := range extractorResults {
|
||||
|
@ -118,7 +114,7 @@ func (e *HTTPExecuter) writeOutputHTTP(req *requests.HTTPRequest, resp *http.Res
|
|||
}
|
||||
|
||||
// write meta if any
|
||||
if len(req.Meta) > 0 {
|
||||
if len(req.Meta) > 0 && !e.noMeta {
|
||||
builder.WriteString(" [")
|
||||
|
||||
var metas []string
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package executer
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type jsonOutput map[string]interface{}
|
||||
|
||||
// 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('\n')
|
||||
builder.WriteString(header)
|
||||
builder.WriteString(": ")
|
||||
}
|
||||
}
|
||||
builder.WriteRune('\n')
|
||||
}
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// isURL tests a string to determine if it is a well-structured url or not.
|
||||
func isURL(toTest string) bool {
|
||||
_, err := url.ParseRequestURI(toTest)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
u, err := url.Parse(toTest)
|
||||
if err != nil || u.Scheme == "" || u.Host == "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// extractDomain extracts the domain name of a URL
|
||||
func extractDomain(theURL string) string {
|
||||
u, err := url.Parse(theURL)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return u.Hostname()
|
||||
}
|
|
@ -9,7 +9,7 @@ type Template struct {
|
|||
// ID is the unique id for the template
|
||||
ID string `yaml:"id"`
|
||||
// Info contains information about the template
|
||||
Info Info `yaml:"info"`
|
||||
Info map[string]string `yaml:"info"`
|
||||
// BulkRequestsHTTP contains the http request to make in the template
|
||||
BulkRequestsHTTP []*requests.BulkHTTPRequest `yaml:"requests,omitempty"`
|
||||
// RequestsDNS contains the dns request to make in the template
|
||||
|
@ -22,18 +22,6 @@ func (t *Template) GetPath() string {
|
|||
return t.path
|
||||
}
|
||||
|
||||
// Info contains information about the request template
|
||||
type Info struct {
|
||||
// Name is the name of the template
|
||||
Name string `yaml:"name"`
|
||||
// Author is the name of the author of the template
|
||||
Author string `yaml:"author"`
|
||||
// Severity optionally describes the severity of the template
|
||||
Severity string `yaml:"severity,omitempty"`
|
||||
// Description optionally describes the template.
|
||||
Description string `yaml:"description,omitempty"`
|
||||
}
|
||||
|
||||
func (t *Template) GetHTTPRequestCount() int64 {
|
||||
var count int64 = 0
|
||||
for _, request := range t.BulkRequestsHTTP {
|
||||
|
|
|
@ -5,7 +5,7 @@ type Workflow struct {
|
|||
// ID is the unique id for the template
|
||||
ID string `yaml:"id"`
|
||||
// Info contains information about the template
|
||||
Info Info `yaml:"info"`
|
||||
Info map[string]string `yaml:"info"`
|
||||
// CookieReuse makes all cookies shared by templates within the workflow
|
||||
CookieReuse bool `yaml:"cookie-reuse,omitempty"`
|
||||
// Variables contains the variables accessible to the pseudo-code
|
||||
|
@ -19,15 +19,3 @@ type Workflow struct {
|
|||
func (w *Workflow) GetPath() string {
|
||||
return w.path
|
||||
}
|
||||
|
||||
// Info contains information about workflow
|
||||
type Info struct {
|
||||
// Name is the name of the workflow
|
||||
Name string `yaml:"name"`
|
||||
// Author is the name of the author of the workflow
|
||||
Author string `yaml:"author"`
|
||||
// Severity optionally describes the severity of the template
|
||||
Severity string `yaml:"severity,omitempty"`
|
||||
// Description optionally describes the template.
|
||||
Description string `yaml:"description,omitempty"`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue