diff --git a/librecursebuster/main_test.go b/librecursebuster/main_test.go index 3df330b..9eb06d1 100644 --- a/librecursebuster/main_test.go +++ b/librecursebuster/main_test.go @@ -156,7 +156,6 @@ func TestBadHeaders(t *testing.T) { cfg := getDefaultConfig() cfg.BadHeader = ArrayStringFlag{} cfg.BadHeader.Set("X-Bad-Header: test123") - fmt.Println(cfg.BadHeader) urlSlice := preSetupTest(cfg, "2005", finished) gState.WordList = append(gState.WordList, "badheader") found := postSetupTest(urlSlice) @@ -170,6 +169,28 @@ func TestBadHeaders(t *testing.T) { } +func TestAjax(t *testing.T) { + //ensure that basic auth checks are found + finished := make(chan struct{}) + cfg := getDefaultConfig() + cfg.Ajax = true + urlSlice := preSetupTest(cfg, "2006", finished) + gState.WordList = append(gState.WordList, "ajaxonly") + gState.WordList = append(gState.WordList, "onlynoajax") + found := postSetupTest(urlSlice) + gState.Wait() + + fmt.Println(found) + if x, ok := found["/ajaxonly"]; !ok || !x { + panic("Failed ajax header check") + } + + if x, ok := found["/onlynoajax"]; ok || x { + panic("Failed ajax header check") + } + +} + func postSetupTest(urlSlice []string) (found map[string]bool) { //start up the management goroutines go ManageRequests() diff --git a/librecursebuster/structs.go b/librecursebuster/structs.go index d681765..8fb4822 100644 --- a/librecursebuster/structs.go +++ b/librecursebuster/structs.go @@ -249,6 +249,7 @@ type HostState struct { //Config represents the configuration supplied at runtime. Different to program State which can change, this is set once, and only queried during program operation. type Config struct { Agent string + Ajax bool AppendDir bool Auth string BadResponses string @@ -320,6 +321,11 @@ type SpiderPage struct { //SetupState will perform all the basic state setup functions (adding URL's to the blacklist etc) func SetupState(globalState *State) { SetState(globalState) + + if gState.Cfg.Ajax { + gState.Cfg.Headers = append(gState.Cfg.Headers, "X-Requested-With:XMLHttpRequest") + } + for _, x := range strings.Split(gState.Cfg.Extensions, ",") { gState.Extensions = append(gState.Extensions, x) } diff --git a/librecursebuster/testserver/main.go b/librecursebuster/testserver/main.go index 4970897..75c5332 100644 --- a/librecursebuster/testserver/main.go +++ b/librecursebuster/testserver/main.go @@ -109,6 +109,18 @@ func handler(w http.ResponseWriter, r *http.Request) { respCode = 500 case "/c/d": respCode = 666 + case "/ajaxonly": + if r.Header.Get("X-Requested-With") == "XMLHttpRequest" { + respCode = 200 + } else { + respCode = 404 + } + case "/onlynoajax": + if r.Header.Get("X-Requested-With") == "XMLHttpRequest" { + respCode = 404 + } else { + respCode = 200 + } default: respCode = 404 } diff --git a/main.go b/main.go index a83cb42..19db0fa 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,7 @@ import ( "github.com/fatih/color" ) -const version = "1.5.8" +const version = "1.5.9" func main() { if runtime.GOOS == "windows" { //lol goos @@ -35,6 +35,7 @@ func main() { globalState.TotalTested = &totesTested flag.BoolVar(&globalState.Cfg.ShowAll, "all", false, "Show, and write the result of all checks") flag.BoolVar(&globalState.Cfg.AppendDir, "appendslash", false, "Append a / to all directory bruteforce requests (like extension, but slash instead of .yourthing)") + flag.BoolVar(&globalState.Cfg.Ajax, "ajax", false, "Add the X-Requested-With: XMLHttpRequest header to all requests") 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.") flag.StringVar(&globalState.Cfg.BadResponses, "bad", "404", "Responses to consider 'bad' or 'not found'. Comma-separated. This works the opposite way of gobuster!") flag.Var(&globalState.Cfg.BadHeader, "badheader", "Check for presence of this header. If an exact match is found, the response is considered bad.Supply as key:value. Can specify multiple - eg '-badheader Location:cats -badheader X-ATT-DeviceId:XXXXX'")