Compare commits
No commits in common. "1cd61646ffdc4e682c0827c6684f332d6233ffc5" and "e3e4e6250d1f541168a400bdaa48e111e435c3ec" have entirely different histories.
1cd61646ff
...
e3e4e6250d
10
CHANGELOG.md
10
CHANGELOG.md
@ -1,26 +1,16 @@
|
|||||||
## Changelog
|
## Changelog
|
||||||
- master
|
- master
|
||||||
- New
|
|
||||||
- Changed
|
|
||||||
- Fix a bug in autocalibration strategy merging, when two files have the same strategy key
|
|
||||||
- Fix panic when setting rate to 0 in the interactive console
|
|
||||||
|
|
||||||
- v2.1.0
|
|
||||||
- New
|
- New
|
||||||
- autocalibration-strategy refactored to support extensible strategy configuration
|
- autocalibration-strategy refactored to support extensible strategy configuration
|
||||||
- New cli flag `-raw` to omit urlencoding for URIs
|
- New cli flag `-raw` to omit urlencoding for URIs
|
||||||
- New cli flags `-ck` and `-cc` to enable the use of client side certificate authentication
|
|
||||||
- Integration with `github.com/ffuf/pencode` library, added `-enc` cli flag to do various in-fly encodings for input data
|
- Integration with `github.com/ffuf/pencode` library, added `-enc` cli flag to do various in-fly encodings for input data
|
||||||
- Changed
|
- Changed
|
||||||
- Fix multiline output
|
|
||||||
- Explicitly allow TLS1.0
|
- Explicitly allow TLS1.0
|
||||||
- Fix markdown output file format
|
- Fix markdown output file format
|
||||||
- Fix csv output file format
|
- Fix csv output file format
|
||||||
- Fixed divide by 0 error when setting rate limit to 0 manually.
|
- Fixed divide by 0 error when setting rate limit to 0 manually.
|
||||||
- Automatic brotli and deflate decompression
|
- Automatic brotli and deflate decompression
|
||||||
- Report if request times out when a time based matcher or filter is active
|
- Report if request times out when a time based matcher or filter is active
|
||||||
- All 2XX status codes are now matched
|
|
||||||
- Allow adding "unused" wordlists in config file
|
|
||||||
|
|
||||||
- v2.0.0
|
- v2.0.0
|
||||||
- New
|
- New
|
||||||
|
|||||||
10
README.md
10
README.md
@ -150,19 +150,16 @@ parameter.
|
|||||||
To define the test case for ffuf, use the keyword `FUZZ` anywhere in the URL (`-u`), headers (`-H`), or POST data (`-d`).
|
To define the test case for ffuf, use the keyword `FUZZ` anywhere in the URL (`-u`), headers (`-H`), or POST data (`-d`).
|
||||||
|
|
||||||
```
|
```
|
||||||
Fuzz Faster U Fool - v2.1.0
|
Fuzz Faster U Fool - v2.0.0
|
||||||
|
|
||||||
HTTP OPTIONS:
|
HTTP OPTIONS:
|
||||||
-H Header `"Name: Value"`, separated by colon. Multiple -H flags are accepted.
|
-H Header `"Name: Value"`, separated by colon. Multiple -H flags are accepted.
|
||||||
-X HTTP method to use
|
-X HTTP method to use
|
||||||
-b Cookie data `"NAME1=VALUE1; NAME2=VALUE2"` for copy as curl functionality.
|
-b Cookie data `"NAME1=VALUE1; NAME2=VALUE2"` for copy as curl functionality.
|
||||||
-cc Client cert for authentication. Client key needs to be defined as well for this to work
|
|
||||||
-ck Client key for authentication. Client certificate needs to be defined as well for this to work
|
|
||||||
-d POST data
|
-d POST data
|
||||||
-http2 Use HTTP2 protocol (default: false)
|
-http2 Use HTTP2 protocol (default: false)
|
||||||
-ignore-body Do not fetch the response content. (default: false)
|
-ignore-body Do not fetch the response content. (default: false)
|
||||||
-r Follow redirects (default: false)
|
-r Follow redirects (default: false)
|
||||||
-raw Do not encode URI (default: false)
|
|
||||||
-recursion Scan recursively. Only FUZZ keyword is supported, and URL (-u) has to end in it. (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-depth Maximum recursion depth. (default: 0)
|
||||||
-recursion-strategy Recursion strategy: "default" for a redirect based, and "greedy" to recurse on all matches (default: default)
|
-recursion-strategy Recursion strategy: "default" for a redirect based, and "greedy" to recurse on all matches (default: default)
|
||||||
@ -178,7 +175,7 @@ GENERAL OPTIONS:
|
|||||||
-acc Custom auto-calibration string. Can be used multiple times. Implies -ac
|
-acc Custom auto-calibration string. Can be used multiple times. Implies -ac
|
||||||
-ach Per host autocalibration (default: false)
|
-ach Per host autocalibration (default: false)
|
||||||
-ack Autocalibration keyword (default: FUZZ)
|
-ack Autocalibration keyword (default: FUZZ)
|
||||||
-acs Custom auto-calibration strategies. Can be used multiple times. Implies -ac
|
-acs Autocalibration strategy: "basic" or "advanced" (default: basic)
|
||||||
-c Colorize output. (default: false)
|
-c Colorize output. (default: false)
|
||||||
-config Load configuration from a file
|
-config Load configuration from a file
|
||||||
-json JSON output, printing newline-delimited JSON records (default: false)
|
-json JSON output, printing newline-delimited JSON records (default: false)
|
||||||
@ -198,7 +195,7 @@ GENERAL OPTIONS:
|
|||||||
-v Verbose output, printing full URL and redirect location (if any) with the results. (default: false)
|
-v Verbose output, printing full URL and redirect location (if any) with the results. (default: false)
|
||||||
|
|
||||||
MATCHER OPTIONS:
|
MATCHER OPTIONS:
|
||||||
-mc Match HTTP status codes, or "all" for everything. (default: 200-299,301,302,307,401,403,405,500)
|
-mc Match HTTP status codes, or "all" for everything. (default: 200,204,301,302,307,401,403,405,500)
|
||||||
-ml Match amount of lines in response
|
-ml Match amount of lines in response
|
||||||
-mmode Matcher set operator. Either of: and, or (default: or)
|
-mmode Matcher set operator. Either of: and, or (default: or)
|
||||||
-mr Match regexp
|
-mr Match regexp
|
||||||
@ -218,7 +215,6 @@ FILTER OPTIONS:
|
|||||||
INPUT OPTIONS:
|
INPUT OPTIONS:
|
||||||
-D DirSearch wordlist compatibility mode. Used in conjunction with -e flag. (default: false)
|
-D DirSearch wordlist compatibility mode. Used in conjunction with -e flag. (default: false)
|
||||||
-e Comma separated list of extensions. Extends FUZZ keyword.
|
-e Comma separated list of extensions. Extends FUZZ keyword.
|
||||||
-enc Encoders for keywords, eg. 'FUZZ:urlencode b64encode'
|
|
||||||
-ic Ignore wordlist comments (default: false)
|
-ic Ignore wordlist comments (default: false)
|
||||||
-input-cmd Command producing the input. --input-num is required when using this input method. Overrides -w.
|
-input-cmd Command producing the input. --input-num is required when using this input method. Overrides -w.
|
||||||
-input-num Number of inputs to test. Used in conjunction with --input-cmd. (default: 100)
|
-input-num Number of inputs to test. Used in conjunction with --input-cmd. (default: 100)
|
||||||
|
|||||||
22
default.nix
22
default.nix
@ -1,22 +0,0 @@
|
|||||||
{ pkgs ? (
|
|
||||||
let
|
|
||||||
inherit (builtins) fetchTree fromJSON readFile;
|
|
||||||
inherit ((fromJSON (readFile ./flake.lock)).nodes) nixpkgs gomod2nix;
|
|
||||||
in
|
|
||||||
import (fetchTree nixpkgs.locked) {
|
|
||||||
overlays = [
|
|
||||||
(import "${fetchTree gomod2nix.locked}/overlay.nix")
|
|
||||||
];
|
|
||||||
}
|
|
||||||
)
|
|
||||||
, buildGoApplication ? pkgs.buildGoApplication
|
|
||||||
}:
|
|
||||||
|
|
||||||
buildGoApplication {
|
|
||||||
# TODO: Change name to ffuff to avoid conflicts
|
|
||||||
pname = "ffuf";
|
|
||||||
version = "0.1";
|
|
||||||
pwd = ./.;
|
|
||||||
src = ./.;
|
|
||||||
modules = ./gomod2nix.toml;
|
|
||||||
}
|
|
||||||
85
flake.lock
85
flake.lock
@ -1,85 +0,0 @@
|
|||||||
{
|
|
||||||
"nodes": {
|
|
||||||
"flake-utils": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1731533236,
|
|
||||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "numtide",
|
|
||||||
"repo": "flake-utils",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gomod2nix": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": [
|
|
||||||
"flake-utils"
|
|
||||||
],
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1733668782,
|
|
||||||
"narHash": "sha256-tPsqU00FhgdFr0JiQUiBMgPVbl1jbPCY5gbFiJycL3I=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "gomod2nix",
|
|
||||||
"rev": "514283ec89c39ad0079ff2f3b1437404e4cba608",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "gomod2nix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1738546358,
|
|
||||||
"narHash": "sha256-nLivjIygCiqLp5QcL7l56Tca/elVqM9FG1hGd9ZSsrg=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "c6e957d81b96751a3d5967a0fd73694f303cc914",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixos-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": {
|
|
||||||
"inputs": {
|
|
||||||
"flake-utils": "flake-utils",
|
|
||||||
"gomod2nix": "gomod2nix",
|
|
||||||
"nixpkgs": "nixpkgs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systems": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1681028828,
|
|
||||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-systems",
|
|
||||||
"repo": "default",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"root": "root",
|
|
||||||
"version": 7
|
|
||||||
}
|
|
||||||
29
flake.nix
29
flake.nix
@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
description = "A basic gomod2nix flake";
|
|
||||||
|
|
||||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
||||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
|
||||||
inputs.gomod2nix.url = "github:nix-community/gomod2nix";
|
|
||||||
inputs.gomod2nix.inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
inputs.gomod2nix.inputs.flake-utils.follows = "flake-utils";
|
|
||||||
|
|
||||||
outputs = { self, nixpkgs, flake-utils, gomod2nix }:
|
|
||||||
(flake-utils.lib.eachDefaultSystem
|
|
||||||
(system:
|
|
||||||
let
|
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
|
||||||
|
|
||||||
# The current default sdk for macOS fails to compile go projects, so we use a newer one for now.
|
|
||||||
# This has no effect on other platforms.
|
|
||||||
callPackage = pkgs.darwin.apple_sdk_11_0.callPackage or pkgs.callPackage;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
packages.default = callPackage ./. {
|
|
||||||
inherit (gomod2nix.legacyPackages.${system}) buildGoApplication;
|
|
||||||
};
|
|
||||||
devShells.default = callPackage ./shell.nix {
|
|
||||||
inherit (gomod2nix.legacyPackages.${system}) mkGoEnv gomod2nix;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
schema = 3
|
|
||||||
|
|
||||||
[mod]
|
|
||||||
[mod."github.com/PuerkitoBio/goquery"]
|
|
||||||
version = "v1.8.0"
|
|
||||||
hash = "sha256-I3QaPWATvBOL/F26fIiYWKS13yBUYo+9o3tcsGIu8tY="
|
|
||||||
[mod."github.com/adrg/xdg"]
|
|
||||||
version = "v0.4.0"
|
|
||||||
hash = "sha256-zGjkdUQmrVqD6rMO9oDY+TeJCpuqnHyvkPCaXDlac/U="
|
|
||||||
[mod."github.com/andybalholm/brotli"]
|
|
||||||
version = "v1.0.5"
|
|
||||||
hash = "sha256-/qS8wU8yZQJ+uTOg66rEl9s7spxq9VIXF5L1BcaEClc="
|
|
||||||
[mod."github.com/andybalholm/cascadia"]
|
|
||||||
version = "v1.3.1"
|
|
||||||
hash = "sha256-M0u22DXSeXUaYtl1KoW1qWL46niFpycFkraCEQ/luYA="
|
|
||||||
[mod."github.com/davecgh/go-spew"]
|
|
||||||
version = "v1.1.1"
|
|
||||||
hash = "sha256-nhzSUrE1fCkN0+RL04N4h8jWmRFPPPWbCuDc7Ss0akI="
|
|
||||||
[mod."github.com/ffuf/pencode"]
|
|
||||||
version = "v0.0.0-20230421231718-2cea7e60a693"
|
|
||||||
hash = "sha256-/ysCCAHXmBBQC8MojiRbmbZSWo5gUE3u9AuCV24ul18="
|
|
||||||
[mod."github.com/pelletier/go-toml"]
|
|
||||||
version = "v1.9.5"
|
|
||||||
hash = "sha256-RJ9K1BTId0Mled7S66iGgxHkZ5JKEIsrrNaEfM8aImc="
|
|
||||||
[mod."golang.org/x/net"]
|
|
||||||
version = "v0.7.0"
|
|
||||||
hash = "sha256-LgZYZRwtMqm+soNh+esxDSeRuIDxRGb9OEfYaFJHCDI="
|
|
||||||
[mod."golang.org/x/sys"]
|
|
||||||
version = "v0.5.0"
|
|
||||||
hash = "sha256-0LTr3KeJ1OMQAwYUQo1513dXJtQAJn5Dq8sFkc8ps1U="
|
|
||||||
4
main.go
4
main.go
@ -51,7 +51,7 @@ func (m *wordlistFlag) Set(value string) error {
|
|||||||
func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions {
|
func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions {
|
||||||
var ignored bool
|
var ignored bool
|
||||||
|
|
||||||
var cookies, autocalibrationstrings, autocalibrationstrategies, headers, inputcommands multiStringFlag
|
var cookies, autocalibrationstrings, autocalibrationstrategies, headers, inputcommands multiStringFlag
|
||||||
var wordlists, encoders wordlistFlag
|
var wordlists, encoders wordlistFlag
|
||||||
|
|
||||||
cookies = opts.HTTP.Cookies
|
cookies = opts.HTTP.Cookies
|
||||||
@ -144,7 +144,7 @@ func ParseFlags(opts *ffuf.ConfigOptions) *ffuf.ConfigOptions {
|
|||||||
|
|
||||||
opts.General.AutoCalibrationStrings = autocalibrationstrings
|
opts.General.AutoCalibrationStrings = autocalibrationstrings
|
||||||
if len(autocalibrationstrategies) > 0 {
|
if len(autocalibrationstrategies) > 0 {
|
||||||
opts.General.AutoCalibrationStrategies = []string{}
|
opts.General.AutoCalibrationStrategies = []string {}
|
||||||
for _, strategy := range autocalibrationstrategies {
|
for _, strategy := range autocalibrationstrategies {
|
||||||
opts.General.AutoCalibrationStrategies = append(opts.General.AutoCalibrationStrategies, strings.Split(strategy, ",")...)
|
opts.General.AutoCalibrationStrategies = append(opts.General.AutoCalibrationStrategies, strings.Split(strategy, ",")...)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
package ffuf
|
package ffuf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
"encoding/json"
|
||||||
|
"path/filepath"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AutocalibrationStrategy map[string][]string
|
type AutocalibrationStrategy map[string][]string
|
||||||
@ -20,9 +20,9 @@ func (j *Job) autoCalibrationStrings() map[string][]string {
|
|||||||
if len(j.Config.AutoCalibrationStrings) > 0 {
|
if len(j.Config.AutoCalibrationStrings) > 0 {
|
||||||
cInputs["custom"] = append(cInputs["custom"], j.Config.AutoCalibrationStrings...)
|
cInputs["custom"] = append(cInputs["custom"], j.Config.AutoCalibrationStrings...)
|
||||||
return cInputs
|
return cInputs
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, strategy := range j.Config.AutoCalibrationStrategies {
|
for _, strategy := range j.Config.AutoCalibrationStrategies {
|
||||||
jsonStrategy, err := os.ReadFile(filepath.Join(AUTOCALIBDIR, strategy+".json"))
|
jsonStrategy, err := os.ReadFile(filepath.Join(AUTOCALIBDIR, strategy+".json"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -36,36 +36,36 @@ func (j *Job) autoCalibrationStrings() map[string][]string {
|
|||||||
j.Output.Warning(fmt.Sprintf("Skipping strategy \"%s\" because of error: %s\n", strategy, err))
|
j.Output.Warning(fmt.Sprintf("Skipping strategy \"%s\" because of error: %s\n", strategy, err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
cInputs = mergeMaps(cInputs, tmpStrategy)
|
cInputs = mergeMaps(cInputs, tmpStrategy)
|
||||||
}
|
}
|
||||||
|
|
||||||
return cInputs
|
return cInputs
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupDefaultAutocalibrationStrategies() error {
|
func setupDefaultAutocalibrationStrategies() error {
|
||||||
basic_strategy := AutocalibrationStrategy{
|
basic_strategy := AutocalibrationStrategy {
|
||||||
"basic_admin": []string{"admin" + RandomString(16), "admin" + RandomString(8)},
|
"basic_admin": []string{"admin"+RandomString(16), "admin"+RandomString(8)},
|
||||||
"htaccess": []string{".htaccess" + RandomString(16), ".htaccess" + RandomString(8)},
|
"htaccess": []string{".htaccess"+RandomString(16), ".htaccess"+RandomString(8)},
|
||||||
"basic_random": []string{RandomString(16), RandomString(8)},
|
"basic_random": []string{RandomString(16), RandomString(8)},
|
||||||
}
|
}
|
||||||
basic_strategy_json, err := json.Marshal(basic_strategy)
|
basic_strategy_json, err := json.Marshal(basic_strategy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
advanced_strategy := AutocalibrationStrategy{
|
advanced_strategy := AutocalibrationStrategy {
|
||||||
"basic_admin": []string{"admin" + RandomString(16), "admin" + RandomString(8)},
|
"basic_admin": []string{"admin"+RandomString(16), "admin"+RandomString(8)},
|
||||||
"htaccess": []string{".htaccess" + RandomString(16), ".htaccess" + RandomString(8)},
|
"htaccess": []string{".htaccess"+RandomString(16), ".htaccess"+RandomString(8)},
|
||||||
"basic_random": []string{RandomString(16), RandomString(8)},
|
"basic_random": []string{RandomString(16), RandomString(8)},
|
||||||
"admin_dir": []string{"admin" + RandomString(16) + "/", "admin" + RandomString(8) + "/"},
|
"admin_dir": []string{"admin"+RandomString(16)+"/", "admin"+RandomString(8)+"/"},
|
||||||
"random_dir": []string{RandomString(16) + "/", RandomString(8) + "/"},
|
"random_dir": []string{RandomString(16)+"/", RandomString(8)+"/"},
|
||||||
}
|
}
|
||||||
advanced_strategy_json, err := json.Marshal(advanced_strategy)
|
advanced_strategy_json, err := json.Marshal(advanced_strategy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
basic_strategy_file := filepath.Join(AUTOCALIBDIR, "basic.json")
|
basic_strategy_file := filepath.Join(AUTOCALIBDIR, "basic.json")
|
||||||
if !FileExists(basic_strategy_file) {
|
if !FileExists(basic_strategy_file) {
|
||||||
err = os.WriteFile(filepath.Join(AUTOCALIBDIR, "basic.json"), basic_strategy_json, 0640)
|
err = os.WriteFile(filepath.Join(AUTOCALIBDIR, "basic.json"), basic_strategy_json, 0640)
|
||||||
@ -76,7 +76,7 @@ func setupDefaultAutocalibrationStrategies() error {
|
|||||||
err = os.WriteFile(filepath.Join(AUTOCALIBDIR, "advanced.json"), advanced_strategy_json, 0640)
|
err = os.WriteFile(filepath.Join(AUTOCALIBDIR, "advanced.json"), advanced_strategy_json, 0640)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,108 +0,0 @@
|
|||||||
package ffuf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NullOutput is a dummy output provider that does nothing
|
|
||||||
type NullOutput struct {
|
|
||||||
Results []Result
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewNullOutput() *NullOutput { return &NullOutput{} }
|
|
||||||
func (o *NullOutput) Banner() {}
|
|
||||||
func (o *NullOutput) Finalize() error { return nil }
|
|
||||||
func (o *NullOutput) Progress(status Progress) {}
|
|
||||||
func (o *NullOutput) Info(infostring string) {}
|
|
||||||
func (o *NullOutput) Error(errstring string) {}
|
|
||||||
func (o *NullOutput) Raw(output string) {}
|
|
||||||
func (o *NullOutput) Warning(warnstring string) {}
|
|
||||||
func (o *NullOutput) Result(resp Response) {}
|
|
||||||
func (o *NullOutput) PrintResult(res Result) {}
|
|
||||||
func (o *NullOutput) SaveFile(filename, format string) error { return nil }
|
|
||||||
func (o *NullOutput) GetCurrentResults() []Result { return o.Results }
|
|
||||||
func (o *NullOutput) SetCurrentResults(results []Result) { o.Results = results }
|
|
||||||
func (o *NullOutput) Reset() {}
|
|
||||||
func (o *NullOutput) Cycle() {}
|
|
||||||
|
|
||||||
func TestAutoCalibrationStrings(t *testing.T) {
|
|
||||||
// Create a temporary directory for the test
|
|
||||||
tmpDir, err := os.MkdirTemp("", "ffuf-test")
|
|
||||||
AUTOCALIBDIR = tmpDir
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create temporary directory: %v", err)
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(tmpDir)
|
|
||||||
|
|
||||||
// Create a test strategy file
|
|
||||||
strategy := AutocalibrationStrategy{
|
|
||||||
"test": {"foo", "bar"},
|
|
||||||
}
|
|
||||||
strategyJSON, err := json.Marshal(strategy)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to marshal strategy to JSON: %v", err)
|
|
||||||
}
|
|
||||||
strategyFile := filepath.Join(tmpDir, "test.json")
|
|
||||||
err = os.WriteFile(strategyFile, strategyJSON, 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to write strategy file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a test job with the strategy
|
|
||||||
job := &Job{
|
|
||||||
Config: &Config{
|
|
||||||
AutoCalibrationStrategies: []string{"test"},
|
|
||||||
},
|
|
||||||
Output: NewNullOutput(),
|
|
||||||
}
|
|
||||||
cInputs := job.autoCalibrationStrings()
|
|
||||||
|
|
||||||
// Verify that the custom strategy was added
|
|
||||||
if len(cInputs["custom"]) != 0 {
|
|
||||||
t.Errorf("Expected custom strategy to be empty, but got %v", cInputs["custom"])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that the test strategy was added
|
|
||||||
expected := []string{"foo", "bar"}
|
|
||||||
if len(cInputs["test"]) != len(expected) {
|
|
||||||
t.Errorf("Expected test strategy to have %d inputs, but got %d", len(expected), len(cInputs["test"]))
|
|
||||||
}
|
|
||||||
for i, input := range cInputs["test"] {
|
|
||||||
if input != expected[i] {
|
|
||||||
t.Errorf("Expected test strategy input %d to be %q, but got %q", i, expected[i], input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that a missing strategy is skipped
|
|
||||||
job = &Job{
|
|
||||||
Config: &Config{
|
|
||||||
AutoCalibrationStrategies: []string{"missing"},
|
|
||||||
},
|
|
||||||
Output: NewNullOutput(),
|
|
||||||
}
|
|
||||||
cInputs = job.autoCalibrationStrings()
|
|
||||||
if len(cInputs) != 0 {
|
|
||||||
t.Errorf("Expected missing strategy to be skipped, but got %v", cInputs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that a malformed strategy is skipped
|
|
||||||
malformedStrategy := []byte(`{"test": "foo"}`)
|
|
||||||
malformedFile := filepath.Join(tmpDir, "malformed.json")
|
|
||||||
err = os.WriteFile(malformedFile, malformedStrategy, 0644)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to write malformed strategy file: %v", err)
|
|
||||||
}
|
|
||||||
job = &Job{
|
|
||||||
Config: &Config{
|
|
||||||
AutoCalibrationStrategies: []string{"malformed"},
|
|
||||||
},
|
|
||||||
Output: NewNullOutput(),
|
|
||||||
}
|
|
||||||
cInputs = job.autoCalibrationStrings()
|
|
||||||
if len(cInputs) != 0 {
|
|
||||||
t.Errorf("Expected malformed strategy to be skipped, but got %v", cInputs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
//VERSION holds the current version number
|
//VERSION holds the current version number
|
||||||
VERSION = "2.1.0"
|
VERSION = "2.0.0"
|
||||||
//VERSION_APPENDIX holds additional version definition
|
//VERSION_APPENDIX holds additional version definition
|
||||||
VERSION_APPENDIX = "-dev"
|
VERSION_APPENDIX = "-dev"
|
||||||
CONFIGDIR = filepath.Join(xdg.ConfigHome, "ffuf")
|
CONFIGDIR = filepath.Join(xdg.ConfigHome, "ffuf")
|
||||||
|
|||||||
@ -347,7 +347,6 @@ func (j *Job) isMatch(resp Response) bool {
|
|||||||
}
|
}
|
||||||
if match {
|
if match {
|
||||||
matched = true
|
matched = true
|
||||||
fmt.Printf("%s\n", resp.Data)
|
|
||||||
} else if j.Config.MatcherMode == "and" {
|
} else if j.Config.MatcherMode == "and" {
|
||||||
// we already know this isn't "and" match
|
// we already know this isn't "and" match
|
||||||
return false
|
return false
|
||||||
|
|||||||
@ -170,7 +170,7 @@ func NewConfigOptions() *ConfigOptions {
|
|||||||
c.Matcher.Lines = ""
|
c.Matcher.Lines = ""
|
||||||
c.Matcher.Regexp = ""
|
c.Matcher.Regexp = ""
|
||||||
c.Matcher.Size = ""
|
c.Matcher.Size = ""
|
||||||
c.Matcher.Status = "200-299,301,302,307,401,403,405,500"
|
c.Matcher.Status = "200,204,301,302,307,401,403,405,500"
|
||||||
c.Matcher.Time = ""
|
c.Matcher.Time = ""
|
||||||
c.Matcher.Words = ""
|
c.Matcher.Words = ""
|
||||||
c.Output.DebugLog = ""
|
c.Output.DebugLog = ""
|
||||||
@ -373,6 +373,7 @@ func ConfigFromOptions(parseOpts *ConfigOptions, ctx context.Context, cancel con
|
|||||||
conf.ClientKey = parseOpts.HTTP.ClientKey
|
conf.ClientKey = parseOpts.HTTP.ClientKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//Prepare headers and make canonical
|
//Prepare headers and make canonical
|
||||||
for _, v := range parseOpts.HTTP.Headers {
|
for _, v := range parseOpts.HTTP.Headers {
|
||||||
hs := strings.SplitN(v, ":", 2)
|
hs := strings.SplitN(v, ":", 2)
|
||||||
|
|||||||
@ -19,13 +19,15 @@ func NewRateThrottle(conf *Config) *RateThrottle {
|
|||||||
Config: conf,
|
Config: conf,
|
||||||
lastAdjustment: time.Now(),
|
lastAdjustment: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.Rate > 0 {
|
if conf.Rate > 0 {
|
||||||
r.rateCounter = ring.New(int(conf.Rate * 5))
|
r.rateCounter = ring.New(int(conf.Rate * 5))
|
||||||
|
} else {
|
||||||
|
r.rateCounter = ring.New(conf.Threads * 5)
|
||||||
|
}
|
||||||
|
if conf.Rate > 0 {
|
||||||
ratemicros := 1000000 / conf.Rate
|
ratemicros := 1000000 / conf.Rate
|
||||||
r.RateLimiter = time.NewTicker(time.Microsecond * time.Duration(ratemicros))
|
r.RateLimiter = time.NewTicker(time.Microsecond * time.Duration(ratemicros))
|
||||||
} else {
|
} else {
|
||||||
r.rateCounter = ring.New(conf.Threads * 5)
|
|
||||||
//Million rps is probably a decent hardcoded upper speedlimit
|
//Million rps is probably a decent hardcoded upper speedlimit
|
||||||
r.RateLimiter = time.NewTicker(time.Microsecond * 1)
|
r.RateLimiter = time.NewTicker(time.Microsecond * 1)
|
||||||
}
|
}
|
||||||
@ -70,17 +72,10 @@ func (r *RateThrottle) ChangeRate(rate int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r.RateLimiter.Stop()
|
r.RateLimiter.Stop()
|
||||||
if rate > 0 {
|
r.RateLimiter = time.NewTicker(time.Microsecond * time.Duration(ratemicros))
|
||||||
r.RateLimiter = time.NewTicker(time.Microsecond * time.Duration(ratemicros))
|
|
||||||
// reset the rate counter
|
|
||||||
r.rateCounter = ring.New(rate * 5)
|
|
||||||
} else {
|
|
||||||
r.RateLimiter = time.NewTicker(time.Microsecond * 1)
|
|
||||||
// reset the rate counter
|
|
||||||
r.rateCounter = ring.New(r.Config.Threads * 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Config.Rate = int64(rate)
|
r.Config.Rate = int64(rate)
|
||||||
|
// reset the rate counter
|
||||||
|
r.rateCounter = ring.New(rate * 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
// rateTick adds a new duration measurement tick to rate counter
|
// rateTick adds a new duration measurement tick to rate counter
|
||||||
|
|||||||
@ -131,16 +131,7 @@ func mergeMaps(m1 map[string][]string, m2 map[string][]string) map[string][]stri
|
|||||||
merged[k] = v
|
merged[k] = v
|
||||||
}
|
}
|
||||||
for key, value := range m2 {
|
for key, value := range m2 {
|
||||||
if _, ok := merged[key]; !ok {
|
merged[key] = value
|
||||||
// Key not found, add it
|
|
||||||
merged[key] = value
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, entry := range value {
|
|
||||||
if !StrInSlice(entry, merged[key]) {
|
|
||||||
merged[key] = append(merged[key], entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return merged
|
return merged
|
||||||
}
|
}
|
||||||
@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
func TestToCSV(t *testing.T) {
|
func TestToCSV(t *testing.T) {
|
||||||
result := ffuf.Result{
|
result := ffuf.Result{
|
||||||
Input: map[string][]byte{"x": {66}, "FFUFHASH": {65}},
|
Input: map[string][]byte{"x": {66}},
|
||||||
Position: 1,
|
Position: 1,
|
||||||
StatusCode: 200,
|
StatusCode: 200,
|
||||||
ContentLength: 3,
|
ContentLength: 3,
|
||||||
@ -37,8 +37,8 @@ func TestToCSV(t *testing.T) {
|
|||||||
"5",
|
"5",
|
||||||
"application/json",
|
"application/json",
|
||||||
"123ns",
|
"123ns",
|
||||||
"resultfile",
|
"resultfile"}) {
|
||||||
"A"}) {
|
|
||||||
t.Errorf("CSV was not generated in expected format")
|
t.Errorf("CSV was not generated in expected format")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
shell.nix
24
shell.nix
@ -1,24 +0,0 @@
|
|||||||
{ pkgs ? (
|
|
||||||
let
|
|
||||||
inherit (builtins) fetchTree fromJSON readFile;
|
|
||||||
inherit ((fromJSON (readFile ./flake.lock)).nodes) nixpkgs gomod2nix;
|
|
||||||
in
|
|
||||||
import (fetchTree nixpkgs.locked) {
|
|
||||||
overlays = [
|
|
||||||
(import "${fetchTree gomod2nix.locked}/overlay.nix")
|
|
||||||
];
|
|
||||||
}
|
|
||||||
)
|
|
||||||
, mkGoEnv ? pkgs.mkGoEnv
|
|
||||||
, gomod2nix ? pkgs.gomod2nix
|
|
||||||
}:
|
|
||||||
|
|
||||||
let
|
|
||||||
goEnv = mkGoEnv { pwd = ./.; };
|
|
||||||
in
|
|
||||||
pkgs.mkShell {
|
|
||||||
packages = [
|
|
||||||
goEnv
|
|
||||||
gomod2nix
|
|
||||||
];
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user