diff --git a/README.md b/README.md index 0c0732db..70e79758 100644 --- a/README.md +++ b/README.md @@ -176,7 +176,8 @@ OUTPUT: -csv store output in csv format -csvo, -csv-output-encoding string define output encoding -json store output in JSONL(ines) format - -irr, -include-response include http request/response in JSON output (-json only) + -irh, -include-response-header include http response (headers) in JSON output (-json only) + -irr, -include-response include http request/response (headers + body) in JSON output (-json only) -irrb, -include-response-base64 include base64 encoded http request/response in JSON output (-json only) -include-chain include redirect http chain in JSON output (-json only) -store-chain include http redirect chain in responses (-sr only) diff --git a/runner/options.go b/runner/options.go index f4044efb..7eaef38a 100644 --- a/runner/options.go +++ b/runner/options.go @@ -52,6 +52,7 @@ type ScanOptions struct { OutputWebSocket bool OutputWithNoColor bool OutputMethod bool + ResponseHeadersInStdout bool ResponseInStdout bool Base64ResponseInStdout bool ChainInStdout bool @@ -104,6 +105,7 @@ func (s *ScanOptions) Clone() *ScanOptions { OutputWebSocket: s.OutputWebSocket, OutputWithNoColor: s.OutputWithNoColor, OutputMethod: s.OutputMethod, + ResponseHeadersInStdout: s.ResponseHeadersInStdout, ResponseInStdout: s.ResponseInStdout, Base64ResponseInStdout: s.Base64ResponseInStdout, ChainInStdout: s.ChainInStdout, @@ -192,6 +194,7 @@ type Options struct { NoColor bool OutputServerHeader bool OutputWebSocket bool + ResponseHeadersInStdout bool ResponseInStdout bool Base64ResponseInStdout bool chainInStdout bool @@ -381,7 +384,8 @@ func ParseOptions() *Options { flagSet.BoolVar(&options.CSVOutput, "csv", false, "store output in csv format"), flagSet.StringVarP(&options.CSVOutputEncoding, "csv-output-encoding", "csvo", "", "define output encoding"), flagSet.BoolVar(&options.JSONOutput, "json", false, "store output in JSONL(ines) format"), - flagSet.BoolVarP(&options.ResponseInStdout, "include-response", "irr", false, "include http request/response in JSON output (-json only)"), + flagSet.BoolVarP(&options.ResponseHeadersInStdout, "include-response-header", "irh", false, "include http response (headers) in JSON output (-json only)"), + flagSet.BoolVarP(&options.ResponseInStdout, "include-response", "irr", false, "include http request/response (headers + body) in JSON output (-json only)"), flagSet.BoolVarP(&options.Base64ResponseInStdout, "include-response-base64", "irrb", false, "include base64 encoded http request/response in JSON output (-json only)"), flagSet.BoolVar(&options.chainInStdout, "include-chain", false, "include redirect http chain in JSON output (-json only)"), flagSet.BoolVar(&options.StoreChain, "store-chain", false, "include http redirect chain in responses (-sr only)"), diff --git a/runner/runner.go b/runner/runner.go index 7f2cb4ef..0f73f6ac 100644 --- a/runner/runner.go +++ b/runner/runner.go @@ -209,6 +209,7 @@ func New(options *Options) (*Runner, error) { scanopts.StoreResponse = options.StoreResponse scanopts.StoreResponseDirectory = options.StoreResponseDir scanopts.OutputServerHeader = options.OutputServerHeader + scanopts.ResponseHeadersInStdout = options.ResponseHeadersInStdout scanopts.OutputWithNoColor = options.NoColor scanopts.ResponseInStdout = options.ResponseInStdout scanopts.Base64ResponseInStdout = options.Base64ResponseInStdout @@ -675,14 +676,14 @@ func (r *Runner) RunEnumeration() { default: // unknown encoding gologger.Fatal().Msgf("unknown csv output encoding: %s\n", r.options.CSVOutputEncoding) } - header := Result{}.CSVHeader() + headers := Result{}.CSVHeader() if !r.options.OutputAll && !jsonAndCsv { - gologger.Silent().Msgf("%s\n", header) + gologger.Silent().Msgf("%s\n", headers) } if csvFile != nil { //nolint:errcheck // this method needs a small refactor to reduce complexity - csvFile.WriteString(header + "\n") + csvFile.WriteString(headers + "\n") } } if r.options.StoreResponseDir != "" { @@ -1519,12 +1520,16 @@ retry: } var ( - serverResponseRaw string - request string - rawResponseHeader string - responseHeader map[string]interface{} + serverResponseRaw string + request string + rawResponseHeaders string + responseHeaders map[string]interface{} ) + if scanopts.ResponseHeadersInStdout { + responseHeaders = normalizeHeaders(resp.Headers) + } + respData := string(resp.Data) if r.options.NoDecode { respData = string(resp.RawData) @@ -1533,13 +1538,13 @@ retry: if scanopts.ResponseInStdout || r.options.OutputMatchCondition != "" || r.options.OutputFilterCondition != "" { serverResponseRaw = string(respData) request = string(requestDump) - responseHeader = normalizeHeaders(resp.Headers) - rawResponseHeader = resp.RawHeaders + responseHeaders = normalizeHeaders(resp.Headers) + rawResponseHeaders = resp.RawHeaders } else if scanopts.Base64ResponseInStdout { serverResponseRaw = stringz.Base64([]byte(respData)) request = stringz.Base64(requestDump) - responseHeader = normalizeHeaders(resp.Headers) - rawResponseHeader = stringz.Base64([]byte(resp.RawHeaders)) + responseHeaders = normalizeHeaders(resp.Headers) + rawResponseHeaders = stringz.Base64([]byte(resp.RawHeaders)) } // check for virtual host @@ -1882,8 +1887,8 @@ retry: result := Result{ Timestamp: time.Now(), Request: request, - ResponseHeader: responseHeader, - RawHeader: rawResponseHeader, + ResponseHeaders: responseHeaders, + RawHeaders: rawResponseHeaders, Scheme: parsed.Scheme, Port: finalPort, Path: finalPath, diff --git a/runner/types.go b/runner/types.go index f3cb4eb2..071e728c 100644 --- a/runner/types.go +++ b/runner/types.go @@ -52,8 +52,8 @@ type Result struct { FavIconMMH3 string `json:"favicon,omitempty" csv:"favicon"` FaviconPath string `json:"favicon_path,omitempty" csv:"favicon_path"` FinalURL string `json:"final_url,omitempty" csv:"final_url"` - ResponseHeader map[string]interface{} `json:"header,omitempty" csv:"header"` - RawHeader string `json:"raw_header,omitempty" csv:"raw_header"` + ResponseHeaders map[string]interface{} `json:"header,omitempty" csv:"header"` + RawHeaders string `json:"raw_header,omitempty" csv:"raw_header"` Request string `json:"request,omitempty" csv:"request"` ResponseTime string `json:"time,omitempty" csv:"time"` Jarm string `json:"jarm,omitempty" csv:"jarm"` @@ -83,7 +83,7 @@ type Result struct { // function to get dsl variables from result struct func dslVariables() ([]string, error) { fakeResult := Result{} - fieldsToIgnore := []string{"Hashes", "ResponseHeader", "Err", "KnowledgeBase"} + fieldsToIgnore := []string{"Hashes", "ResponseHeaders", "Err", "KnowledgeBase"} if err := faker.FakeData(&fakeResult, options.WithFieldsToIgnore(fieldsToIgnore...)); err != nil { return nil, err }