diff --git a/README.md b/README.md index 8ef5ccc..4819b01 100644 --- a/README.md +++ b/README.md @@ -95,43 +95,47 @@ After setting up your API key, you are ready to use CloudBrute. ██║ ██║ ██║ ██║██║ ██║██║ ██║██╔══██╗██╔══██╗██║ ██║ ██║ ██╔══╝ ╚██████╗███████╗╚██████╔╝╚██████╔╝██████╔╝██████╔╝██║ ██║╚██████╔╝ ██║ ███████╗ ╚═════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚══════╝ - V 1.0.0 -[-d|--domain] is required + V 1.0.3 usage: CloudBrute [-h|--help] -d|--domain "" -k|--keyword "" -w|--wordlist "" [-c|--cloud ""] [-t|--threads ] [-T|--timeout ] [-p|--proxy ""] - [-a|--randomagent ""] [-D|--debug] + [-a|--randomagent ""] [-D|--debug] [-q|--quite] + [-m|--mode ""] [-o|--output ""] + [-C|--configFolder ""] Awesome Cloud Enumerator Arguments: - -h --help Print help information - -d --domain domain - -k --keyword keyword used to generator urls - -w --wordlist path to wordlist - -c --cloud force a search, check config.yaml providers list - -t --threads number of threads. Default: 80 - -T --timeout timeout per request in seconds. Default: 10 - -p --proxy use proxy list - -a --randomagent user agent randomization - -D --debug show debug logs. Default: false + -h --help Print help information + -d --domain domain + -k --keyword keyword used to generator urls + -w --wordlist path to wordlist + -c --cloud force a search, check config.yaml providers list + -t --threads number of threads. Default: 80 + -T --timeout timeout per request in seconds. Default: 10 + -p --proxy use proxy list + -a --randomagent user agent randomization + -D --debug show debug logs. Default: false + -q --quite suppress all output. Default: false + -m --mode storage or app. Default: 1 + -o --output Output file. Default: out.txt + -C --configFolder Config path. Default: config ``` for example ``` -CloudBrute -d target.com -k target -t 80 -T 10 -w "./data/storage_small.txt" +CloudBrute -d target.com -k target -m storage -t 80 -T 10 -w "./data/storage_small.txt" ``` -please note -k keyword is used to generate URLs, so if you want the full domain to be part of mutation, you have used it for both domain (-d) and keyword (-k) arguments +please note -k keyword used to generate URLs, so if you want the full domain to be part of mutation, you have used it for both domain (-d) and keyword (-k) arguments -If a cloud provider is not detected or want force searching on a specific provider, you can use -c option. +If a cloud provider not detected or want force searching on a specific provider, you can use -c option. ``` -CloudBrute -d target.com -k keyword -t 80 -T 10 -w -c amazon +CloudBrute -d target.com -k keyword -m storage -t 80 -T 10 -w -c amazon -o target_output.txt ``` -After execution CloudBrute will write to the same directory with following format. -```target-2020-09-09T17-20-18.txt``` + ## in action diff --git a/config/config.yaml b/config/config.yaml index 2bcd9ce..5dfd3d3 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -1,4 +1,4 @@ providers: [ "amazon","alibaba","amazon","microsoft","digitalocean","linode","vultr","google" ] # supported providers -environments: [ "test", "dev", "prod", "stage" ] # used for mutations +environments: [ "test", "dev", "prod", "stage" , "staging" , "bak" ] # used for mutations proxytype: "http" # socks5 / http ipinfo: "" # IPINFO.io API KEY diff --git a/config/modules/alibaba.yaml b/config/modules/alibaba.yaml index d11f373..f69a39f 100644 --- a/config/modules/alibaba.yaml +++ b/config/modules/alibaba.yaml @@ -25,4 +25,5 @@ regions: "oss-me-east-1" ] storage_urls: [ ] app_urls: [ ] -region_urls: [ "aliyuncs.com" ] \ No newline at end of file +storage_region_urls: [ "aliyuncs.com" ] +app_region_urls: [ ] \ No newline at end of file diff --git a/config/modules/amazon.yaml b/config/modules/amazon.yaml index 4adbdce..1816c1d 100644 --- a/config/modules/amazon.yaml +++ b/config/modules/amazon.yaml @@ -26,4 +26,5 @@ regions: [ ] app_urls: [ "awsapps.com" ] storage_urls: [ "s3.amazonaws.com" ] -region_urls: [ "amazonaws.com" ] \ No newline at end of file +app_region_urls: [ "amazonaws.com" ] +storage_region_urls: [] \ No newline at end of file diff --git a/config/modules/digitalocean.yaml b/config/modules/digitalocean.yaml index 6b80f51..4463bd9 100644 --- a/config/modules/digitalocean.yaml +++ b/config/modules/digitalocean.yaml @@ -12,4 +12,5 @@ regions: [ storage_urls: [ ] app_urls: [ ] -region_urls: [ "digitaloceanspaces.com" ] \ No newline at end of file +storage_region_urls: [ "digitaloceanspaces.com" ] +app_region_urls: [] \ No newline at end of file diff --git a/config/modules/google.yaml b/config/modules/google.yaml index a1f2918..173ad1c 100644 --- a/config/modules/google.yaml +++ b/config/modules/google.yaml @@ -2,6 +2,8 @@ app_urls: [ "appspot.com" ] storage_urls: [ "storage.googleapis.com" ] regions: [ ] region_urls: [ ] +storage_region_urls: [] +app_region_urls: [] # we don't need to brute these as we can always use main urls . # asia-east2 diff --git a/config/modules/linode.yaml b/config/modules/linode.yaml index b3e0be5..fd6ea6d 100644 --- a/config/modules/linode.yaml +++ b/config/modules/linode.yaml @@ -1,4 +1,5 @@ regions: [ "us-east-1" , "eu-central-1", "ap-south-1" ] storage_urls: [ ] app_urls: [ ] -region_urls: [ "linodeobjects.com" ] \ No newline at end of file +storage_region_urls: [ "linodeobjects.com" ] +app_region_urls: [] \ No newline at end of file diff --git a/config/modules/microsoft.yaml b/config/modules/microsoft.yaml index c47d6e8..6256d18 100644 --- a/config/modules/microsoft.yaml +++ b/config/modules/microsoft.yaml @@ -46,4 +46,5 @@ app_urls: "cloudapp.net", "p.azurewebsites.net" ] -region_urls: [ "cloudapp.azure.com" ] \ No newline at end of file +app_region_urls: [ "cloudapp.azure.com" ] +storage_region_urls: [ ] \ No newline at end of file diff --git a/config/modules/vultr.yaml b/config/modules/vultr.yaml index b5935d1..7890fdf 100644 --- a/config/modules/vultr.yaml +++ b/config/modules/vultr.yaml @@ -1,4 +1,5 @@ regions: [ "ewr1" ] storage_urls: [ ] app_urls: [ ] -region_urls: [ "vultrobjects.com" ] \ No newline at end of file +storage_region_urls: [ "vultrobjects.com" ] +app_region_url: [ ] \ No newline at end of file diff --git a/internal/brute.go b/internal/brute.go index 494834f..cc2e69e 100644 --- a/internal/brute.go +++ b/internal/brute.go @@ -2,13 +2,13 @@ package internal import ( "crypto/tls" + "errors" "fmt" "github.com/cheggaaa/pb" "github.com/rs/zerolog/log" "golang.org/x/net/proxy" "net/http" "net/url" - "os" "strconv" "strings" "time" @@ -178,33 +178,33 @@ func AsyncHTTPHead(urls []string, threads int, timeout int, details RequestDetai } if strings.Contains(res, "200") { - out = fmt.Sprintf("%s:%s - %s", "Open", status, domain) - log.Info().Msg("Open : " + "[response code :" + res + "]") + out = fmt.Sprintf("%s:%s - %s", status,"Open", domain) + log.Info().Msg(out) } if strings.Contains(res, "301") || strings.Contains(res, "402") { - out = fmt.Sprintf("%s:%s - %s", "Open", status, domain) - log.Warn().Msg("Redirect : " + "[response code :" + res + "]") + out = fmt.Sprintf("%s:%s - %s", status,"Redirect", domain) + log.Warn().Msg(out) } if strings.Contains(res, "400") || strings.Contains(res, "401") || strings.Contains(res, "403") { - out = fmt.Sprintf("%s:%s - %s", "Open", status, domain) - log.Warn().Msg("Protected : " + "[response code :" + res + "]") + out = fmt.Sprintf("%s:%s - %s", status , "Protected" , domain) + log.Warn().Msg(out) } if strings.Contains(res, "500") || strings.Contains(res, "502") { - out = fmt.Sprintf("%s:%s - %s", "Open", status, res) - log.Warn().Msg("Server error :" + "[response code :" + res + "]") + out = fmt.Sprintf("%s:%s - %s", status,"Server Error", res) + log.Warn().Msg(out) } if out != "" { - AppendTo(output, out) + _, _ = AppendTo(output, out) } } case <-time.After(time.Duration(timeout) * time.Second): - fmt.Fprintf(os.Stderr, "timeout") + log.Warn().Msg("TimeOut") bar.Increment() case <-quit: bar.Set(len(urls)) @@ -219,7 +219,7 @@ func AsyncHTTPHead(urls []string, threads int, timeout int, details RequestDetai } -func GenerateMutatedUrls(wordListPath string, level int, provider string, providerPath string, target string, environments []string) ([]string, error) { +func GenerateMutatedUrls(wordListPath string, mode string, provider string, providerPath string, target string, environments []string) ([]string, error) { //envs := []string{"test", "dev", "prod", "stage"} words, err := ReadTextFile(wordListPath) @@ -231,6 +231,7 @@ func GenerateMutatedUrls(wordListPath string, level int, provider string, provid var compiled []string + for _, env := range environments { for _, word := range words { @@ -263,45 +264,75 @@ func GenerateMutatedUrls(wordListPath string, level int, provider string, provid var finalUrls []string - // @NOTE start to decide level here 0,1,2 - if level >= 1 { - if len(providerConfig.Regions) > 0 { - for _, region := range providerConfig.Regions { - for _, regionUrl := range providerConfig.RegionUrls { + if mode == "storage"{ - for _, word := range compiled { - - finalUrls = append(finalUrls, word+"."+region+"."+regionUrl) - } - } - } + if len(providerConfig.StorageUrls) < 1 && len(providerConfig.StorageRegionUrls) < 1 { + return nil,errors.New("storage are not supported on :" + provider ) } - } - if level >= 2 { if len(providerConfig.StorageUrls) > 0 { - for _, storage := range providerConfig.StorageUrls { + + for _, app := range providerConfig.StorageUrls { for _, word := range compiled { - finalUrls = append(finalUrls, word+"."+storage) + finalUrls = append(finalUrls, word+"."+app) } } } + + + if len(providerConfig.StorageRegionUrls) > 0 { + + for _, region := range providerConfig.Regions { + + for _, regionUrl := range providerConfig.StorageRegionUrls { + + for _, word := range compiled { + + finalUrls = append(finalUrls, word+"."+region+"."+regionUrl) + } + } + } + + } + } - if level >= 3 { + if mode == "app"{ + + if len(providerConfig.APPUrls) < 1 && len(providerConfig.AppRegionUrls) < 1 { + return nil,errors.New("storage are not supported on :" + provider ) + } + + if len(providerConfig.APPUrls) > 0 { for _, app := range providerConfig.APPUrls { for _, word := range compiled { finalUrls = append(finalUrls, word+"."+app) } + } + } + + if len(providerConfig.AppRegionUrls) > 0 { + + for _, region := range providerConfig.Regions { + + for _, regionUrl := range providerConfig.AppRegionUrls { + for _, word := range compiled { + + finalUrls = append(finalUrls, word+"."+region+"."+regionUrl) + } + } } + } + } + return finalUrls, nil } diff --git a/internal/config.go b/internal/config.go index 9a29b30..6286063 100644 --- a/internal/config.go +++ b/internal/config.go @@ -29,6 +29,8 @@ type CloudConfig struct { APPUrls []string `yaml:"app_urls"` StorageUrls []string `yaml:"storage_urls"` RegionUrls []string `yaml:"region_urls"` + AppRegionUrls []string `yaml:"app_region_urls"` + StorageRegionUrls []string `yaml:"storage_region_urls"` } func InitConfig(path string) *Config { diff --git a/internal/utils.go b/internal/utils.go index ea364ca..528d1df 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -2,7 +2,6 @@ package internal import ( "bufio" - "fmt" "github.com/rs/zerolog/log" "math/rand" "os" @@ -59,45 +58,46 @@ func SelectRandomItem(agents []string) string { } -func WriteResultsToFile(results []string, output string) { - - file, err := os.OpenFile(output+".txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - defer file.Close() - - if err != nil { - log.Fatal().Err(err).Msg("failed creating file") - } - - lineWriter := bufio.NewWriter(file) - - for _, result := range results { - _, _ = lineWriter.WriteString(result + "\n") - } - - lineWriter.Flush() - -} - -func Unique(input []string) []string { - unique := make(map[string]bool, len(input)) - list := make([]string, len(unique)) - for _, el := range input { - if len(el) != 0 { - if !unique[el] { - list = append(list, el) - unique[el] = true - } - } - } - return list -} - -func GenerateOutputName(output string) string { - - t := time.Now() - result := fmt.Sprintf("%s-%d-%02d-%02dT%02d-%02d-%02d", - output, t.Year(), t.Month(), t.Day(), - t.Hour(), t.Minute(), t.Second()) - - return result -} +//func WriteResultsToFile(results []string, output string) { +// +// file, err := os.OpenFile(output+".txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) +// defer file.Close() +// +// if err != nil { +// log.Fatal().Err(err).Msg("failed creating file") +// } +// +// lineWriter := bufio.NewWriter(file) +// +// for _, result := range results { +// _, _ = lineWriter.WriteString(result + "\n") +// } +// +// lineWriter.Flush() +// +//} + +//func GenerateOutputName(output string) string { +// +// t := time.Now() +// result := fmt.Sprintf("%s-%d-%02d-%02dT%02d-%02d-%02d", +// output, t.Year(), t.Month(), t.Day(), +// t.Hour(), t.Minute(), t.Second()) +// +// return result +//} + +//func Unique(input []string) []string { +// unique := make(map[string]bool, len(input)) +// list := make([]string, len(unique)) +// for _, el := range input { +// if len(el) != 0 { +// if !unique[el] { +// list = append(list, el) +// unique[el] = true +// } +// } +// } +// return list +//} +// diff --git a/main.go b/main.go index 8bfee83..d5edd47 100644 --- a/main.go +++ b/main.go @@ -13,8 +13,10 @@ import ( ) func main() { - parser := argparse.NewParser("CloudBrute", "Awesome Cloud Enumerator") + + // parse arguments + parser := argparse.NewParser("CloudBrute", "Awesome Cloud Enumerator") domain := parser.String("d", "domain", &argparse.Options{ Required: true, @@ -68,11 +70,11 @@ func main() { Help: "suppress all output", Default: false}) - level := parser.Int("L", "level", + mode := parser.String("m", "mode", &argparse.Options{ Default: 1, Required: false, - Help: "Set depth level for speed up the process"}) + Help: "storage or app"}) output := parser.String("o", "output", &argparse.Options{ @@ -99,10 +101,10 @@ func main() { ██║ ██║ ██║ ██║██║ ██║██║ ██║██╔══██╗██╔══██╗██║ ██║ ██║ ██╔══╝ ╚██████╗███████╗╚██████╔╝╚██████╔╝██████╔╝██████╔╝██║ ██║╚██████╔╝ ██║ ███████╗ ╚═════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚══════╝ - V 1.0.0 + V 1.0.3 ` if !*quite { - fmt.Fprintf(os.Stderr, banner) + _, _ = fmt.Fprintf(os.Stderr, banner) } // beautify the results @@ -174,7 +176,7 @@ func main() { log.Info().Msg(provider + " detected") - urls, err := engine.GenerateMutatedUrls(*wordList, *level, provider, providerPath, *keyword, config.Environments) + urls, err := engine.GenerateMutatedUrls(*wordList, *mode, provider, providerPath, *keyword, config.Environments) if err != nil { log.Fatal().Err(err).Msg("Exiting...")