diff --git a/librecursebuster/main_test.go b/librecursebuster/main_test.go index 62b7dc7..2e0d705 100644 --- a/librecursebuster/main_test.go +++ b/librecursebuster/main_test.go @@ -476,7 +476,7 @@ func TestWeirdWords(t *testing.T) { t.Parallel() finished := make(chan struct{}) cfg := getDefaultConfig() - gState, urlSlice := preSetupTest(cfg, "2020", finished, t) + gState, urlSlice := preSetupTest(cfg, "2021", finished, t) //add some woderful and weird things to the wordlist for i := 0; i < 256; i++ { gState.WordList = append(gState.WordList, "te"+string(i)+"st") @@ -502,6 +502,51 @@ func TestWeirdWords(t *testing.T) { } } +func TestGoodCodes(t *testing.T) { + t.Parallel() + finished := make(chan struct{}) + cfg := getDefaultConfig() + cfg.GoodResponses = "500" + gState, urlSlice := preSetupTest(cfg, "2022", finished, t) + found := postSetupTest(urlSlice, gState) + gState.Wait() + + tested := []string{} + ok := []string{ + "/c", "/c/", + } + for _, i := range ok { + tested = append(tested, i) + if x, ok := found[i]; !ok || x == nil { + t.Error("Did not find " + i) + } + } + + //check for values that should not have been found + for k := range found { + if strings.Contains(k, "z") { + t.Error("Found (but should not have) " + k) + } + } + + if x, ok := found["/a/x"]; ok && x != nil { + t.Error("Found (but should not have) /a/x") + } + + if x, ok := found["/a"]; ok && x != nil { + t.Error("Found (but should not have) /a") + } + + if x, ok := found["/a/b"]; ok && x != nil { + t.Error("Found (but should not have) /a/b") + } + + if x, ok := found["/b/y"]; ok && x != nil { + t.Error("Found (but should not have) /b/y") + } + +} + func postSetupTest(urlSlice []string, gState *State) (found map[string]*http.Response) { //start up the management goroutines go gState.ManageRequests() diff --git a/librecursebuster/net.go b/librecursebuster/net.go index e0b45ed..1548277 100644 --- a/librecursebuster/net.go +++ b/librecursebuster/net.go @@ -129,10 +129,18 @@ func (gState *State) evaluateURL(method string, urlString string, client *http.C } //Check if we care about it (header only) section - if gState.BadResponses[headResp.StatusCode] { - success = false - //<-gState.Chans.workersChan - return headResp, content, success + if len(gState.GoodResponses) > 0 { + if v, ok := gState.GoodResponses[headResp.StatusCode]; !ok || !v { + success = false + //<-gState.Chans.workersChan + return headResp, content, success + } + } else { + if gState.BadResponses[headResp.StatusCode] { + success = false + //<-gState.Chans.workersChan + return headResp, content, success + } } //this is all we have to do if we aren't doing GET's @@ -166,9 +174,17 @@ func (gState *State) evaluateURL(method string, urlString string, client *http.C } //Check if we care about it (response code only) section - if gState.BadResponses[headResp.StatusCode] { - success = false - return headResp, content, success + if len(gState.GoodResponses) > 0 { + if v, ok := gState.GoodResponses[headResp.StatusCode]; !ok || !v { + success = false + //<-gState.Chans.workersChan + return headResp, content, success + } + } else { + if gState.BadResponses[headResp.StatusCode] { + success = false + return headResp, content, success + } } //check for bad headers in the response diff --git a/librecursebuster/structs.go b/librecursebuster/structs.go index dd89ede..c4e35c6 100644 --- a/librecursebuster/structs.go +++ b/librecursebuster/structs.go @@ -152,6 +152,7 @@ type State struct { Blacklist map[string]bool Whitelist map[string]bool BadResponses map[int]bool //response codes to consider *dont care* (this might be worth putting in per host state, but idk how) + GoodResponses map[int]bool //response codes to consider *only care* Extensions []string Methods []string // WordlistLen *uint32 @@ -186,6 +187,7 @@ func (s *State) AddWG() { func (State) Init() *State { s := &State{ BadResponses: make(map[int]bool), + GoodResponses: make(map[int]bool), Whitelist: make(map[string]bool), Blacklist: make(map[string]bool), StopDir: make(chan struct{}, 1), @@ -265,6 +267,7 @@ type Config struct { AppendDir bool Auth string BadResponses string + GoodResponses string BadHeader ArrayStringFlag BodyContent string BlacklistLocation string @@ -353,12 +356,24 @@ func (s *State) SetupState() { for _, x := range strings.Split(s.Cfg.BadResponses, ",") { i, err := strconv.Atoi(x) if err != nil { - fmt.Println("Bad error code supplied!") + fmt.Println("Bad response code supplied!") panic(err) } s.BadResponses[i] = true //this is probably a candidate for individual urls. Unsure how to config that cleanly though } + for _, x := range strings.Split(s.Cfg.GoodResponses, ",") { + if x == "" { + continue + } + i, err := strconv.Atoi(x) + if err != nil { + fmt.Println("Bad response code supplied!") + panic(err) + } + s.GoodResponses[i] = true + } + s.Client = s.ConfigureHTTPClient(false) s.BurpClient = s.ConfigureHTTPClient(true) diff --git a/main.go b/main.go index 8eb592f..f8f505c 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,7 @@ import ( "github.com/fatih/color" ) -const version = "1.6.8" +const version = "1.6.9" func main() { if runtime.GOOS == "windows" { //lol goos @@ -31,11 +31,12 @@ func main() { globalState.Cfg.Version = version //** totesTested := uint64(0) globalState.TotalTested = &totesTested - flag.BoolVar(&globalState.Cfg.ShowAll, "all", false, "Show, and write the result of all checks") //Test written - flag.BoolVar(&globalState.Cfg.AppendDir, "appendslash", false, "Append a / to all directory bruteforce requests (like extension, but slash instead of .yourthing)") //Test Written - flag.BoolVar(&globalState.Cfg.Ajax, "ajax", false, "Add the X-Requested-With: XMLHttpRequest header to all requests") //Test Written - flag.StringVar(&globalState.Cfg.Auth, "auth", "", "Basic auth. Supply this with the base64 encoded portion to be placed after the word 'Basic' in the Authorization header.") //Test Written - flag.StringVar(&globalState.Cfg.BadResponses, "bad", "404", "Responses to consider 'bad' or 'not found'. Comma-separated. This works the opposite way of gobuster!") //Test Written + flag.BoolVar(&globalState.Cfg.ShowAll, "all", false, "Show, and write the result of all checks") //Test written + flag.BoolVar(&globalState.Cfg.AppendDir, "appendslash", false, "Append a / to all directory bruteforce requests (like extension, but slash instead of .yourthing)") //Test Written + flag.BoolVar(&globalState.Cfg.Ajax, "ajax", false, "Add the X-Requested-With: XMLHttpRequest header to all requests") //Test Written + flag.StringVar(&globalState.Cfg.Auth, "auth", "", "Basic auth. Supply this with the base64 encoded portion to be placed after the word 'Basic' in the Authorization header.") //Test Written + flag.StringVar(&globalState.Cfg.BadResponses, "bad", "404", "Responses to consider 'bad' or 'not found'. Comma-separated. This works the opposite way of gobuster!") //Test Written + flag.StringVar(&globalState.Cfg.GoodResponses, "good", "", "Whitelist of response codes to consider good. If this flag is used, ONLY the provided codes will be considered 'good'.") flag.Var(&globalState.Cfg.BadHeader, "badheader", "Check for presence of this header. If an prefix match is found, the response is considered bad.Supply as key:value. Can specify multiple - eg '-badheader Location:cats -badheader X-ATT-DeviceId:XXXXX'") //Test Written flag.StringVar(&globalState.Cfg.BodyContent, "body", "", "File containing content to send in the body of the request. Content-length header will be set accordingly. Note: HEAD requests will/should fail (server will reply with a 400). Set the '-nohead' option to prevent HEAD being sent before a GET.") //Test Written flag.StringVar(&globalState.Cfg.BlacklistLocation, "blacklist", "", "Blacklist of prefixes to not check. Will not check on exact matches.") //Test Written