Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Connect match reqs #6

Merged
merged 42 commits into from
Sep 8, 2024
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
18b1529
chore: delete the template explanation
57ave May 31, 2024
4f7ca8a
feat(match): test parse arg to proceed with match option
louonezime Jun 24, 2024
1ab304d
featt(matcher): main architect for initiation parsed matching options
louonezime Jun 25, 2024
b3158a4
feat(matcher): parse through status code as argument
louonezime Jun 25, 2024
8f0ce47
feat(match-doc): add usage to read me to be more user friendly
louonezime Jun 25, 2024
4413b02
feat(cmd): function create to parse wordlist file line by line
57ave Jun 25, 2024
38ea845
feat(cmd): function to create channel and assign worker
57ave Jun 25, 2024
9daf216
refactor(matcher): separate different matching types from main MatchP…
louonezime Jun 27, 2024
957b4e4
fix(matcher): forward Get request from net/http in match
louonezime Jun 27, 2024
fa7f27f
build: add package main to file
57ave Jun 28, 2024
fcb8c2f
feat(cmd): implement worker correctly task depend now on it
57ave Jun 28, 2024
5a93954
feat(cmd): add execute query file and call it in routine
57ave Jun 28, 2024
681cde1
feat(cmd): implement struct forceData with neccesary data for brutfor…
57ave Jun 28, 2024
18834ea
feat: cli arguments handling
SIMLUKE Jul 4, 2024
67fbe65
fix: handle all status codes as option
louonezime Jul 15, 2024
80bb47e
feat: add matching for headers and body
louonezime Jul 16, 2024
075062f
style: refactor code and make sure each match quit
louonezime Jul 16, 2024
22d3f2c
Merge branch 'main' into gustave/request
57ave Jul 16, 2024
cb95775
chore: update dirs to match main
57ave Jul 16, 2024
3defbfd
merge: merge change
57ave Jul 16, 2024
173293d
refactor: main management of matchers is cleaned up
louonezime Jul 16, 2024
bcdb373
feat(matcher): temporary main is adapted to separate parser from star…
louonezime Jul 16, 2024
fbbf506
fix(matcher): adapt MatchResponse for Gustave query execution
louonezime Jul 16, 2024
2e47a15
merge: match status depending of response
57ave Jul 17, 2024
570b74e
refactor: implementation of query package and utils with temporary main
louonezime Jul 17, 2024
2f5f902
feat: autocompletion for cli (zsh-bash)
SIMLUKE Jul 23, 2024
372f7cf
Merge branch 'cli-interface' into test-temp-for-pr
SIMLUKE Jul 30, 2024
ac340dc
fix: updated autocompletion files
SIMLUKE Jul 30, 2024
d967998
feat():stop worker
57ave Aug 8, 2024
46e8747
Merge branch 'connect-match-reqs' into cli-interface
SIMLUKE Aug 8, 2024
3d17afe
Merge pull request #7 from PoCInnovation/cli-interface
SIMLUKE Aug 8, 2024
26f3c21
fix: unable to build (typo) and now requires wordlist path
SIMLUKE Aug 8, 2024
9641844
fix: fixed url error bug
SIMLUKE Aug 8, 2024
cbb4718
refactor(matcher): adapt to parsing of cli for matching criteria
louonezime Aug 8, 2024
692821c
style: parsing is cleaned up for unecessary code
louonezime Aug 8, 2024
bb9f6dd
feat(matcher): implement matcher in the query execution
louonezime Aug 12, 2024
c4a7249
style(matcher): fixup leftover useless bool return
louonezime Aug 12, 2024
9ddafb7
merge: merged refactor/refactor-match into connect-match-reqs
pierrelissope Aug 13, 2024
bf1711d
fix: fixed verbose option added clearer help
SIMLUKE Aug 14, 2024
c52c124
chore: remove duplicate directory
louonezime Aug 31, 2024
b9ad771
fix(query): close channel before wait to avoid panic
louonezime Aug 31, 2024
be582a2
chore: fix norm to camelcase
louonezime Aug 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

NAME = bruteforce

SRC = src/main.go

all: $(NAME)
Expand All @@ -15,4 +16,8 @@ fclean:

re: fclean all

.PHONY: all clean fclean re
install_program:
echo "source $(pwd)/autocompletion/bash/_bruteforce" >> ~/.bashrc
echo "source $(pwd)/autocompletion/zsh/_bruteforce" >> ~/.zshrc

.PHONY: all clean fclean re install_program
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,27 @@ go run src/main.go

### Usage

No usage so far
```bash
./bruteforce [OPTIONS]
```

### Matching

For matching usage, the following flags are available:

`-status-codes` : match based on a list of status codes.

For example, `./bruteforce -status-codes="200,201,202,401,404"`.

*By default* : 200, 401, 403, 404, 429, 500

`-header` : match based on a header.

For example, `./bruteforce -header="Content-Type: application/json"`.

`-body` : match based on a body.

For example, `./bruteforce -body="Hello World"`.

## Get involved

Expand Down
28 changes: 28 additions & 0 deletions autocompletion/bash/_bruteforce
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/bin/bash

_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}") )
fi
}

complete -F _bruteforce_completion bruteforce
23 changes: 23 additions & 0 deletions autocompletion/zsh/_bruteforce
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#compdef bruteforce

_bruteforce() {
local -a args

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:'
'--header=[Header to match]:header:'
'--body=[String to match in response body]:body:'
'--wordlist=[Wordlist to bruteforce URLs with]:wordlist:_files'
'*:url:_bruteforce_urls'
)

_arguments -s $args
}

_bruteforce_urls() {
_urls -p 'http://' 'https://'
}

compdef _bruteforce bruteforce
40 changes: 40 additions & 0 deletions cmd/callWorker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"time"
)

type forceData struct {
worker int
wordList string
url string
}

func executeQueryFromFile(data forceData, currentPath chan string) {
for taskData := range currentPath{
queryExecute(data, taskData, "POST")
}
}

func mainRequest(data forceData) {
channel := make(chan string)
wordArray := GetFileContent("../wordList/rootList")

for i := 0 ;i < data.worker; i++ {
go executeQueryFromFile(data, channel)
}
for i := 0; i < len(wordArray); i++ {
channel <- wordArray[i]
}
time.Sleep(1 * time.Second)
close(channel)
}

func main () {
data := forceData {
worker: 3,
wordList: "../wordList/rootList",
url: "http://localhost:3333",
}
mainRequest(data);
}
18 changes: 18 additions & 0 deletions cmd/getFile.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package main

import (
"os"
"fmt"
"log"
"strings"
)

func GetFileContent(filePath string) []string{
body, err := os.ReadFile(filePath)
if err != nil {
log.Fatalf("unable to read file: %v", err)
}
dataTab := strings.Split(string(body), "\n")
fmt.Println(dataTab[0])
return dataTab
}
35 changes: 35 additions & 0 deletions cmd/queryExecute.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import (
"fmt"
"log"
"io/ioutil"
"net/http"
)

func queryExecute(data forceData, path string, method string) {

client := &http.Client{}
req, err := http.NewRequest(method, data.url + path, nil)
if err != nil {
log.Fatal(err)
}

q := req.URL.Query()

req.URL.RawQuery = q.Encode()

resp, err := client.Do(req)
if err != nil {
fmt.Println(err)
}

defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}

fmt.Println(string(body))
}
50 changes: 50 additions & 0 deletions src/cli/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package cli

import (
"bruteforce/src/models"
"errors"
"flag"
"fmt"
"os"
)

func Parse_cli_args() (models.Forcing_params, error) {
louonezime marked this conversation as resolved.
Show resolved Hide resolved
var params models.Forcing_params

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")
bodyPtr := flag.String("body", "", "String to match in response body")
wordlistPtr := flag.String("wordlist", "", "Wordlist to bruteforce url with")
flag.IntVar(&params.Workers, "threads", 1, "Number of threads to be used")

flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage: bruteforce [options] <url>\n")
fmt.Fprintf(os.Stderr, "Options:\n")
flag.PrintDefaults()
}

flag.Parse()

if params.Workers < 1 {
return params, ThreadsError
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a worker limit

fmt.Print(flag.Args())
if len(flag.Args()) < 1 {
return params, UrlError
}
params.Url = flag.Args()[0]
// params.BoolFlags.Verbose = *forkptr
params.Status = *statusPtr
params.Header = *headerPtr
params.Body = *bodyPtr
params.Wordlist = *wordlistPtr
if params.Wordlist == "" {
return params, WordListError
}
return params, nil
}
15 changes: 14 additions & 1 deletion src/main.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
package main

import (
"bruteforce/src/cli"
"bruteforce/src/matching"
"bruteforce/src/query"
"fmt"
)

func main() {
fmt.Println("Hello World")

forcing_params, err := cli.Parse_cli_args()
louonezime marked this conversation as resolved.
Show resolved Hide resolved

if err != nil {
panic(err)
}
fmt.Println(forcing_params)

criteria := matcher.MatchParser(&forcing_params)

query.MainRequest(&forcing_params, criteria) // maybe like this?
}
13 changes: 13 additions & 0 deletions src/matching/body.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package matcher

import (
"errors"
"strings"
)

func matchContents(body []byte, criteria MatchCriteria) (bool, error) {
if criteria.BodyContains != "" && !strings.Contains(string(body), criteria.BodyContains) {
return false, errors.New("body content mismatch")
}
return true, nil
}
36 changes: 36 additions & 0 deletions src/matching/headers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package matcher

import (
"fmt"
"log"
"net/http"
"strings"
)

func matchHeaders(resp *http.Response, criteria MatchCriteria) (bool, error) {
for key, value := range criteria.Headers {
if resp.Header.Get(key) != value {
return false, fmt.Errorf("header mismatch: %s=%s\nheaders: %s", key, value, resp.Header)
}
}
return true, nil
}

func parseHeaders(headersList string) map[string]string {
if headersList == "" {
return nil
}

headers := make(map[string]string)
headerPairs := strings.Split(headersList, ",")

for _, pair := range headerPairs {
parts := strings.SplitN(pair, ":", 2)
if len(parts) == 2 {
headers[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
} else {
log.Printf("[WARN] Invalid header format: %s", pair)
}
}
return headers
}
49 changes: 49 additions & 0 deletions src/matching/matcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package matcher

import (
"bruteforce/src/models"
"io"
"log"
"net/http"
)

type MatchCriteria struct {
StatusCodes []int
Headers map[string]string
BodyContains string
}

func MatchResponse(response *http.Response, criteria MatchCriteria) (bool, string) {
body, err := io.ReadAll(response.Body)
if err != nil {
return false, err.Error()
}

if matched, err := matchStatusCode(response, criteria.StatusCodes); !matched {
return false, err.Error()
}
if matched, err := matchHeaders(response, criteria); !matched {
return false, err.Error()
}
if matched, err := matchContents(body, criteria); !matched {
return false, err.Error()
}

return true, "matched successfully"
}

func MatchParser(params *models.Forcing_params) MatchCriteria {
matchCodes, err := parseStatusCodes(params.Status)
if err != nil {
log.Fatal("Error parsing status codes:", err)
}

matchHeaders := parseHeaders(params.Header)
criteria := MatchCriteria{
StatusCodes: matchCodes,
Headers: matchHeaders,
BodyContains: params.Body,
}

return criteria
}
Loading
Loading