Merge pull request #372 from projectdiscovery/iceman-add-dynamic-fields

Added dynamic field in info key support
dev
bauthard 2020-10-19 16:27:38 +05:30 committed by GitHub
commit b194472371
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 96 additions and 155 deletions

View File

@ -10,7 +10,7 @@ require (
github.com/json-iterator/go v1.1.10
github.com/karrick/godirwalk v1.16.1
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/miekg/dns v1.1.33
github.com/miekg/dns v1.1.34
github.com/pkg/errors v0.9.1
github.com/projectdiscovery/gologger v1.0.1
github.com/projectdiscovery/httpx v1.0.2

View File

@ -37,8 +37,8 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.33 h1:8KUVEKrUw2dmu1Ys0aWnkEJgoRaLAzNysfCh2KSMWiI=
github.com/miekg/dns v1.1.33/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.34 h1:SgTzfkN+oLoIHF1bgUP+C71mzuDl3AhLApHzCCIAMWM=
github.com/miekg/dns v1.1.34/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=

View File

@ -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)
}

View File

@ -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
}

View File

@ -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()
}

View File

@ -14,36 +14,29 @@ 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["template"] = e.template.ID
output["type"] = "dns"
output["matched"] = domain
for k, v := range e.template.Info {
output[k] = v
}
if matcher != nil && len(matcher.Name) > 0 {
output.MatcherName = matcher.Name
output["matcher_name"] = matcher.Name
}
if len(extractorResults) > 0 {
output.ExtractedResults = extractorResults
output["extracted_results"] = extractorResults
}
if e.jsonRequest {
output.Request = req.String()
output.Response = resp.String()
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)
@ -68,9 +61,9 @@ func (e *DNSExecuter) writeOutputDNS(domain string, req, resp *dns.Msg, matcher
builder.WriteString(colorizer.Colorizer.BrightBlue("dns").String())
builder.WriteString("] ")
if e.template.Info.Severity != "" {
if e.template.Info["severity"] != "" {
builder.WriteString("[")
builder.WriteString(colorizer.GetColorizedSeverity(e.template.Info.Severity))
builder.WriteString(colorizer.GetColorizedSeverity(e.template.Info["severity"]))
builder.WriteString("] ")
}

View File

@ -24,23 +24,21 @@ func (e *HTTPExecuter) writeOutputHTTP(req *requests.HTTPRequest, resp *http.Res
}
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)
output["template"] = e.template.ID
output["type"] = "http"
output["matched"] = URL
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.MatcherName = matcher.Name
output["matcher_name"] = matcher.Name
}
if len(extractorResults) > 0 {
output.ExtractedResults = extractorResults
output["extracted_results"] = extractorResults
}
// TODO: URL should be an argument
@ -49,24 +47,21 @@ func (e *HTTPExecuter) writeOutputHTTP(req *requests.HTTPRequest, resp *http.Res
if err != nil {
gologger.Warningf("could not dump request: %s\n", err)
} else {
output.Request = string(dumpedRequest)
output["request"] = string(dumpedRequest)
}
dumpedResponse, err := httputil.DumpResponse(resp, false)
if err != nil {
gologger.Warningf("could not dump response: %s\n", err)
} else {
output.Response = string(dumpedResponse) + body
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,7 +70,6 @@ func (e *HTTPExecuter) writeOutputHTTP(req *requests.HTTPRequest, resp *http.Res
return
}
}
return
}
@ -94,9 +88,9 @@ func (e *HTTPExecuter) writeOutputHTTP(req *requests.HTTPRequest, resp *http.Res
builder.WriteString(colorizer.Colorizer.BrightBlue("http").String())
builder.WriteString("] ")
if e.template.Info.Severity != "" {
if e.template.Info["severity"] != "" {
builder.WriteString("[")
builder.WriteString(colorizer.GetColorizedSeverity(e.template.Info.Severity))
builder.WriteString(colorizer.GetColorizedSeverity(e.template.Info["severity"]))
builder.WriteString("] ")
}

59
v2/pkg/executer/utils.go Normal file
View File

@ -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()
}

View File

@ -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 {

View File

@ -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"`
}