package ffuf import ( "fmt" "log" "math/rand" "strconv" "time" ) func (j *Job) autoCalibrationStrings() map[string][]string { rand.Seed(time.Now().UnixNano()) cInputs := make(map[string][]string) if len(j.Config.AutoCalibrationStrings) < 1 { cInputs["basic_admin"] = append(cInputs["basic_admin"], "admin"+RandomString(16)) cInputs["basic_admin"] = append(cInputs["basic_admin"], "admin"+RandomString(8)) cInputs["htaccess"] = append(cInputs["htaccess"], ".htaccess"+RandomString(16)) cInputs["htaccess"] = append(cInputs["htaccess"], ".htaccess"+RandomString(8)) cInputs["basic_random"] = append(cInputs["basic_random"], RandomString(16)) cInputs["basic_random"] = append(cInputs["basic_random"], RandomString(8)) if j.Config.AutoCalibrationStrategy == "advanced" { // Add directory tests and .htaccess too cInputs["admin_dir"] = append(cInputs["admin_dir"], "admin"+RandomString(16)+"/") cInputs["admin_dir"] = append(cInputs["admin_dir"], "admin"+RandomString(8)+"/") cInputs["random_dir"] = append(cInputs["random_dir"], RandomString(16)+"/") cInputs["random_dir"] = append(cInputs["random_dir"], RandomString(8)+"/") } } else { cInputs["custom"] = append(cInputs["custom"], j.Config.AutoCalibrationStrings...) } return cInputs } func (j *Job) calibrationRequest(inputs map[string][]byte) (Response, error) { basereq := BaseRequest(j.Config) req, err := j.Runner.Prepare(inputs, &basereq) if err != nil { j.Output.Error(fmt.Sprintf("Encountered an error while preparing autocalibration request: %s\n", err)) j.incError() log.Printf("%s", err) return Response{}, err } resp, err := j.Runner.Execute(&req) if err != nil { j.Output.Error(fmt.Sprintf("Encountered an error while executing autocalibration request: %s\n", err)) j.incError() log.Printf("%s", err) return Response{}, err } // Only calibrate on responses that would be matched otherwise if j.isMatch(resp) { return resp, nil } return resp, fmt.Errorf("Response wouldn't be matched") } //CalibrateForHost runs autocalibration for a specific host func (j *Job) CalibrateForHost(host string, input map[string][]byte) error { if j.Config.MatcherManager.CalibratedForDomain(host) { return nil } if input[j.Config.AutoCalibrationKeyword] == nil { return fmt.Errorf("Autocalibration keyword \"%s\" not found in the request.", j.Config.AutoCalibrationKeyword) } cStrings := j.autoCalibrationStrings() for _, v := range cStrings { responses := make([]Response, 0) for _, cs := range v { input[j.Config.AutoCalibrationKeyword] = []byte(cs) resp, err := j.calibrationRequest(input) if err != nil { continue } responses = append(responses, resp) err = j.calibrateFilters(responses, true) if err != nil { j.Output.Error(fmt.Sprintf("%s", err)) } } } j.Config.MatcherManager.SetCalibratedForHost(host, true) return nil } //CalibrateResponses returns slice of Responses for randomly generated filter autocalibration requests func (j *Job) Calibrate(input map[string][]byte) error { if j.Config.MatcherManager.Calibrated() { return nil } cInputs := j.autoCalibrationStrings() for _, v := range cInputs { responses := make([]Response, 0) for _, cs := range v { input[j.Config.AutoCalibrationKeyword] = []byte(cs) resp, err := j.calibrationRequest(input) if err != nil { continue } responses = append(responses, resp) err = j.calibrateFilters(responses, false) if err != nil { j.Output.Error(fmt.Sprintf("%s", err)) } } } j.Config.MatcherManager.SetCalibrated(true) return nil } //CalibrateIfNeeded runs a self-calibration task for filtering options (if needed) by requesting random resources and // configuring the filters accordingly func (j *Job) CalibrateIfNeeded(host string, input map[string][]byte) error { j.calibMutex.Lock() defer j.calibMutex.Unlock() if !j.Config.AutoCalibration { return nil } if j.Config.AutoCalibrationPerHost { return j.CalibrateForHost(host, input) } return j.Calibrate(input) } func (j *Job) calibrateFilters(responses []Response, perHost bool) error { // Work down from the most specific common denominator if len(responses) > 0 { // Content length baselineSize := responses[0].ContentLength sizeMatch := true for _, r := range responses { if baselineSize != r.ContentLength { sizeMatch = false } } if sizeMatch { if perHost { // Check if already filtered for _, f := range j.Config.MatcherManager.FiltersForDomain(responses[0].Request.Host) { match, _ := f.Filter(&responses[0]) if match { // Already filtered return nil } } _ = j.Config.MatcherManager.AddPerDomainFilter(responses[0].Request.Host, "size", strconv.FormatInt(baselineSize, 10)) return nil } else { // Check if already filtered for _, f := range j.Config.MatcherManager.GetFilters() { match, _ := f.Filter(&responses[0]) if match { // Already filtered return nil } } _ = j.Config.MatcherManager.AddFilter("size", strconv.FormatInt(baselineSize, 10), false) return nil } } // Content words baselineWords := responses[0].ContentWords wordsMatch := true for _, r := range responses { if baselineWords != r.ContentWords { wordsMatch = false } } if wordsMatch { if perHost { // Check if already filtered for _, f := range j.Config.MatcherManager.FiltersForDomain(responses[0].Request.Host) { match, _ := f.Filter(&responses[0]) if match { // Already filtered return nil } } _ = j.Config.MatcherManager.AddPerDomainFilter(responses[0].Request.Host, "word", strconv.FormatInt(baselineWords, 10)) return nil } else { // Check if already filtered for _, f := range j.Config.MatcherManager.GetFilters() { match, _ := f.Filter(&responses[0]) if match { // Already filtered return nil } } _ = j.Config.MatcherManager.AddFilter("word", strconv.FormatInt(baselineSize, 10), false) return nil } } // Content lines baselineLines := responses[0].ContentLines linesMatch := true for _, r := range responses { if baselineLines != r.ContentLines { linesMatch = false } } if linesMatch { if perHost { // Check if already filtered for _, f := range j.Config.MatcherManager.FiltersForDomain(responses[0].Request.Host) { match, _ := f.Filter(&responses[0]) if match { // Already filtered return nil } } _ = j.Config.MatcherManager.AddPerDomainFilter(responses[0].Request.Host, "line", strconv.FormatInt(baselineLines, 10)) return nil } else { // Check if already filtered for _, f := range j.Config.MatcherManager.GetFilters() { match, _ := f.Filter(&responses[0]) if match { // Already filtered return nil } } _ = j.Config.MatcherManager.AddFilter("line", strconv.FormatInt(baselineSize, 10), false) return nil } } } return fmt.Errorf("No common filtering values found") }