From c9847cb063b8a08d83996c70a680fc1a7768a704 Mon Sep 17 00:00:00 2001 From: SIMLUKE Date: Mon, 30 Sep 2024 12:04:11 +0200 Subject: [PATCH] feat: added --data and --method data allows the user to input data to pass to the body of the requests method allows the user to change the http method to bruteforce with --- autocompletion/bash/_bruteforce | 57 +++++++++++++++++++++------------ autocompletion/zsh/_bruteforce | 4 ++- src/cli/cli.go | 42 +++++++++++++++++------- src/matching/matcher.go | 5 ++- src/matching/status.go | 6 ++-- src/models/models.go | 2 ++ src/query/callWorker.go | 2 +- src/query/queryExecute.go | 17 +++++++--- 8 files changed, 91 insertions(+), 44 deletions(-) diff --git a/autocompletion/bash/_bruteforce b/autocompletion/bash/_bruteforce index 49c9bbc..d413a5e 100644 --- a/autocompletion/bash/_bruteforce +++ b/autocompletion/bash/_bruteforce @@ -1,28 +1,43 @@ -#!/bin/bash +_bruteforce() { + local cur prev opts methods -_bruteforce_completion() { - local cur prev opts cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" - opts="--threads -v --status-codes --header --body --wordlist" - if [[ ${COMP_CWORD} -eq 1 ]]; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - elif [[ ${COMP_CWORD} -eq 2 ]]; then - case "${prev}" in - --threads) - COMPREPLY=( $(compgen -W "1 2 4 8 16 32" -- "${cur}") ) - ;; - --status-codes) - COMPREPLY=( $(compgen -W "200 401 403 404 429 500" -- "${cur}") ) - ;; - --header|--body|--wordlist) - COMPREPLY=() - ;; - esac - else - COMPREPLY=( $(compgen -W "http:// https://" -- "${cur}") ) + opts="-v --threads --status-codes --header --body --wordlist --method --data" + + methods="POST GET PUT PATCH DELETE HEAD OPTIONS" + + case "$prev" in + --threads) + COMPREPLY=( $(compgen -W "1 2 4 8 16 32" -- "$cur") ) + return 0 + ;; + --status-codes) + COMPREPLY=( $(compgen -W "200 401 403 404 429 500" -- "$cur") ) + return 0 + ;; + --method) + COMPREPLY=( $(compgen -W "$methods" -- "$cur") ) + return 0 + ;; + --wordlist) + COMPREPLY=( $(compgen -f -- "$cur") ) + return 0 + ;; + --header|--body|--data) + return 0 + ;; + *) + ;; + esac + + if [[ "$cur" == http* ]]; then + COMPREPLY=( $(compgen -W "http:// https://" -- "$cur") ) + return 0 fi + + COMPREPLY=( $(compgen -W "$opts" -- "$cur") ) } -complete -F _bruteforce_completion bruteforce +complete -F _bruteforce bruteforce diff --git a/autocompletion/zsh/_bruteforce b/autocompletion/zsh/_bruteforce index 0b8c685..9522cfd 100644 --- a/autocompletion/zsh/_bruteforce +++ b/autocompletion/zsh/_bruteforce @@ -6,10 +6,12 @@ _bruteforce() { args=( '-v[verbose mode]' '--threads=[number of threads]:number of threads:(1 2 4 8 16 32)' - '--status-codes=[Comma-separated list of status codes to match]:status codes:' + '--status-codes=[Comma-separated list of status codes to match]:codes:(200 401 403 404 429 500)' '--header=[Header to match]:header:' '--body=[String to match in response body]:body:' '--wordlist=[Wordlist to bruteforce URLs with]:wordlist:_files' + '--method=[Method to bruteforce with]:methodes:(POST GET PUT PATCH DELETE HEAD OPTIONS)' + '--data=[JSON Data to inlude in body when bruteforcing]':data '*:url:_bruteforce_urls' ) diff --git a/src/cli/cli.go b/src/cli/cli.go index 34f3d77..fc21a17 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -3,24 +3,49 @@ package cli import ( "bruteforce/src/matching" "bruteforce/src/models" + "encoding/json" "errors" "flag" "fmt" "os" ) +func errorHandling(params models.ForcingParams) (models.ForcingParams, error) { + ThreadsError := errors.New("wrong number of threads given") + WordListError := errors.New("no wordlist given") + DataError := errors.New("Invalid JSON data") + methodError := errors.New("Invalid HTTP method") + var method_list = [7]string{"POST", "GET", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"} + + if params.Data != "" && !json.Valid([]byte(params.Data)) { + return params, DataError + } + if params.Workers < 1 { + return params, ThreadsError + } + if params.Wordlist == "" { + return params, WordListError + } + for i := 0; i < 7; i++ { + if params.Method == method_list[i] { + return params, nil + } + } + return params, methodError +} + func ParseCliArgs() (models.ForcingParams, error) { var params models.ForcingParams - UrlError := errors.New("no url given") - ThreadsError := errors.New("wrong number of threads given") - WordListError := errors.New("no wordlist given") forkptr := flag.Bool("v", false, "Verbose program") statusPtr := flag.String("status-codes", "200,401,403,404,429,500", "Comma-separated list of status codes to match") headerPtr := flag.String("header", "", "Header to match, formatted as \"key: value\"") bodyPtr := flag.String("body", "", "String to match in response body") wordlistPtr := flag.String("wordlist", "", "Wordlist to bruteforce url with") + methodPtr := flag.String("method", "POST", "Method to bruteforce with") + postDataptr := flag.String("data", "", "JSON Data to inlude in body when bruteforcing") + flag.IntVar(¶ms.Workers, "threads", 1, "Number of threads to be used") flag.Usage = func() { @@ -39,13 +64,8 @@ func ParseCliArgs() (models.ForcingParams, error) { params.BoolFlags.Verbose = *forkptr params.Wordlist = *wordlistPtr params.Criteria = matcher.MatchParser(*statusPtr, *headerPtr, *bodyPtr) + params.Data = *postDataptr + params.Method = *methodPtr - if params.Workers < 1 { - return params, ThreadsError - } - if params.Wordlist == "" { - return params, WordListError - } - - return params, nil + return errorHandling(params) } diff --git a/src/matching/matcher.go b/src/matching/matcher.go index 817336a..788be80 100644 --- a/src/matching/matcher.go +++ b/src/matching/matcher.go @@ -6,8 +6,8 @@ import ( "net/http" ) -func MatchResponse(response *http.Response, body []byte, criteria models.MatchCriteria) error { - if err := matchStatusCode(response, criteria); err != nil { +func MatchResponse(response *http.Response, body []byte, criteria models.MatchCriteria, params *models.ForcingParams) error { + if err := matchStatusCode(response, criteria, params); err != nil { return err } if err := matchHeaders(response, criteria); err != nil { @@ -16,7 +16,6 @@ func MatchResponse(response *http.Response, body []byte, criteria models.MatchCr if err := matchContents(body, criteria); err != nil { return err } - return nil } diff --git a/src/matching/status.go b/src/matching/status.go index 2a90b01..b0e0a5b 100644 --- a/src/matching/status.go +++ b/src/matching/status.go @@ -8,13 +8,15 @@ import ( "strings" ) -func matchStatusCode(resp *http.Response, criteria models.MatchCriteria) error { +func matchStatusCode(resp *http.Response, criteria models.MatchCriteria, params *models.ForcingParams) error { isAll := false if criteria.StatusCodes[0] == 0 { isAll = !isAll } else { - log.Printf("Matching status codes %d...", criteria.StatusCodes) + if params.BoolFlags.Verbose { + log.Printf("Matching status codes %d...", criteria.StatusCodes) + } } for _, code := range criteria.StatusCodes { if resp.StatusCode == code || isAll { diff --git a/src/models/models.go b/src/models/models.go index 63b8078..2ce9f64 100644 --- a/src/models/models.go +++ b/src/models/models.go @@ -26,4 +26,6 @@ type ForcingParams struct { Wordlist string BoolFlags boolflags Criteria MatchCriteria + Data string + Method string } diff --git a/src/query/callWorker.go b/src/query/callWorker.go index 3e6e938..7056071 100644 --- a/src/query/callWorker.go +++ b/src/query/callWorker.go @@ -14,7 +14,7 @@ func executeQueryFromFile(wg *sync.WaitGroup, params *models.ForcingParams, curr } for taskData := range currentPath { - QueryExecute(params, taskData, "POST") + QueryExecute(params, taskData, params.Method) } } diff --git a/src/query/queryExecute.go b/src/query/queryExecute.go index ca69493..b490b52 100644 --- a/src/query/queryExecute.go +++ b/src/query/queryExecute.go @@ -3,6 +3,7 @@ package query import ( "bruteforce/src/matching" "bruteforce/src/models" + "bytes" "fmt" "io" "log" @@ -11,12 +12,16 @@ import ( func QueryExecute(params *models.ForcingParams, path string, method string) { client := &http.Client{} - req, err := http.NewRequest(method, params.Url+path, nil) + body_req := []byte(params.Data) + + req, err := http.NewRequest(method, params.Url+path, bytes.NewBuffer(body_req)) + if err != nil { log.Fatal(err) } - log.Printf("NewRequest(%s)", params.Url+path) - + if params.BoolFlags.Verbose { + log.Printf("NewRequest(%s)", params.Url+path) + } q := req.URL.Query() req.URL.RawQuery = q.Encode() @@ -31,9 +36,11 @@ func QueryExecute(params *models.ForcingParams, path string, method string) { log.Fatal(err) } - if err := matcher.MatchResponse(resp, body, params.Criteria); err == nil { + if err := matcher.MatchResponse(resp, body, params.Criteria, params); err == nil { fmt.Println(string(body)) } else { - log.Println(err) + if params.BoolFlags.Verbose { + log.Println(err) + } } }