diff --git a/README.md b/README.md index 80b8046..6a847e8 100644 --- a/README.md +++ b/README.md @@ -54,10 +54,22 @@ For example, `./bruteforce -status-codes="200,201,202,401,404"`. For example, `./bruteforce -header="Content-Type: application/json"`. +To match multiple headers, use commas to separate each querie. Specify `ALL` if you wish to have all matches be true, if not don't add the `ALL`. As so: + +- should match all of the headers: + +`./bruteforce -header="all,Content-Type: application/json,Content-Type: text/css"` + +- match on any of the headers: + +`./bruteforce -header="one,Content-Type: application/json,Content-Type: text/css"` + `-body` : match based on a body. For example, `./bruteforce -body="Hello World"`. +Same applies the body for multiple queries of strings in the body as the header. + ## Get involved You're invited to join this project ! Check out the [contributing guide](./CONTRIBUTING.md). diff --git a/src/matching/body.go b/src/matching/body.go index 9ccf9f5..e25c3c6 100644 --- a/src/matching/body.go +++ b/src/matching/body.go @@ -2,13 +2,48 @@ package matcher import ( "bruteforce/src/models" - "errors" + "fmt" + "log" "strings" ) func matchContents(body []byte, criteria models.MatchCriteria) error { - if criteria.BodyContains != "" && !strings.Contains(string(body), criteria.BodyContains) { - return errors.New("body content mismatch") + bodyStr := string(body) + + if len(criteria.Body.BodyContains) > 0 { + for _, content := range criteria.Body.BodyContains { + if !strings.Contains(bodyStr, content) { + return fmt.Errorf("body content mismatch: missing %s", content) + } + } } return nil } + +func parseBody(body string) models.BodyMatch { + if body == "" { + return models.BodyMatch{} + } + + parts := strings.Split(body, ",") + firstPart := strings.TrimSpace(parts[0]) + mode := true + + if firstPart == "all" { + parts = parts[1:] + log.Println("[INFO] Matching criteria for body is set to check if.") + } else if firstPart == "one" { + mode = !mode + parts = parts[1:] + log.Println("[INFO] Matching criteria for body is set to check if one true.") + } + + var parsedBody []string + for _, part := range parts { + part = strings.TrimSpace(part) + if part != "" { + parsedBody = append(parsedBody, part) + } + } + return models.BodyMatch{BodyContains: parsedBody, MatchAllBody: mode} +} diff --git a/src/matching/headers.go b/src/matching/headers.go index 9bae1bb..2fbfa3a 100644 --- a/src/matching/headers.go +++ b/src/matching/headers.go @@ -9,7 +9,7 @@ import ( ) func matchHeaders(resp *http.Response, criteria models.MatchCriteria) error { - for key, value := range criteria.Headers { + for key, value := range criteria.Header.Headers { if resp.Header.Get(key) != value { return fmt.Errorf("header mismatch: %s=%s\nheaders: %s", key, value, resp.Header) } @@ -17,21 +17,36 @@ func matchHeaders(resp *http.Response, criteria models.MatchCriteria) error { return nil } -func parseHeaders(headersList string) map[string]string { +func parseHeaders(headersList string) models.HeaderMatch { if headersList == "" { - return nil + return models.HeaderMatch{} } headers := make(map[string]string) headerPairs := strings.Split(headersList, ",") + mode := true + firstPair := strings.TrimSpace(headerPairs[0]) + + if strings.HasPrefix(firstPair, "all") { + headerPairs = headerPairs[1:] + log.Println("[INFO] Matching criteria for header is set to check if.") + } else if strings.HasPrefix(firstPair, "one") { + mode = !mode + headerPairs = headerPairs[1:] + log.Println("[INFO] Matching criteria for header is set to check if one true.") + } for _, pair := range headerPairs { parts := strings.SplitN(pair, ":", 2) if len(parts) == 2 { - headers[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + + log.Printf("[INFO] Parsed header: %s=%s", key, value) + headers[key] = value } else { log.Printf("[WARN] Invalid header format: %s", pair) } } - return headers + return models.HeaderMatch{Headers: headers, MatchAllHeader: mode} } diff --git a/src/matching/matcher.go b/src/matching/matcher.go index 687dc60..817336a 100644 --- a/src/matching/matcher.go +++ b/src/matching/matcher.go @@ -27,10 +27,12 @@ func MatchParser(statusPtr string, headerPtr string, bodyPtr string) models.Matc } matchHeaders := parseHeaders(headerPtr) + matchBody := parseBody(bodyPtr) + criteria := models.MatchCriteria{ - StatusCodes: matchCodes, - Headers: matchHeaders, - BodyContains: bodyPtr, + StatusCodes: matchCodes, + Header: matchHeaders, + Body: matchBody, } return criteria diff --git a/src/models/models.go b/src/models/models.go index 1e8ea73..63b8078 100644 --- a/src/models/models.go +++ b/src/models/models.go @@ -4,10 +4,20 @@ type boolflags struct { Verbose bool } +type HeaderMatch struct { + Headers map[string]string + MatchAllHeader bool +} + +type BodyMatch struct { + BodyContains []string + MatchAllBody bool +} + type MatchCriteria struct { - StatusCodes []int - Headers map[string]string - BodyContains string + StatusCodes []int + Header HeaderMatch + Body BodyMatch } type ForcingParams struct { diff --git a/src/query/callWorker.go b/src/query/callWorker.go index 56332b9..3e6e938 100644 --- a/src/query/callWorker.go +++ b/src/query/callWorker.go @@ -8,8 +8,13 @@ import ( func executeQueryFromFile(wg *sync.WaitGroup, params *models.ForcingParams, currentPath chan string) { defer wg.Done() + + if params.Url[len(params.Url)-1] != '/' { + params.Url = params.Url + "/" + } + for taskData := range currentPath { - QueryExecute(params, taskData, "GET") + QueryExecute(params, taskData, "POST") } }