From 38d25bbb8b5e07c49a2ee72f0aa332af3710bce6 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 1 Sep 2020 00:18:36 +0200 Subject: [PATCH] fix and improve method CLI option --- cmd/httpx/httpx.go | 177 ++++++++++++++++++------------------ common/httputilz/methods.go | 28 ++++++ common/stringz/stringz.go | 7 ++ 3 files changed, 125 insertions(+), 87 deletions(-) create mode 100644 common/httputilz/methods.go diff --git a/cmd/httpx/httpx.go b/cmd/httpx/httpx.go index 60bd92b..5859744 100644 --- a/cmd/httpx/httpx.go +++ b/cmd/httpx/httpx.go @@ -87,9 +87,10 @@ func main() { rawhttp.AutomaticHostHeader(false) } } - - if options.Methods != "" { - scanopts.Methods = append(scanopts.Methods, strings.Split(options.Methods, ",")...) + if strings.ToLower(options.Methods) == "all" { + scanopts.Methods = httputilz.AllHTTPMethods() + } else if options.Methods != "" { + scanopts.Methods = append(scanopts.Methods, stringz.SplitByCharAndTrimSpace(options.Methods, ",")...) } if len(scanopts.Methods) == 0 { scanopts.Methods = append(scanopts.Methods, "GET") @@ -117,91 +118,93 @@ func main() { scanopts.Pipeline = options.Pipeline scanopts.HTTP2Probe = options.HTTP2Probe scanopts.OutputMethod = options.OutputMethod - if len(scanopts.Methods) > 0 { + // output verb if more than one is specified + if len(scanopts.Methods) > 1 && !options.Silent { scanopts.OutputMethod = true - - // Try to create output folder if it doesnt exist - if options.StoreResponse && !fileutil.FolderExists(options.StoreResponseDir) { - if err := os.MkdirAll(options.StoreResponseDir, os.ModePerm); err != nil { - gologger.Fatalf("Could not create output directory '%s': %s\n", options.StoreResponseDir, err) - } - } - - // output routine - wgoutput := sizedwaitgroup.New(1) - wgoutput.Add() - output := make(chan Result) - go func(output chan Result) { - defer wgoutput.Done() - - var f *os.File - if options.Output != "" { - var err error - f, err = os.Create(options.Output) - if err != nil { - gologger.Fatalf("Could not create output file '%s': %s\n", options.Output, err) - } - defer f.Close() - } - for r := range output { - if r.err != nil { - gologger.Debugf("Failure '%s': %s\n", r.URL, r.err) - continue - } - - // apply matchers and filters - if len(options.filterStatusCode) > 0 && slice.IntSliceContains(options.filterStatusCode, r.StatusCode) { - continue - } - if len(options.filterContentLength) > 0 && slice.IntSliceContains(options.filterContentLength, r.ContentLength) { - continue - } - if len(options.matchStatusCode) > 0 && !slice.IntSliceContains(options.matchStatusCode, r.StatusCode) { - continue - } - if len(options.matchContentLength) > 0 && !slice.IntSliceContains(options.matchContentLength, r.ContentLength) { - continue - } - - row := r.str - if options.JSONOutput { - row = r.JSON() - } - - gologger.Silentf("%s\n", row) - if f != nil { - f.WriteString(row + "\n") - } - } - }(output) - - wg := sizedwaitgroup.New(options.Threads) - var sc *bufio.Scanner - - // check if file has been provided - if fileutil.FileExists(options.InputFile) { - finput, err := os.Open(options.InputFile) - if err != nil { - gologger.Fatalf("Could read input file '%s': %s\n", options.InputFile, err) - } - defer finput.Close() - sc = bufio.NewScanner(finput) - } else if fileutil.HasStdin() { - sc = bufio.NewScanner(os.Stdin) - } else { - gologger.Fatalf("No input provided") - } - - for sc.Scan() { - process(sc.Text(), &wg, hp, protocol, scanopts, output) - } - - wg.Wait() - - close(output) - - wgoutput.Wait() } + + // Try to create output folder if it doesnt exist + if options.StoreResponse && !fileutil.FolderExists(options.StoreResponseDir) { + if err := os.MkdirAll(options.StoreResponseDir, os.ModePerm); err != nil { + gologger.Fatalf("Could not create output directory '%s': %s\n", options.StoreResponseDir, err) + } + } + + // output routine + wgoutput := sizedwaitgroup.New(1) + wgoutput.Add() + output := make(chan Result) + go func(output chan Result) { + defer wgoutput.Done() + + var f *os.File + if options.Output != "" { + var err error + f, err = os.Create(options.Output) + if err != nil { + gologger.Fatalf("Could not create output file '%s': %s\n", options.Output, err) + } + defer f.Close() + } + for r := range output { + if r.err != nil { + gologger.Debugf("Failure '%s': %s\n", r.URL, r.err) + continue + } + + // apply matchers and filters + if len(options.filterStatusCode) > 0 && slice.IntSliceContains(options.filterStatusCode, r.StatusCode) { + continue + } + if len(options.filterContentLength) > 0 && slice.IntSliceContains(options.filterContentLength, r.ContentLength) { + continue + } + if len(options.matchStatusCode) > 0 && !slice.IntSliceContains(options.matchStatusCode, r.StatusCode) { + continue + } + if len(options.matchContentLength) > 0 && !slice.IntSliceContains(options.matchContentLength, r.ContentLength) { + continue + } + + row := r.str + if options.JSONOutput { + row = r.JSON() + } + + gologger.Silentf("%s\n", row) + if f != nil { + f.WriteString(row + "\n") + } + } + }(output) + + wg := sizedwaitgroup.New(options.Threads) + var sc *bufio.Scanner + + // check if file has been provided + if fileutil.FileExists(options.InputFile) { + finput, err := os.Open(options.InputFile) + if err != nil { + gologger.Fatalf("Could read input file '%s': %s\n", options.InputFile, err) + } + defer finput.Close() + sc = bufio.NewScanner(finput) + } else if fileutil.HasStdin() { + sc = bufio.NewScanner(os.Stdin) + } else { + gologger.Fatalf("No input provided") + } + + for sc.Scan() { + process(sc.Text(), &wg, hp, protocol, scanopts, output) + } + + wg.Wait() + + close(output) + + wgoutput.Wait() + } func process(t string, wg *sizedwaitgroup.SizedWaitGroup, hp *httpx.HTTPX, protocol string, scanopts scanOptions, output chan Result) { @@ -617,7 +620,7 @@ func ParseOptions() *Options { flag.StringVar(&options.HttpProxy, "http-proxy", "", "HTTP Proxy, eg http://127.0.0.1:8080") flag.BoolVar(&options.JSONOutput, "json", false, "JSON Output") flag.StringVar(&options.InputFile, "l", "", "File containing domains") - flag.StringVar(&options.Methods, "x", "", "Request Methods") + flag.StringVar(&options.Methods, "x", "", "Request Methods, use ALL to check all verbs ()") flag.BoolVar(&options.OutputMethod, "method", false, "Output method") flag.BoolVar(&options.Silent, "silent", false, "Silent mode") flag.BoolVar(&options.Version, "version", false, "Show version of httpx") diff --git a/common/httputilz/methods.go b/common/httputilz/methods.go new file mode 100644 index 0000000..acac9bc --- /dev/null +++ b/common/httputilz/methods.go @@ -0,0 +1,28 @@ +package httputilz + +// HTTP methods were copied from net/http - fasthttp +const ( + MethodGet = "GET" // RFC 7231, 4.3.1 + MethodHead = "HEAD" // RFC 7231, 4.3.2 + MethodPost = "POST" // RFC 7231, 4.3.3 + MethodPut = "PUT" // RFC 7231, 4.3.4 + MethodPatch = "PATCH" // RFC 5789 + MethodDelete = "DELETE" // RFC 7231, 4.3.5 + MethodConnect = "CONNECT" // RFC 7231, 4.3.6 + MethodOptions = "OPTIONS" // RFC 7231, 4.3.7 + MethodTrace = "TRACE" // RFC 7231, 4.3.8 +) + +func AllHTTPMethods() []string { + return []string{ + MethodGet, + MethodHead, + MethodPost, + MethodPut, + MethodPatch, + MethodDelete, + MethodConnect, + MethodOptions, + MethodTrace, + } +} diff --git a/common/stringz/stringz.go b/common/stringz/stringz.go index 2e428bc..5199fa8 100644 --- a/common/stringz/stringz.go +++ b/common/stringz/stringz.go @@ -31,3 +31,10 @@ func StringToSliceInt(s string) ([]int, error) { return r, nil } + +func SplitByCharAndTrimSpace(s string, splitchar string) (result []string) { + for _, token := range strings.Split(s, splitchar) { + result = append(result, strings.TrimSpace(token)) + } + return +}