From 77340134110b4a21cf8d06b49fd686d05dfc4d64 Mon Sep 17 00:00:00 2001 From: devploit Date: Tue, 21 Mar 2023 22:34:10 +0100 Subject: [PATCH] dontgo403 refactorization + raw-request function added --- .gitignore | 3 ++ README.md | 22 +++++---- cmd/api.go | 121 +++++++++++++++++++++++++++++++++++++---------- cmd/requester.go | 40 ++++++++++++---- cmd/root.go | 17 +++++-- go.mod | 2 +- 6 files changed, 156 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index 7ff77f2..55123f6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,8 @@ # Built Visual Studio Code Extensions *.vsix +# Test requests files +request.txt + # Built binary dontgo403 diff --git a/README.md b/README.md index 586bf6f..1e9702c 100644 --- a/README.md +++ b/README.md @@ -29,16 +29,18 @@ Usage: dontgo403 [flags] Flags: - -b, --bypassIp string Try bypass tests with a specific IP address (or hostname). i.e.: 'X-Forwarded-For: 192.168.0.1' instead of 'X-Forwarded-For: 127.0.0.1' - -d, --delay int Set a delay (in ms) between each request. Default: 0ms - -f, --folder string Define payloads folder (if it's not in the same path as binary) - -H, --header strings Add a custom header to the requests (can be specified multiple times) - -h, --help help for dontgo403 - -t, --httpMethod string HTTP method to use (default 'GET') - -m, --max_goroutines int Set the max number of goroutines working at same time. Default: 50 (default 50) - -p, --proxy string Proxy URL. For example: http://127.0.0.1:8080 - -u, --uri string Target URL - -a, --useragent string Set the User-Agent string (default 'dontgo403') + -b, --bypassIp string Try bypass tests with a specific IP address (or hostname). i.e.: 'X-Forwarded-For: 192.168.0.1' instead of 'X-Forwarded-For: 127.0.0.1' + -d, --delay int Set a delay (in ms) between each request. Default: 0ms + -f, --folder string Define payloads folder (if it's not in the same path as binary) + -H, --header strings Add a custom header to the requests (can be specified multiple times) + -h, --help help for dontgo403 + --http Set HTTP schema for request-file requests (default HTTPS) + -t, --httpMethod string HTTP method to use (default 'GET') + -m, --max_goroutines int Set the max number of goroutines working at same time. Default: 50 (default 50) + -p, --proxy string Proxy URL. For example: http://127.0.0.1:8080 + -r, --request-file string Path to request file to load flags from + -u, --uri string Target URL + -a, --useragent string Set the User-Agent string (default 'dontgo403') ``` diff --git a/cmd/api.go b/cmd/api.go index 09f176a..5bf26be 100644 --- a/cmd/api.go +++ b/cmd/api.go @@ -3,67 +3,77 @@ package cmd import ( "bufio" "crypto/tls" + "io/ioutil" + "log" "net" "net/http" "net/http/httputil" "net/url" "os" + "strings" "time" ) -// Reads a file given its filename and returns a list containing each of its lines. -func parseFile(filename string) (lines []string, err error) { - +// parseFile reads a file given its filename and returns a list containing each of its lines. +func parseFile(filename string) ([]string, error) { file, err := os.Open(filename) if err != nil { return nil, err } + defer file.Close() + lines := []string{} scanner := bufio.NewScanner(file) - scanner.Split(bufio.ScanLines) - for scanner.Scan() { lines = append(lines, scanner.Text()) } - - err = file.Close() - if err != nil { + if err := scanner.Err(); err != nil { return nil, err } return lines, nil } +// header represents an HTTP header. type header struct { key string value string } -// Makes a GET request using headers `headers` proxy `proxy`. +// request makes an HTTP request using headers `headers` and proxy `proxy`. // -// Uses a custom method if specified. -func request(method, uri string, headers []header, proxy *url.URL) (statusCode int, response []byte, err error) { - - if method == "" { // check if it is nil +// If `method` is empty, it defaults to "GET". +func request(method, uri string, headers []header, proxy *url.URL) (int, []byte, error) { + if method == "" { method = "GET" } - var _proxy *url.URL - if len(proxy.Host) != 0 { - _proxy = proxy - } else { - _proxy = nil + if len(proxy.Host) == 0 { + proxy = nil } - client := &http.Client{Transport: &http.Transport{Proxy: http.ProxyURL(_proxy), - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, DialContext: (&net.Dialer{Timeout: 6 * time.Second}).DialContext}} + client := &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyURL(proxy), + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + DialContext: (&net.Dialer{ + Timeout: 6 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + }, + } req, err := http.NewRequest(method, uri, nil) if err != nil { return 0, nil, err - } else { - req.Close = true } + req.Close = true for _, header := range headers { req.Header.Add(header.key, header.value) @@ -73,14 +83,77 @@ func request(method, uri string, headers []header, proxy *url.URL) (statusCode i if err != nil { return 0, nil, err } + defer res.Body.Close() resp, err := httputil.DumpResponse(res, true) if err != nil { return 0, nil, err } - res.Body.Close() - return res.StatusCode, resp, nil +} + +// makeRequests reads the lines from the file at `filename` and makes HTTP requests to each URL using the given headers and proxy. +func makeRequests(filename string, headers []header, proxy *url.URL) error { + lines, err := parseFile(filename) + if err != nil { + return err + } + + responses := make(chan []byte, len(lines)) + + // Start worker routines to make requests concurrently + for _, line := range lines { + go func(line string) { + statusCode, resp, err := request("", line, headers, proxy) + if err != nil { + log.Printf("Error making request to %s: %s", line, err) + } else { + log.Printf("%s returned status code %d", line, statusCode) + responses <- resp + } + }(line) + } + + // Wait for all responses to be received + for i := 0; i < len(lines); i++ { + resp := <-responses + log.Printf("Received response: %s", resp) + } + + return nil +} + +func loadFlagsFromRequestFile(requestFile string, schema bool) { + // Read the content of the request file + content, err := ioutil.ReadFile(requestFile) + if err != nil { + log.Fatalf("Error reading request file: %v", err) + } + http_schema := "https://" + + if schema != false { + http_schema = "http://" + } + + // Split the request into lines + requestLines := strings.Split(string(content), "\n") + firstLine := requestLines[0] + headers := requestLines[1:] + host := strings.Split(requestLines[1], " ") + + // Extract the HTTP method and URL from the first line of the request + parts := strings.Split(firstLine, " ") + uri := http_schema + host[1] + parts[1] + + // Extract headers from the request and assign them to the req_headers slice + req_headers := []string{} + for _, h := range headers { + if len(h) > 0 { + req_headers = append(req_headers, h) + } + } + // Assign the extracted values to the corresponding flag variables + requester(uri, proxy, useragent, req_headers, bypassIp, folder, httpMethod) } diff --git a/cmd/requester.go b/cmd/requester.go index 38378eb..33497a6 100644 --- a/cmd/requester.go +++ b/cmd/requester.go @@ -6,6 +6,7 @@ import ( "strconv" "strings" "time" + "unicode" "github.com/cheynewallace/tabby" "github.com/fatih/color" @@ -18,6 +19,7 @@ type Result struct { contentLength int } +// printResponse prints the results of HTTP requests in a tabular format with colored output based on the status codes. func printResponse(results []Result) { t := tabby.New() @@ -36,16 +38,16 @@ func printResponse(results []Result) { t.AddLine(code, color.BlueString(strconv.Itoa(result.contentLength)+" bytes"), result.line) } t.Print() - } +// requestMethods makes HTTP requests using a list of methods from a file and prints the results. func requestMethods(uri string, headers []header, proxy *url.URL, folder string) { color.Cyan("\n[####] HTTP METHODS [####]") var lines []string lines, err := parseFile(folder + "/httpmethods") if err != nil { - log.Fatal(err) + log.Fatalf("Error reading /httpmethods file: %v", err) } w := goccm.New(max_goroutines) @@ -69,13 +71,14 @@ func requestMethods(uri string, headers []header, proxy *url.URL, folder string) printResponse(results) } +// requestHeaders makes HTTP requests using a list of headers from a file and prints the results. It can also bypass IP address restrictions by specifying a bypass IP address. func requestHeaders(uri string, headers []header, proxy *url.URL, bypassIp string, folder string, method string) { color.Cyan("\n[####] VERB TAMPERING [####]") var lines []string lines, err := parseFile(folder + "/headers") if err != nil { - log.Fatal(err) + log.Fatalf("Error reading /headers file: %v", err) } var ips []string @@ -84,13 +87,13 @@ func requestHeaders(uri string, headers []header, proxy *url.URL, bypassIp strin } else { ips, err = parseFile(folder + "/ips") if err != nil { - log.Fatal(err) + log.Fatalf("Error reading /ips file: %v", err) } } simpleheaders, err := parseFile(folder + "/simpleheaders") if err != nil { - log.Fatal(err) + log.Fatalf("Error reading /simpleheaders file: %v", err) } w := goccm.New(max_goroutines) @@ -136,13 +139,14 @@ func requestHeaders(uri string, headers []header, proxy *url.URL, bypassIp strin printResponse(results) } +// requestEndPaths makes HTTP requests using a list of custom end paths from a file and prints the results. func requestEndPaths(uri string, headers []header, proxy *url.URL, folder string, method string) { color.Cyan("\n[####] CUSTOM PATHS [####]") var lines []string lines, err := parseFile(folder + "/endpaths") if err != nil { - log.Fatal(err) + log.Fatalf("Error reading custom paths file: %v", err) } w := goccm.New(max_goroutines) @@ -166,11 +170,12 @@ func requestEndPaths(uri string, headers []header, proxy *url.URL, folder string printResponse(results) } +// requestMidPaths makes HTTP requests using a list of custom mid paths from a file and prints the results. func requestMidPaths(uri string, headers []header, proxy *url.URL, folder string, method string) { var lines []string lines, err := parseFile(folder + "/midpaths") if err != nil { - log.Fatal(err) + log.Fatalf("Error reading custom paths file: %v", err) } x := strings.Split(uri, "/") @@ -213,6 +218,7 @@ func requestMidPaths(uri string, headers []header, proxy *url.URL, folder string printResponse(results) } +// requestCapital makes HTTP requests by capitalizing each letter in the last part of the URI and prints the results. func requestCapital(uri string, headers []header, proxy *url.URL, method string) { color.Cyan("\n[####] CAPITALIZATION [####]") @@ -234,8 +240,14 @@ func requestCapital(uri string, headers []header, proxy *url.URL, method string) for _, z := range uripath { time.Sleep(time.Duration(delay) * time.Millisecond) w.Wait() - go func(z string) { - newpath := strings.ReplaceAll(uripath, string(z), strings.ToUpper(string(z))) + go func(z rune) { + newpath := strings.Map(func(r rune) rune { + if r == z { + return unicode.ToUpper(r) + } else { + return r + } + }, uripath) var fullpath string if uri[len(uri)-1:] == "/" { @@ -251,13 +263,15 @@ func requestCapital(uri string, headers []header, proxy *url.URL, method string) results = append(results, Result{fullpath, statusCode, len(response)}) w.Done() - }(string(z)) + }(z) } w.WaitAllDone() printResponse(results) } +// requester is the main function that runs all the tests. func requester(uri string, proxy string, userAgent string, req_headers []string, bypassIp string, folder string, method string) { + // Set up proxy if provided. if len(proxy) != 0 { if !strings.Contains(proxy, "http") { proxy = "http://" + proxy @@ -265,13 +279,17 @@ func requester(uri string, proxy string, userAgent string, req_headers []string, color.Magenta("\n[*] USING PROXY: %s\n", proxy) } userProxy, _ := url.Parse(proxy) + + // Check if URI has trailing slash, if not add it. x := strings.Split(uri, "/") if len(x) < 4 { uri += "/" } + // Set User-Agent header. if len(userAgent) == 0 { userAgent = "dontgo403" } + // Set default request method to GET. if len(method) == 0 { method = "GET" } @@ -280,6 +298,7 @@ func requester(uri string, proxy string, userAgent string, req_headers []string, {"User-Agent", userAgent}, } + // Parse custom headers from CLI arguments and add them to the headers slice. if len(req_headers[0]) != 0 { for _, _header := range req_headers { header_split := strings.Split(_header, ":") @@ -287,6 +306,7 @@ func requester(uri string, proxy string, userAgent string, req_headers []string, } } + // Call each function that will send HTTP requests with different variations of headers and URLs. requestMethods(uri, headers, userProxy, folder) requestHeaders(uri, headers, userProxy, bypassIp, folder, method) requestEndPaths(uri, headers, userProxy, folder, method) diff --git a/cmd/root.go b/cmd/root.go index d797003..ccd6691 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -22,6 +22,8 @@ var ( bypassIp string folder string httpMethod string + requestFile string + schema bool ) // rootCmd @@ -34,6 +36,7 @@ var rootCmd = &cobra.Command{ if len(folder) == 0 { folder = "payloads" } + fi, _ := os.Stdin.Stat() if (fi.Mode() & os.ModeCharDevice) == 0 { bytes, _ := ioutil.ReadAll(os.Stdin) @@ -48,11 +51,15 @@ var rootCmd = &cobra.Command{ requester(uri, proxy, useragent, req_headers, bypassIp, folder, httpMethod) } } else { - if len(uri) == 0 { - cmd.Help() - log.Fatal() + if len(requestFile) > 0 { + loadFlagsFromRequestFile(requestFile, schema) + } else { + if len(uri) == 0 { + cmd.Help() + log.Fatal() + } + requester(uri, proxy, useragent, req_headers, bypassIp, folder, httpMethod) } - requester(uri, proxy, useragent, req_headers, bypassIp, folder, httpMethod) } }, } @@ -75,6 +82,8 @@ func init() { rootCmd.PersistentFlags().StringVarP(&bypassIp, "bypassIp", "b", "", "Try bypass tests with a specific IP address (or hostname). i.e.: 'X-Forwarded-For: 192.168.0.1' instead of 'X-Forwarded-For: 127.0.0.1'") rootCmd.PersistentFlags().StringVarP(&folder, "folder", "f", "", "Define payloads folder (if it's not in the same path as binary)") rootCmd.PersistentFlags().StringVarP(&httpMethod, "httpMethod", "t", "", "HTTP method to use (default 'GET')") + rootCmd.PersistentFlags().StringVarP(&requestFile, "request-file", "r", "", "Path to request file to load flags from") + rootCmd.PersistentFlags().BoolVarP(&schema, "http", "", false, "Set HTTP schema for request-file requests (default HTTPS)") } // initConfig reads in config file and ENV variables if set. diff --git a/go.mod b/go.mod index 61c66c6..93771ab 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module dontgo403 -go 1.17 +go 1.20 require ( github.com/cheynewallace/tabby v1.1.1