Codebase list ffuf / d786c68
Import upstream version 1.3.1 Kali Janitor 3 years ago
15 changed file(s) with 127 addition(s) and 87 deletion(s). Raw diff Collapse all Expand all
99 gcflags:
1010 - all=-trimpath={{.Env.GOPATH}}
1111 ldflags: |
12 -s -w -X github.com/ffuf/ffuf/pkg/ffuf.VERSION_APPENDIX= -extldflags '-static'
12 -s -w -X 'github.com/ffuf/ffuf/pkg/ffuf.VERSION_APPENDIX= exclusive <3' -extldflags '-static'
1313 goos:
1414 - linux
1515 - windows
11 - master
22 - New
33 - Changed
4
5 - v1.3.1
6 - New
7 - Added a CLI flag to disable the interactive mode
8 - Changed
9 - Do not read the last newline in the end of the raw request file when using -request
10 - Fixed an issue with storing the matches for recursion jobs
11 - Fixed the way the "size" is calculated, it should match content-length now
12 - Fixed an issue with header canonicalization when a keyword was just a part of the header name
13 - Fixed output writing so it doesn't silently fail if it needs to create directories recursively
414
515 - v1.3.0
616 - New
2121 * [jvesiluoma](https://github.com/jvesiluoma)
2222 * [Kiblyn11](https://github.com/Kiblyn11)
2323 * [lc](https://github.com/lc)
24 * [mprencipe](https://github.com/mprencipe)
2425 * [nnwakelam](https://twitter.com/nnwakelam)
2526 * [noraj](https://pwn.by/noraj)
2627 * [oh6hay](https://github.com/oh6hay)
157157 To define the test case for ffuf, use the keyword `FUZZ` anywhere in the URL (`-u`), headers (`-H`), or POST data (`-d`).
158158
159159 ```
160 Fuzz Faster U Fool - v1.2.0-git
160 Fuzz Faster U Fool - v1.3.0-dev
161161
162162 HTTP OPTIONS:
163163 -H Header `"Name: Value"`, separated by colon. Multiple -H flags are accepted.
175175 -x Proxy URL (SOCKS5 or HTTP). For example: http://127.0.0.1:8080 or socks5://127.0.0.1:8080
176176
177177 GENERAL OPTIONS:
178 -V Show version information. (default: false)
179 -ac Automatically calibrate filtering options (default: false)
180 -acc Custom auto-calibration string. Can be used multiple times. Implies -ac
181 -c Colorize output. (default: false)
182 -config Load configuration from a file
183 -maxtime Maximum running time in seconds for entire process. (default: 0)
184 -maxtime-job Maximum running time in seconds per job. (default: 0)
185 -p Seconds of `delay` between requests, or a range of random delay. For example "0.1" or "0.1-2.0"
186 -rate Rate of requests per second (default: 0)
187 -s Do not print additional information (silent mode) (default: false)
188 -sa Stop on all error cases. Implies -sf and -se. (default: false)
189 -se Stop on spurious errors (default: false)
190 -sf Stop when > 95% of responses return 403 Forbidden (default: false)
191 -t Number of concurrent threads. (default: 40)
192 -v Verbose output, printing full URL and redirect location (if any) with the results. (default: false)
178 -V Show version information. (default: false)
179 -ac Automatically calibrate filtering options (default: false)
180 -acc Custom auto-calibration string. Can be used multiple times. Implies -ac
181 -c Colorize output. (default: false)
182 -config Load configuration from a file
183 -maxtime Maximum running time in seconds for entire process. (default: 0)
184 -maxtime-job Maximum running time in seconds per job. (default: 0)
185 -noninteractive Disable the interactive console functionality (default: false)
186 -p Seconds of `delay` between requests, or a range of random delay. For example "0.1" or "0.1-2.0"
187 -rate Rate of requests per second (default: 0)
188 -s Do not print additional information (silent mode) (default: false)
189 -sa Stop on all error cases. Implies -sf and -se. (default: false)
190 -se Stop on spurious errors (default: false)
191 -sf Stop when > 95% of responses return 403 Forbidden (default: false)
192 -t Number of concurrent threads. (default: 40)
193 -v Verbose output, printing full URL and redirect location (if any) with the results. (default: false)
193194
194195 MATCHER OPTIONS:
195 -mc Match HTTP status codes, or "all" for everything. (default: 200,204,301,302,307,401,403,405)
196 -ml Match amount of lines in response
197 -mr Match regexp
198 -ms Match HTTP response size
199 -mw Match amount of words in response
196 -mc Match HTTP status codes, or "all" for everything. (default: 200,204,301,302,307,401,403,405)
197 -ml Match amount of lines in response
198 -mr Match regexp
199 -ms Match HTTP response size
200 -mw Match amount of words in response
200201
201202 FILTER OPTIONS:
202 -fc Filter HTTP status codes from response. Comma separated list of codes and ranges
203 -fl Filter by amount of lines in response. Comma separated list of line counts and ranges
204 -fr Filter regexp
205 -fs Filter HTTP response size. Comma separated list of sizes and ranges
206 -fw Filter by amount of words in response. Comma separated list of word counts and ranges
203 -fc Filter HTTP status codes from response. Comma separated list of codes and ranges
204 -fl Filter by amount of lines in response. Comma separated list of line counts and ranges
205 -fr Filter regexp
206 -fs Filter HTTP response size. Comma separated list of sizes and ranges
207 -fw Filter by amount of words in response. Comma separated list of word counts and ranges
207208
208209 INPUT OPTIONS:
209 -D DirSearch wordlist compatibility mode. Used in conjunction with -e flag. (default: false)
210 -e Comma separated list of extensions. Extends FUZZ keyword.
211 -ic Ignore wordlist comments (default: false)
212 -input-cmd Command producing the input. --input-num is required when using this input method. Overrides -w.
213 -input-num Number of inputs to test. Used in conjunction with --input-cmd. (default: 100)
214 -mode Multi-wordlist operation mode. Available modes: clusterbomb, pitchfork (default: clusterbomb)
215 -request File containing the raw http request
216 -request-proto Protocol to use along with raw request (default: https)
217 -w Wordlist file path and (optional) keyword separated by colon. eg. '/path/to/wordlist:KEYWORD'
210 -D DirSearch wordlist compatibility mode. Used in conjunction with -e flag. (default: false)
211 -e Comma separated list of extensions. Extends FUZZ keyword.
212 -ic Ignore wordlist comments (default: false)
213 -input-cmd Command producing the input. --input-num is required when using this input method. Overrides -w.
214 -input-num Number of inputs to test. Used in conjunction with --input-cmd. (default: 100)
215 -input-shell Shell to be used for running command
216 -mode Multi-wordlist operation mode. Available modes: clusterbomb, pitchfork (default: clusterbomb)
217 -request File containing the raw http request
218 -request-proto Protocol to use along with raw request (default: https)
219 -w Wordlist file path and (optional) keyword separated by colon. eg. '/path/to/wordlist:KEYWORD'
218220
219221 OUTPUT OPTIONS:
220 -debug-log Write all of the internal logging to the specified file.
221 -o Write output to file
222 -od Directory path to store matched results to.
223 -of Output file format. Available formats: json, ejson, html, md, csv, ecsv (or, 'all' for all formats) (default: json)
224 -or Don't create the output file if we don't have results
222 -debug-log Write all of the internal logging to the specified file.
223 -o Write output to file
224 -od Directory path to store matched results to.
225 -of Output file format. Available formats: json, ejson, html, md, csv, ecsv (or, 'all' for all formats) (default: json)
226 -or Don't create the output file if we don't have results (default: false)
225227
226228 EXAMPLE USAGE:
227229 Fuzz file paths from wordlist.txt, match all responses but filter out those with content-size 42.
260262 queueskip - advance to the next queued recursion job
261263 restart - restart and resume the current ffuf job
262264 resume - resume current ffuf job (or: ENTER)
263 show - show results
265 show - show results for the current job
264266 savejson [filename] - save current matches to a file
265267 help - you are looking at it
266268 >
3030 delay = ""
3131 maxtime = 0
3232 maxtimejob = 0
33 noninteractive = false
3334 quiet = false
3435 rate = 0
3536 stopon403 = false
6060 Description: "",
6161 Flags: make([]UsageFlag, 0),
6262 Hidden: false,
63 ExpectedFlags: []string{"ac", "acc", "c", "config", "maxtime", "maxtime-job", "p", "rate", "s", "sa", "se", "sf", "t", "v", "V"},
63 ExpectedFlags: []string{"ac", "acc", "c", "config", "maxtime", "maxtime-job", "noninteractive", "p", "rate", "s", "sa", "se", "sf", "t", "v", "V"},
6464 }
6565 u_compat := UsageSection{
6666 Name: "COMPATIBILITY OPTIONS",
6060 flag.BoolVar(&opts.Output.OutputCreateEmptyFile, "or", opts.Output.OutputCreateEmptyFile, "Don't create the output file if we don't have results")
6161 flag.BoolVar(&opts.General.AutoCalibration, "ac", opts.General.AutoCalibration, "Automatically calibrate filtering options")
6262 flag.BoolVar(&opts.General.Colors, "c", opts.General.Colors, "Colorize output.")
63 flag.BoolVar(&opts.General.Noninteractive, "noninteractive", opts.General.Noninteractive, "Disable the interactive console functionality")
6364 flag.BoolVar(&opts.General.Quiet, "s", opts.General.Quiet, "Do not print additional information (silent mode)")
6465 flag.BoolVar(&opts.General.ShowVersion, "V", opts.General.ShowVersion, "Show version information.")
6566 flag.BoolVar(&opts.General.StopOn403, "sf", opts.General.StopOn403, "Stop when > 95% of responses return 403 Forbidden")
197198 fmt.Fprintf(os.Stderr, "Error in autocalibration, exiting: %s\n", err)
198199 os.Exit(1)
199200 }
200 go func() {
201 err := interactive.Handle(job)
202 if err != nil {
203 log.Printf("Error while trying to initialize interactive session: %s", err)
204 }
205 }()
201 if !conf.Noninteractive {
202 go func() {
203 err := interactive.Handle(job)
204 if err != nil {
205 log.Printf("Error while trying to initialize interactive session: %s", err)
206 }
207 }()
208 }
206209
207210 // Job handles waiting for goroutines to complete itself
208211 job.Start()
2929 MaxTime int `json:"maxtime"`
3030 MaxTimeJob int `json:"maxtime_job"`
3131 Method string `json:"method"`
32 Noninteractive bool `json:"noninteractive"`
3233 OutputDirectory string `json:"outputdirectory"`
3334 OutputFile string `json:"outputfile"`
3435 OutputFormat string `json:"outputformat"`
7879 conf.MaxTime = 0
7980 conf.MaxTimeJob = 0
8081 conf.Method = "GET"
82 conf.Noninteractive = false
8183 conf.ProgressFrequency = 125
8284 conf.ProxyURL = ""
8385 conf.Quiet = false
4545 Result(resp Response)
4646 PrintResult(res Result)
4747 SaveFile(filename, format string) error
48 GetResults() []Result
49 SetResults(results []Result)
48 GetCurrentResults() []Result
49 SetCurrentResults(results []Result)
5050 Reset()
51 Cycle()
5152 }
5253
5354 type Result struct {
122122 j.interruptMonitor()
123123 for j.jobsInQueue() {
124124 j.prepareQueueJob()
125 j.Reset()
125 j.Reset(true)
126126 j.RunningJob = true
127127 j.startExecution()
128128 }
134134 }
135135
136136 // Reset resets the counters and wordlist position for a job
137 func (j *Job) Reset() {
137 func (j *Job) Reset(cycle bool) {
138138 j.Input.Reset()
139139 j.Counter = 0
140140 j.skipQueue = false
141141 j.startTimeJob = time.Now()
142 j.Output.Reset()
142 if cycle {
143 j.Output.Cycle()
144 } else {
145 j.Output.Reset()
146 }
143147 }
144148
145149 func (j *Job) jobsInQueue() bool {
4848 Delay string
4949 MaxTime int
5050 MaxTimeJob int
51 Noninteractive bool
5152 Quiet bool
5253 Rate int
5354 ShowVersion bool `toml:"-"`
108109 c.General.Delay = ""
109110 c.General.MaxTime = 0
110111 c.General.MaxTimeJob = 0
112 c.General.Noninteractive = false
111113 c.General.Quiet = false
112114 c.General.Rate = 0
113115 c.General.ShowVersion = false
254256 // except if used in custom defined header
255257 var CanonicalNeeded = true
256258 for _, a := range conf.CommandKeywords {
257 if a == hs[0] {
259 if strings.Contains(hs[0], a) {
258260 CanonicalNeeded = false
259261 }
260262 }
261263 // check if part of InputProviders
262264 if CanonicalNeeded {
263265 for _, b := range conf.InputProviders {
264 if b.Keyword == hs[0] {
266 if strings.Contains(hs[0], b.Keyword) {
265267 CanonicalNeeded = false
266268 }
267269 }
394396 conf.Timeout = parseOpts.HTTP.Timeout
395397 conf.MaxTime = parseOpts.General.MaxTime
396398 conf.MaxTimeJob = parseOpts.General.MaxTimeJob
399 conf.Noninteractive = parseOpts.General.Noninteractive
397400 conf.Verbose = parseOpts.General.Verbose
398401
399402 // Handle copy as curl situation where POST method is implied by --data flag. If method is set to anything but GET, NOOP
485488 }
486489 conf.Data = string(b)
487490
491 // Remove newline (typically added by the editor) at the end of the file
492 if strings.HasSuffix(conf.Data, "\r\n") {
493 conf.Data = conf.Data[:len(conf.Data)-2]
494 } else if strings.HasSuffix(conf.Data, "\n") {
495 conf.Data = conf.Data[:len(conf.Data)-1]
496 }
488497 return nil
489498 }
490499
11
22 var (
33 //VERSION holds the current version number
4 VERSION = "1.3.0"
4 VERSION = "1.3.1"
55 //VERSION_APPENDIX holds additional version definition
66 VERSION_APPENDIX = "-exclusive-dev"
77 )
5252 i.paused = false
5353 i.Job.Resume()
5454 case "restart":
55 i.Job.Reset()
55 i.Job.Reset(false)
5656 i.paused = false
5757 i.Job.Output.Info("Restarting the current ffuf job!")
5858 i.Job.Resume()
5959 case "show":
60 for _, r := range i.Job.Output.GetResults() {
60 for _, r := range i.Job.Output.GetCurrentResults() {
6161 i.Job.Output.PrintResult(r)
6262 }
6363 case "savejson":
149149 }
150150
151151 results := make([]ffuf.Result, 0)
152 for _, res := range i.Job.Output.GetResults() {
152 for _, res := range i.Job.Output.GetCurrentResults() {
153153 fakeResp := &ffuf.Response{
154154 StatusCode: res.StatusCode,
155155 ContentLines: res.ContentLength,
161161 results = append(results, res)
162162 }
163163 }
164 i.Job.Output.SetResults(results)
164 i.Job.Output.SetCurrentResults(results)
165165 }
166166 }
167167
228228 queueskip - advance to the next queued recursion job
229229 restart - restart and resume the current ffuf job
230230 resume - resume current ffuf job (or: ENTER)
231 show - show results
231 show - show results for the current job
232232 savejson [filename] - save current matches to a file
233233 help - you are looking at it
234234 `
2525 )
2626
2727 type Stdoutput struct {
28 config *ffuf.Config
29 Results []ffuf.Result
28 config *ffuf.Config
29 Results []ffuf.Result
30 CurrentResults []ffuf.Result
3031 }
3132
3233 func NewStdoutput(conf *ffuf.Config) *Stdoutput {
3334 var outp Stdoutput
3435 outp.config = conf
35 outp.Results = []ffuf.Result{}
36 outp.Results = make([]ffuf.Result, 0)
37 outp.CurrentResults = make([]ffuf.Result, 0)
3638 return &outp
3739 }
3840
132134
133135 // Reset resets the result slice
134136 func (s *Stdoutput) Reset() {
135 s.Results = make([]ffuf.Result, 0)
137 s.CurrentResults = make([]ffuf.Result, 0)
138 }
139
140 // Cycle moves the CurrentResults to Results and resets the results slice
141 func (s *Stdoutput) Cycle() {
142 s.Results = append(s.Results, s.CurrentResults...)
143 s.Reset()
136144 }
137145
138146 // GetResults returns the result slice
139 func (s *Stdoutput) GetResults() []ffuf.Result {
140 return s.Results
147 func (s *Stdoutput) GetCurrentResults() []ffuf.Result {
148 return s.CurrentResults
141149 }
142150
143151 // SetResults sets the result slice
144 func (s *Stdoutput) SetResults(results []ffuf.Result) {
145 s.Results = results
152 func (s *Stdoutput) SetCurrentResults(results []ffuf.Result) {
153 s.CurrentResults = results
146154 }
147155
148156 func (s *Stdoutput) Progress(status ffuf.Progress) {
221229 }
222230
223231 s.config.OutputFile = BaseFilename + ".json"
224 err = writeJSON(filename, s.config, s.Results)
232 err = writeJSON(filename, s.config, res)
225233 if err != nil {
226234 s.Error(err.Error())
227235 }
228236
229237 s.config.OutputFile = BaseFilename + ".ejson"
230 err = writeEJSON(filename, s.config, s.Results)
238 err = writeEJSON(filename, s.config, res)
231239 if err != nil {
232240 s.Error(err.Error())
233241 }
234242
235243 s.config.OutputFile = BaseFilename + ".html"
236 err = writeHTML(filename, s.config, s.Results)
244 err = writeHTML(filename, s.config, res)
237245 if err != nil {
238246 s.Error(err.Error())
239247 }
240248
241249 s.config.OutputFile = BaseFilename + ".md"
242 err = writeMarkdown(filename, s.config, s.Results)
250 err = writeMarkdown(filename, s.config, res)
243251 if err != nil {
244252 s.Error(err.Error())
245253 }
246254
247255 s.config.OutputFile = BaseFilename + ".csv"
248 err = writeCSV(filename, s.config, s.Results, false)
256 err = writeCSV(filename, s.config, res, false)
249257 if err != nil {
250258 s.Error(err.Error())
251259 }
252260
253261 s.config.OutputFile = BaseFilename + ".ecsv"
254 err = writeCSV(filename, s.config, s.Results, true)
262 err = writeCSV(filename, s.config, res, true)
255263 if err != nil {
256264 s.Error(err.Error())
257265 }
265273 var err error
266274 switch format {
267275 case "all":
268 err = s.writeToAll(filename, s.config, s.Results)
276 err = s.writeToAll(filename, s.config, append(s.Results, s.CurrentResults...))
269277 case "json":
270 err = writeJSON(filename, s.config, s.Results)
278 err = writeJSON(filename, s.config, append(s.Results, s.CurrentResults...))
271279 case "ejson":
272 err = writeEJSON(filename, s.config, s.Results)
280 err = writeEJSON(filename, s.config, append(s.Results, s.CurrentResults...))
273281 case "html":
274 err = writeHTML(filename, s.config, s.Results)
282 err = writeHTML(filename, s.config, append(s.Results, s.CurrentResults...))
275283 case "md":
276 err = writeMarkdown(filename, s.config, s.Results)
284 err = writeMarkdown(filename, s.config, append(s.Results, s.CurrentResults...))
277285 case "csv":
278 err = writeCSV(filename, s.config, s.Results, false)
286 err = writeCSV(filename, s.config, append(s.Results, s.CurrentResults...), false)
279287 case "ecsv":
280 err = writeCSV(filename, s.config, s.Results, true)
288 err = writeCSV(filename, s.config, append(s.Results, s.CurrentResults...), true)
281289 }
282290 return err
283291 }
318326 ResultFile: resp.ResultFile,
319327 Host: resp.Request.Host,
320328 }
321 s.Results = append(s.Results, sResult)
329 s.CurrentResults = append(s.CurrentResults, sResult)
322330 // Output the result
323331 s.PrintResult(sResult)
324332 }
327335 var fileContent, fileName, filePath string
328336 // Create directory if needed
329337 if s.config.OutputDirectory != "" {
330 err := os.Mkdir(s.config.OutputDirectory, 0750)
338 err := os.MkdirAll(s.config.OutputDirectory, 0750)
331339 if err != nil {
332340 if !os.IsExist(err) {
333341 s.Error(err.Error())
1212 "strconv"
1313 "strings"
1414 "time"
15 "unicode/utf8"
1615
1716 "github.com/ffuf/ffuf/pkg/ffuf"
1817 )
146145 }
147146
148147 if respbody, err := ioutil.ReadAll(httpresp.Body); err == nil {
149 resp.ContentLength = int64(utf8.RuneCountInString(string(respbody)))
148 resp.ContentLength = int64(len(string(respbody)))
150149 resp.Data = respbody
151150 }
152151