Skip to content

Commit

Permalink
Interactive mode and recursion-strategy (#426)
Browse files Browse the repository at this point in the history
* Add new feature: recursion-strategy

* Implementation of interactive mode (#8)

* Add interactive mode documentation (#9)

* Prepare for release 1.3.0 (#11)
  • Loading branch information
joohoi committed Apr 18, 2021
1 parent ac63d53 commit f97c2f7
Show file tree
Hide file tree
Showing 23 changed files with 645 additions and 193 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
@@ -1,7 +1,14 @@
## Changelog
- master
- New
- Changed

- v1.3.0
- New
- All output file formats now include the `Content-Type`.
- New CLI flag `-recursion-strategy` that allows adding new queued recursion jobs for non-redirect responses.
- Ability to enter interactive mode by pressing `ENTER` during the ffuf execution. The interactive mode allows
user to change filters, manage recursion queue, save snapshot of matches to a file etc.
- Changed
- Fix a badchar in progress output

Expand Down
63 changes: 51 additions & 12 deletions README.md
Expand Up @@ -20,6 +20,7 @@ A fast web fuzzer written in Go.
- [Using external mutator](https://github.com/ffuf/ffuf#using-external-mutator-to-produce-test-cases)
- [Configuration files](https://github.com/ffuf/ffuf#configuration-files)
- [Help](https://github.com/ffuf/ffuf#usage)
- [Interactive mode](https://github.com/ffuf/ffuf#interactive-mode)
- [Sponsorware?](https://github.com/ffuf/ffuf#sponsorware)

## Sponsors
Expand Down Expand Up @@ -160,18 +161,19 @@ To define the test case for ffuf, use the keyword `FUZZ` anywhere in the URL (`-
Fuzz Faster U Fool - v1.2.0-git
HTTP OPTIONS:
-H Header `"Name: Value"`, separated by colon. Multiple -H flags are accepted.
-X HTTP method to use (default: GET)
-b Cookie data `"NAME1=VALUE1; NAME2=VALUE2"` for copy as curl functionality.
-d POST data
-ignore-body Do not fetch the response content. (default: false)
-r Follow redirects (default: false)
-recursion Scan recursively. Only FUZZ keyword is supported, and URL (-u) has to end in it. (default: false)
-recursion-depth Maximum recursion depth. (default: 0)
-replay-proxy Replay matched requests using this proxy.
-timeout HTTP request timeout in seconds. (default: 10)
-u Target URL
-x HTTP Proxy URL
-H Header `"Name: Value"`, separated by colon. Multiple -H flags are accepted.
-X HTTP method to use
-b Cookie data `"NAME1=VALUE1; NAME2=VALUE2"` for copy as curl functionality.
-d POST data
-ignore-body Do not fetch the response content. (default: false)
-r Follow redirects (default: false)
-recursion Scan recursively. Only FUZZ keyword is supported, and URL (-u) has to end in it. (default: false)
-recursion-depth Maximum recursion depth. (default: 0)
-recursion-strategy Recursion strategy: "default" for a redirect based, and "greedy" to recurse on all matches (default: default)
-replay-proxy Replay matched requests using this proxy.
-timeout HTTP request timeout in seconds. (default: 10)
-u Target URL
-x Proxy URL (SOCKS5 or HTTP). For example: http://127.0.0.1:8080 or socks5://127.0.0.1:8080
GENERAL OPTIONS:
-V Show version information. (default: false)
Expand Down Expand Up @@ -241,6 +243,43 @@ EXAMPLE USAGE:
```

### Interactive mode

By pressing `ENTER` during ffuf execution, the process is paused and user is dropped to a shell-like interactive mode:
```
entering interactive mode
type "help" for a list of commands, or ENTER to resume.
> help
available commands:
fc [value] - (re)configure status code filter
fl [value] - (re)configure line count filter
fw [value] - (re)configure word count filter
fs [value] - (re)configure size filter
queueshow - show recursive job queue
queuedel [number] - delete a recursion job in the queue
queueskip - advance to the next queued recursion job
restart - restart and resume the current ffuf job
resume - resume current ffuf job (or: ENTER)
show - show results
savejson [filename] - save current matches to a file
help - you are looking at it
>
```

in this mode, filters can be reconfigured, queue managed and the current state saved to disk.

When (re)configuring the filters, they get applied posthumously and all the false positive matches from memory that
would have been filtered out by the newly added filters get deleted.

The new state of matches can be printed out with a command `show` that will print out all the matches as like they
would have been found by `ffuf`.

As "negative" matches are not stored to memory, relaxing the filters cannot unfortunately bring back the lost matches.
For this kind of scenario, the user is able to use the command `restart`, which resets the state and starts the current
job from the beginning.


## Sponsorware

`ffuf` employs a sponsorware model. This means that all new features developed by its author are initially exclusively
Expand Down
3 changes: 2 additions & 1 deletion ffufrc.example
Expand Up @@ -15,7 +15,8 @@
method = "GET"
proxyurl = "http://127.0.0.1:8080"
recursion = false
recursiondepth = 0
recursion_depth = 0
recursion_strategy = "default"
replayproxyurl = "http://127.0.0.1:8080"
timeout = 10
url = "https://example.org/FUZZ"
Expand Down
2 changes: 1 addition & 1 deletion help.go
Expand Up @@ -54,7 +54,7 @@ func Usage() {
Description: "Options controlling the HTTP request and its parts.",
Flags: make([]UsageFlag, 0),
Hidden: false,
ExpectedFlags: []string{"H", "X", "b", "d", "r", "u", "recursion", "recursion-depth", "replay-proxy", "timeout", "ignore-body", "x"},
ExpectedFlags: []string{"H", "X", "b", "d", "r", "u", "recursion", "recursion-depth", "recursion-strategy", "replay-proxy", "timeout", "ignore-body", "x"},
}
u_general := UsageSection{
Name: "GENERAL OPTIONS",
Expand Down
19 changes: 13 additions & 6 deletions main.go
Expand Up @@ -4,16 +4,16 @@ import (
"context"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"strings"

"github.com/ffuf/ffuf/pkg/ffuf"
"github.com/ffuf/ffuf/pkg/filter"
"github.com/ffuf/ffuf/pkg/input"
"github.com/ffuf/ffuf/pkg/interactive"
"github.com/ffuf/ffuf/pkg/output"
"github.com/ffuf/ffuf/pkg/runner"
"io/ioutil"
"log"
"os"
"strings"
)

type multiStringFlag []string
Expand Down Expand Up @@ -91,8 +91,9 @@ func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions {
flag.StringVar(&opts.HTTP.Data, "data-ascii", opts.HTTP.Data, "POST data (alias of -d)")
flag.StringVar(&opts.HTTP.Data, "data-binary", opts.HTTP.Data, "POST data (alias of -d)")
flag.StringVar(&opts.HTTP.Method, "X", opts.HTTP.Method, "HTTP method to use")
flag.StringVar(&opts.HTTP.ProxyURL, "x", opts.HTTP.ProxyURL, "HTTP Proxy URL")
flag.StringVar(&opts.HTTP.ProxyURL, "x", opts.HTTP.ProxyURL, "Proxy URL (SOCKS5 or HTTP). For example: http://127.0.0.1:8080 or socks5://127.0.0.1:8080")
flag.StringVar(&opts.HTTP.ReplayProxyURL, "replay-proxy", opts.HTTP.ReplayProxyURL, "Replay matched requests using this proxy.")
flag.StringVar(&opts.HTTP.RecursionStrategy, "recursion-strategy", opts.HTTP.RecursionStrategy, "Recursion strategy: \"default\" for a redirect based, and \"greedy\" to recurse on all matches")
flag.StringVar(&opts.HTTP.URL, "u", opts.HTTP.URL, "Target URL")
flag.StringVar(&opts.Input.Extensions, "e", opts.Input.Extensions, "Comma separated list of extensions. Extends FUZZ keyword.")
flag.StringVar(&opts.Input.InputMode, "mode", opts.Input.InputMode, "Multi-wordlist operation mode. Available modes: clusterbomb, pitchfork")
Expand Down Expand Up @@ -197,6 +198,12 @@ func main() {
fmt.Fprintf(os.Stderr, "Error in autocalibration, exiting: %s\n", err)
os.Exit(1)
}
go func() {
err := interactive.Handle(job)
if err != nil {
log.Printf("Error while trying to initialize interactive session: %s", err)
}
}()

// Job handles waiting for goroutines to complete itself
job.Start()
Expand Down
4 changes: 3 additions & 1 deletion pkg/ffuf/config.go
Expand Up @@ -33,13 +33,14 @@ type Config struct {
OutputDirectory string `json:"outputdirectory"`
OutputFile string `json:"outputfile"`
OutputFormat string `json:"outputformat"`
OutputCreateEmptyFile bool `json:"OutputCreateEmptyFile"`
OutputCreateEmptyFile bool `json:"OutputCreateEmptyFile"`
ProgressFrequency int `json:"-"`
ProxyURL string `json:"proxyurl"`
Quiet bool `json:"quiet"`
Rate int64 `json:"rate"`
Recursion bool `json:"recursion"`
RecursionDepth int `json:"recursion_depth"`
RecursionStrategy string `json:"recursion_strategy"`
ReplayProxyURL string `json:"replayproxyurl"`
StopOn403 bool `json:"stop_403"`
StopOnAll bool `json:"stop_all"`
Expand Down Expand Up @@ -84,6 +85,7 @@ func NewConfig(ctx context.Context, cancel context.CancelFunc) Config {
conf.Rate = 0
conf.Recursion = false
conf.RecursionDepth = 0
conf.RecursionStrategy = "default"
conf.StopOn403 = false
conf.StopOnAll = false
conf.StopOnErrors = false
Expand Down
22 changes: 22 additions & 0 deletions pkg/ffuf/interfaces.go
Expand Up @@ -4,6 +4,7 @@ package ffuf
type FilterProvider interface {
Filter(response *Response) (bool, error)
Repr() string
ReprVerbose() string
}

//RunnerProvider is an interface for request executors
Expand Down Expand Up @@ -40,6 +41,27 @@ type OutputProvider interface {
Progress(status Progress)
Info(infostring string)
Error(errstring string)
Raw(output string)
Warning(warnstring string)
Result(resp Response)
PrintResult(res Result)
SaveFile(filename, format string) error
GetResults() []Result
SetResults(results []Result)
Reset()
}

type Result struct {
Input map[string][]byte `json:"input"`
Position int `json:"position"`
StatusCode int64 `json:"status"`
ContentLength int64 `json:"length"`
ContentWords int64 `json:"words"`
ContentLines int64 `json:"lines"`
ContentType string `json:"content-type"`
RedirectLocation string `json:"redirectlocation"`
Url string `json:"url"`
ResultFile string `json:"resultfile"`
Host string `json:"host"`
HTMLColor string `json:"-"`
}

0 comments on commit f97c2f7

Please sign in to comment.