mirror of
https://github.com/ffuf/ffuf
synced 2024-11-10 06:04:17 +00:00
Added raw request parsing option (#137)
* Added request body option * Update CHANGELOG.md * Update CONTRIBUTORS.md * Removed typo * Fixed the URL in path issue * Misc changes to align to codebase
This commit is contained in:
parent
ac2b447dfd
commit
01e516988d
3 changed files with 83 additions and 2 deletions
|
@ -2,6 +2,7 @@
|
|||
|
||||
- master
|
||||
- New
|
||||
- New CLI flags `-request` to specify the raw request file to build the actual request from and `-request-proto` to define the new request format.
|
||||
- New CLI flag `-od` (output directory) to enable writing requests and responses for matched results to a file for postprocessing or debugging purposes.
|
||||
- New CLI flag `-maxtime` to limit the running time of ffuf
|
||||
- New CLI flags `-recursion` and `-recursion-depth` to control recursive ffuf jobs if directories are found. This requires the `-u` to end with FUZZ keyword.
|
||||
|
|
|
@ -15,3 +15,4 @@
|
|||
* [seblw](https://github.com/seblw)
|
||||
* [SolomonSklash](https://github.com/SolomonSklash)
|
||||
* [Shaked](https://github.com/Shaked)
|
||||
* [Ice3man543](https://github.com/Ice3man543)
|
||||
|
|
83
main.go
83
main.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
|
@ -32,6 +33,8 @@ type cliOptions struct {
|
|||
matcherWords string
|
||||
matcherLines string
|
||||
proxyURL string
|
||||
request string
|
||||
requestProto string
|
||||
outputFormat string
|
||||
wordlists multiStringFlag
|
||||
inputcommands multiStringFlag
|
||||
|
@ -89,6 +92,8 @@ func main() {
|
|||
flag.StringVar(&opts.matcherWords, "mw", "", "Match amount of words in response")
|
||||
flag.StringVar(&opts.matcherLines, "ml", "", "Match amount of lines in response")
|
||||
flag.StringVar(&opts.proxyURL, "x", "", "HTTP Proxy URL")
|
||||
flag.StringVar(&opts.request, "request", "", "File containing the raw http request")
|
||||
flag.StringVar(&opts.requestProto, "request-proto", "https", "Protocol to use along with raw request")
|
||||
flag.StringVar(&conf.Method, "X", "GET", "HTTP method to use")
|
||||
flag.StringVar(&conf.OutputFile, "o", "", "Write output to file")
|
||||
flag.StringVar(&opts.outputFormat, "of", "json", "Output file format. Available formats: json, ejson, html, md, csv, ecsv")
|
||||
|
@ -239,9 +244,10 @@ func prepareConfig(parseOpts *cliOptions, conf *ffuf.Config) error {
|
|||
|
||||
var err error
|
||||
var err2 error
|
||||
if len(conf.Url) == 0 {
|
||||
errs.Add(fmt.Errorf("-u flag is required"))
|
||||
if len(conf.Url) == 0 && parseOpts.request == "" {
|
||||
errs.Add(fmt.Errorf("-u flag or -request flag is required"))
|
||||
}
|
||||
|
||||
// prepare extensions
|
||||
if parseOpts.extensions != "" {
|
||||
extensions := strings.Split(parseOpts.extensions, ",")
|
||||
|
@ -293,6 +299,15 @@ func prepareConfig(parseOpts *cliOptions, conf *ffuf.Config) error {
|
|||
errs.Add(fmt.Errorf("Either -w or --input-cmd flag is required"))
|
||||
}
|
||||
|
||||
// Prepare the request using body
|
||||
if parseOpts.request != "" {
|
||||
err := parseRawRequest(parseOpts, conf)
|
||||
if err != nil {
|
||||
errmsg := fmt.Sprintf("Could not parse raw request: %s", err)
|
||||
errs.Add(fmt.Errorf(errmsg))
|
||||
}
|
||||
}
|
||||
|
||||
//Prepare headers
|
||||
for _, v := range parseOpts.headers {
|
||||
hs := strings.SplitN(v, ":", 2)
|
||||
|
@ -385,6 +400,70 @@ func prepareConfig(parseOpts *cliOptions, conf *ffuf.Config) error {
|
|||
return errs.ErrorOrNil()
|
||||
}
|
||||
|
||||
func parseRawRequest(parseOpts *cliOptions, conf *ffuf.Config) error {
|
||||
file, err := os.Open(parseOpts.request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not open request file: %s", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
r := bufio.NewReader(file)
|
||||
|
||||
s, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read request: %s", err)
|
||||
}
|
||||
parts := strings.Split(s, " ")
|
||||
if len(parts) < 3 {
|
||||
return fmt.Errorf("malformed request supplied")
|
||||
}
|
||||
// Set the request Method
|
||||
conf.Method = parts[0]
|
||||
|
||||
for {
|
||||
line, err := r.ReadString('\n')
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
if err != nil || line == "" {
|
||||
break
|
||||
}
|
||||
|
||||
p := strings.SplitN(line, ":", 2)
|
||||
if len(p) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.EqualFold(p[0], "content-length") {
|
||||
continue
|
||||
}
|
||||
|
||||
conf.Headers[strings.TrimSpace(p[0])] = strings.TrimSpace(p[1])
|
||||
}
|
||||
|
||||
// Handle case with the full http url in path. In that case,
|
||||
// ignore any host header that we encounter and use the path as request URL
|
||||
if strings.HasPrefix(parts[1], "http") {
|
||||
parsed, err := url.Parse(parts[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not parse request URL: %s", err)
|
||||
}
|
||||
conf.Url = parts[1]
|
||||
conf.Headers["Host"] = parsed.Host
|
||||
} else {
|
||||
// Build the request URL from the request
|
||||
conf.Url = parseOpts.requestProto + "://" + conf.Headers["Host"] + parts[1]
|
||||
}
|
||||
|
||||
// Set the request body
|
||||
b, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read request body: %s", err)
|
||||
}
|
||||
conf.Data = string(b)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func keywordPresent(keyword string, conf *ffuf.Config) bool {
|
||||
//Search for keyword from HTTP method, URL and POST data too
|
||||
if strings.Index(conf.Method, keyword) != -1 {
|
||||
|
|
Loading…
Reference in a new issue