diff --git a/.gitignore b/.gitignore index 3d72576..fd76dd5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store -.idea \ No newline at end of file +.idea +out* \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml index c65414a..9b3ce21 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -9,7 +9,7 @@ builds: - linux - windows - darwin - main: ./cmd/main.go + main: ./main.go archives: - replacements: darwin: Darwin @@ -18,11 +18,11 @@ archives: 386: i386 amd64: x86_64 format_overrides: - - goos: windows - format: zip + - goos: windows + format: zip files: - - data/* - - config/**/* + - data/* + - config/**/* checksum: name_template: 'checksums.txt' snapshot: diff --git a/config/config.yaml b/config/config.yaml index 46b9bcf..2bcd9ce 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 +providers: [ "amazon","alibaba","amazon","microsoft","digitalocean","linode","vultr","google" ] # supported providers +environments: [ "test", "dev", "prod", "stage" ] # 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 82e689d..d11f373 100644 --- a/config/modules/alibaba.yaml +++ b/config/modules/alibaba.yaml @@ -1,28 +1,28 @@ regions: - ["oss-cn-hangzhou", - "oss-cn-shanghai", - "oss-cn-qingdao", - "oss-cn-beijing", - "oss-cn-zhangjiakou", - "oss-cn-huhehaote", - "oss-cn-wulanchabu", - "oss-cn-shenzhen", - "oss-cn-heyuan", - "oss-cn-chengdu", - "oss-cn-hongkong", - "oss-ap-southeast-1", - "oss-ap-southeast-2", - "oss-ap-southeast-3", - "oss-ap-southeast-3", - "oss-ap-southeast-5", - "oss-ap-northeast-1", - "oss-ap-south-1", - "oss-eu-central-1", - "oss-eu-west-1", - "cn-north-1", - "oss-us-west-1", - "oss-us-east-1", - "oss-me-east-1"] -storage_urls: [] -app_urls: [] -region_urls: ["aliyuncs.com"] \ No newline at end of file + [ "oss-cn-hangzhou", + "oss-cn-shanghai", + "oss-cn-qingdao", + "oss-cn-beijing", + "oss-cn-zhangjiakou", + "oss-cn-huhehaote", + "oss-cn-wulanchabu", + "oss-cn-shenzhen", + "oss-cn-heyuan", + "oss-cn-chengdu", + "oss-cn-hongkong", + "oss-ap-southeast-1", + "oss-ap-southeast-2", + "oss-ap-southeast-3", + "oss-ap-southeast-3", + "oss-ap-southeast-5", + "oss-ap-northeast-1", + "oss-ap-south-1", + "oss-eu-central-1", + "oss-eu-west-1", + "cn-north-1", + "oss-us-west-1", + "oss-us-east-1", + "oss-me-east-1" ] +storage_urls: [ ] +app_urls: [ ] +region_urls: [ "aliyuncs.com" ] \ No newline at end of file diff --git a/config/modules/amazon.yaml b/config/modules/amazon.yaml index 8664412..4adbdce 100644 --- a/config/modules/amazon.yaml +++ b/config/modules/amazon.yaml @@ -1,29 +1,29 @@ regions: [ - "ap-northeast-1", - "ap-northeast-2", - "ap-northeast-3", - "ap-south-1", - "ap-southeast-1", - "ap-southeast-2", - "ap-northeast-3", - "ca-central-1", - "cn-north-1", - "cn-northwest-1", - "eu-central-1", - "eu-north-1", - "eu-west-1", - "eu-west-2", - "eu-west-3", - "sa-east-1", - "us-east-1", - "us-east-2", - "us-west-1", - "us-west-2", - "cn-north-1", - "cn-northwest-1", - "us-gov-east-1", - "us-gov-west-1", - ] -app_urls: ["awsapps.com"] -storage_urls: ["s3.amazonaws.com"] -region_urls: ["amazonaws.com"] \ No newline at end of file + "ap-northeast-1", + "ap-northeast-2", + "ap-northeast-3", + "ap-south-1", + "ap-southeast-1", + "ap-southeast-2", + "ap-northeast-3", + "ca-central-1", + "cn-north-1", + "cn-northwest-1", + "eu-central-1", + "eu-north-1", + "eu-west-1", + "eu-west-2", + "eu-west-3", + "sa-east-1", + "us-east-1", + "us-east-2", + "us-west-1", + "us-west-2", + "cn-north-1", + "cn-northwest-1", + "us-gov-east-1", + "us-gov-west-1", +] +app_urls: [ "awsapps.com" ] +storage_urls: [ "s3.amazonaws.com" ] +region_urls: [ "amazonaws.com" ] \ No newline at end of file diff --git a/config/modules/digitalocean.yaml b/config/modules/digitalocean.yaml index 4da8615..6b80f51 100644 --- a/config/modules/digitalocean.yaml +++ b/config/modules/digitalocean.yaml @@ -8,8 +8,8 @@ regions: [ "sfo1", "sgp1", "lon1", - ] +] -storage_urls: [] -app_urls: [] -region_urls: ["digitaloceanspaces.com"] \ No newline at end of file +storage_urls: [ ] +app_urls: [ ] +region_urls: [ "digitaloceanspaces.com" ] \ No newline at end of file diff --git a/config/modules/google.yaml b/config/modules/google.yaml index a344bb6..a1f2918 100644 --- a/config/modules/google.yaml +++ b/config/modules/google.yaml @@ -1,7 +1,7 @@ -app_urls: [ "appspot.com"] -storage_urls: ["storage.googleapis.com"] -regions: [] -region_urls: [] +app_urls: [ "appspot.com" ] +storage_urls: [ "storage.googleapis.com" ] +regions: [ ] +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 b20295d..b3e0be5 100644 --- a/config/modules/linode.yaml +++ b/config/modules/linode.yaml @@ -1,4 +1,4 @@ -regions: ["us-east-1" , "eu-central-1", "ap-south-1"] -storage_urls: [] -app_urls: [] -region_urls: ["linodeobjects.com"] \ No newline at end of file +regions: [ "us-east-1" , "eu-central-1", "ap-south-1" ] +storage_urls: [ ] +app_urls: [ ] +region_urls: [ "linodeobjects.com" ] \ No newline at end of file diff --git a/config/modules/microsoft.yaml b/config/modules/microsoft.yaml index cf21a53..c47d6e8 100644 --- a/config/modules/microsoft.yaml +++ b/config/modules/microsoft.yaml @@ -1,49 +1,49 @@ -regions: ['eastasia', - 'southeastasia', - 'centralus', - 'eastus', - 'eastus2', - 'westus', - 'northcentralus', - 'southcentralus', - 'northeurope', - 'westeurope', - 'japanwest', - 'japaneast', - 'brazilsouth', - 'australiaeast', - 'australiasoutheast', - 'southindia', - 'centralindia', - 'westindia', - 'canadacentral', - 'canadaeast', - 'uksouth', - 'ukwest', - 'westcentralus', - 'westus2', - 'koreacentral', - 'koreasouth', - 'francecentral', - 'francesouth', - 'australiacentral', - 'australiacentral2', - 'southafricanorth', - 'southafricawest'] +regions: [ 'eastasia', + 'southeastasia', + 'centralus', + 'eastus', + 'eastus2', + 'westus', + 'northcentralus', + 'southcentralus', + 'northeurope', + 'westeurope', + 'japanwest', + 'japaneast', + 'brazilsouth', + 'australiaeast', + 'australiasoutheast', + 'southindia', + 'centralindia', + 'westindia', + 'canadacentral', + 'canadaeast', + 'uksouth', + 'ukwest', + 'westcentralus', + 'westus2', + 'koreacentral', + 'koreasouth', + 'francecentral', + 'francesouth', + 'australiacentral', + 'australiacentral2', + 'southafricanorth', + 'southafricawest' ] storage_urls: - [ - "web.core.windows.net", - "file.core.windows.net", - "blob.core.windows.net", - "queue.core.windows.net" ] + [ + "web.core.windows.net", + "file.core.windows.net", + "blob.core.windows.net", + "queue.core.windows.net" ] app_urls: [ - "azurewebsites.net", - "cloudapp.net", - "p.azurewebsites.net"] + "azurewebsites.net", + "cloudapp.net", + "p.azurewebsites.net" ] -region_urls: ["cloudapp.azure.com"] \ No newline at end of file +region_urls: [ "cloudapp.azure.com" ] \ No newline at end of file diff --git a/config/modules/vultr.yaml b/config/modules/vultr.yaml index e5e9838..b5935d1 100644 --- a/config/modules/vultr.yaml +++ b/config/modules/vultr.yaml @@ -1,4 +1,4 @@ -regions: ["ewr1"] -storage_urls: [] -app_urls: [] -region_urls: ["vultrobjects.com"] \ No newline at end of file +regions: [ "ewr1" ] +storage_urls: [ ] +app_urls: [ ] +region_urls: [ "vultrobjects.com" ] \ No newline at end of file diff --git a/internal/brute.go b/internal/brute.go index bc7d909..494834f 100644 --- a/internal/brute.go +++ b/internal/brute.go @@ -8,30 +8,27 @@ import ( "golang.org/x/net/proxy" "net/http" "net/url" + "os" "strconv" "strings" "time" ) -func HandleHTTPRequests(reqs, results chan string, quit chan int, bar *pb.ProgressBar , details *RequestDetails) { - +func HandleHTTPRequests(reqs, results chan string, quit chan int, bar *pb.ProgressBar, details *RequestDetails) { for link := range reqs { - log.Debug().Msg(link) if len(details.ProxyList) > 0 { - chosenProxy := SelectRandomItem(details.ProxyList) - - if details.ProxyType == "socks5" { + chosenProxy := SelectRandomItem(details.ProxyList) + if details.ProxyType == "socks5" { log.Debug().Msg("requesting through socks5 proxy : " + chosenProxy) - dialSocksProxy, err := proxy.SOCKS5("tcp", chosenProxy, nil, proxy.Direct) - socksTransport := &http.Transport{Dial: dialSocksProxy.Dial, DisableKeepAlives: true , TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} + socksTransport := &http.Transport{Dial: dialSocksProxy.Dial, DisableKeepAlives: true, TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} if err != nil { continue @@ -41,12 +38,12 @@ func HandleHTTPRequests(reqs, results chan string, quit chan int, bar *pb.Progre Transport: socksTransport, } - req , _ := http.NewRequest("HEAD","https://" +link ,nil) + req, _ := http.NewRequest("HEAD", "https://"+link, nil) - if len(details.RandomAgent) >0 { + if len(details.RandomAgent) > 0 { chosenAgent := SelectRandomItem(details.RandomAgent) - req.Header.Set("User-Agent",chosenAgent) + req.Header.Set("User-Agent", chosenAgent) } @@ -65,29 +62,26 @@ func HandleHTTPRequests(reqs, results chan string, quit chan int, bar *pb.Progre } - if details.ProxyType == "http"{ - + if details.ProxyType == "http" { - proxyURL, _ := url.Parse("http://"+chosenProxy) + proxyURL, _ := url.Parse("http://" + chosenProxy) log.Debug().Msg("requesting through http proxy : " + chosenProxy) httpProxyClient := &http.Client{ Transport: &http.Transport{ DisableKeepAlives: true, - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, Proxy: http.ProxyURL(proxyURL), }, } + req, _ := http.NewRequest("HEAD", "http://"+link, nil) - - req , _ := http.NewRequest("HEAD","http://" +link ,nil) - - if len(details.RandomAgent) >1 { + if len(details.RandomAgent) > 1 { chosenAgent := SelectRandomItem(details.RandomAgent) - req.Header.Set("User-Agent",chosenAgent) + req.Header.Set("User-Agent", chosenAgent) log.Debug().Msg("user-agent : " + chosenAgent) } @@ -105,27 +99,25 @@ func HandleHTTPRequests(reqs, results chan string, quit chan int, bar *pb.Progre bar.Increment() results <- link + ":" + strconv.Itoa(resp.StatusCode) - } } else { client := http.Client{ Transport: &http.Transport{ - DisableKeepAlives: true }, - } + DisableKeepAlives: true}, + } - req , _ := http.NewRequest("HEAD","https://" +link ,nil) + req, _ := http.NewRequest("HEAD", "https://"+link, nil) - if len(details.RandomAgent) >0 { + if len(details.RandomAgent) > 0 { chosenAgent := SelectRandomItem(details.RandomAgent) - req.Header.Set("User-Agent",chosenAgent) + req.Header.Set("User-Agent", chosenAgent) } resp, err := client.Do(req) - if err != nil { results <- "err" @@ -139,18 +131,15 @@ func HandleHTTPRequests(reqs, results chan string, quit chan int, bar *pb.Progre results <- link + ":" + strconv.Itoa(resp.StatusCode) } - if len(reqs) == len(results) { quit <- 0 } - } - } -func AsyncHTTPHead(urls []string, threads int, timeout int , details RequestDetails , output string) { +func AsyncHTTPHead(urls []string, threads int, timeout int, details RequestDetails, output string) { result := make(chan string) reqs := make(chan string, len(urls)) // buffered @@ -159,7 +148,7 @@ func AsyncHTTPHead(urls []string, threads int, timeout int , details RequestDeta bar := pb.StartNew(len(urls)) for i := 0; i < threads; i++ { - go HandleHTTPRequests(reqs, result, quit , bar , &details) + go HandleHTTPRequests(reqs, result, quit, bar, &details) } go func() { @@ -168,8 +157,7 @@ func AsyncHTTPHead(urls []string, threads int, timeout int , details RequestDeta } }() - - var results []string + //var results []string // parsing http codes // 500 , 502 server error @@ -182,57 +170,61 @@ func AsyncHTTPHead(urls []string, threads int, timeout int , details RequestDeta select { case res := <-result: if res != "err" { + domain := res + var out, status string + if strings.Contains(res, ":") { + domain = strings.Split(res, ":")[0] + status = strings.Split(res, ":")[1] + } + if strings.Contains(res, "200") { - log.Info().Msg("Open : " + "[response code :" + res +"]") - results = append(results,"Open : " + "[response code :" + res +"]") + out = fmt.Sprintf("%s:%s - %s", "Open", status, domain) + log.Info().Msg("Open : " + "[response code :" + res + "]") } if strings.Contains(res, "301") || strings.Contains(res, "402") { - log.Warn().Msg("Redirect : " + "[response code :" + res +"]") - results = append(results,"Redirect : " + "[response code :" + res +"]") + out = fmt.Sprintf("%s:%s - %s", "Open", status, domain) + log.Warn().Msg("Redirect : " + "[response code :" + res + "]") } if strings.Contains(res, "400") || strings.Contains(res, "401") || strings.Contains(res, "403") { - log.Warn().Msg("Protected : " + "[response code :" + res +"]") - results = append(results,"Protected : " + "[response code :" + res +"]") + out = fmt.Sprintf("%s:%s - %s", "Open", status, domain) + log.Warn().Msg("Protected : " + "[response code :" + res + "]") } if strings.Contains(res, "500") || strings.Contains(res, "502") { - log.Warn().Msg("Server error :" + "[response code :" + res +"]") - results = append(results,"Server error :" + "[response code :" + res +"]") + out = fmt.Sprintf("%s:%s - %s", "Open", status, res) + log.Warn().Msg("Server error :" + "[response code :" + res + "]") + } + if out != "" { + AppendTo(output, out) } + } case <-time.After(time.Duration(timeout) * time.Second): - fmt.Println("timeout") + fmt.Fprintf(os.Stderr, "timeout") bar.Increment() case <-quit: bar.Set(len(urls)) bar.Finish() - if len(results) >0 { - - WriteResultsToFile(results , output) - } - - + //if len(results) >0 { + // WriteResultsToFile(results , output) + //} return } } - - - } -func GenerateMutatedUrls(wordListPath string, provider string, providerPath string, target string , environments []string) ([]string, error) { - +func GenerateMutatedUrls(wordListPath string, level int, provider string, providerPath string, target string, environments []string) ([]string, error) { //envs := []string{"test", "dev", "prod", "stage"} - words ,err := ReadTextFile(wordListPath) + words, err := ReadTextFile(wordListPath) - if err!=nil{ + if err != nil { log.Fatal().Err(err).Msg("Exiting ...") } permutations := []string{"%s-%s-%s", "%s-%s.%s", "%s-%s%s", "%s.%s-%s", "%s.%s.%s"} @@ -261,10 +253,9 @@ func GenerateMutatedUrls(wordListPath string, provider string, providerPath stri } - providerConfig, err := InitCloudConfig(provider ,providerPath ) + providerConfig, err := InitCloudConfig(provider, providerPath) if err != nil { - log.Fatal().Err(err).Msg("Exiting...") } @@ -272,40 +263,42 @@ func GenerateMutatedUrls(wordListPath string, provider string, providerPath stri var finalUrls []string - if len(providerConfig.Regions) > 0 { + // @NOTE start to decide level here 0,1,2 + if level >= 1 { + if len(providerConfig.Regions) > 0 { + for _, region := range providerConfig.Regions { - for _, region := range providerConfig.Regions { + for _, regionUrl := range providerConfig.RegionUrls { - for _, regionUrl := range providerConfig.RegionUrls { + for _, word := range compiled { - for _, word := range compiled { - - finalUrls = append ( finalUrls, word + "." + region + "." + regionUrl) + finalUrls = append(finalUrls, word+"."+region+"."+regionUrl) + } } } } } - if len(providerConfig.StorageUrls) > 0 { - for _, storage := range providerConfig.StorageUrls { - - for _, word := range compiled { - - finalUrls = append(finalUrls, word+"."+storage) + if level >= 2 { + if len(providerConfig.StorageUrls) > 0 { + for _, storage := range providerConfig.StorageUrls { + for _, word := range compiled { + finalUrls = append(finalUrls, word+"."+storage) + } } - } } - if len(providerConfig.APPUrls) > 0 { - for _, app := range providerConfig.APPUrls { + if level >= 3 { + if len(providerConfig.APPUrls) > 0 { + for _, app := range providerConfig.APPUrls { - for _, word := range compiled { + for _, word := range compiled { + finalUrls = append(finalUrls, word+"."+app) + } - finalUrls = append ( finalUrls, word + "." + app) } - } } diff --git a/internal/brute_test.go b/internal/brute_test.go index 3737a9a..24ed7ae 100644 --- a/internal/brute_test.go +++ b/internal/brute_test.go @@ -8,22 +8,19 @@ import ( func TestGenerateMutatedUrls(t *testing.T) { - envs := []string{"test", "dev", "prod", "stage"} - got , err := GenerateMutatedUrls("../data/storage_small.txt","amazon" , "../config/modules/", "target" , envs) - if err!=nil{ + got, err := GenerateMutatedUrls("../data/storage_small.txt", "amazon", "../config/modules/", "target", envs) + if err != nil { t.Errorf("Error generating urls %s", err) } var stringArr []string - if reflect.TypeOf(got) != reflect.TypeOf(stringArr){ + if reflect.TypeOf(got) != reflect.TypeOf(stringArr) { fmt.Println("Received wrong type") } - - -} \ No newline at end of file +} diff --git a/internal/cloud.go b/internal/cloud.go index 7e93129..5889e10 100644 --- a/internal/cloud.go +++ b/internal/cloud.go @@ -8,20 +8,19 @@ import ( "strings" ) -func CloudDetect(domain string , key string) (string , error) { +func CloudDetect(domain string, key string) (string, error) { authTransport := ipinfo.AuthTransport{Token: key} httpClient := authTransport.Client() - resp, err := net.LookupIP(domain) if err != nil { log.Error().Msg("Can't Connect to host") - return "",err + return "", err } - if len(resp) < 1{ + if len(resp) < 1 { log.Error().Msg("Can't resolve target IP") - return "",err + return "", err } firstIp := resp[0] @@ -31,42 +30,39 @@ func CloudDetect(domain string , key string) (string , error) { log.Error().Msg("Can't resolve target Organization") } - log.Debug().Msg( strings.TrimSpace( info)) + log.Debug().Msg(strings.TrimSpace(info)) info = strings.ToLower(info) - - - if strings.Contains(info,"amazon") { - return "amazon",nil + if strings.Contains(info, "amazon") { + return "amazon", nil } - if strings.Contains(info,"linode") { - return "linode",nil + if strings.Contains(info, "linode") { + return "linode", nil } - if strings.Contains(info,"digitalocean") { - return "digitalocean",nil + if strings.Contains(info, "digitalocean") { + return "digitalocean", nil } - if strings.Contains(info,"google") { - return "google",nil + if strings.Contains(info, "google") { + return "google", nil } - if strings.Contains(info,"microsoft") { - return "microsoft",nil + if strings.Contains(info, "microsoft") { + return "microsoft", nil } - if strings.Contains(info,"alibaba") { - return "alibaba",nil + if strings.Contains(info, "alibaba") { + return "alibaba", nil } - if strings.Contains(info,"choopa") { - return "vultr",nil + if strings.Contains(info, "choopa") { + return "vultr", nil } // CloudFlare detection if target is behind proxy it means we can't detect true provider - if strings.Contains(info,"cloudflare") { - return "",errors.New("CloudFlare detected target is behind proxy") + if strings.Contains(info, "cloudflare") { + return "", errors.New("CloudFlare detected target is behind proxy") } - return info,nil - + return info, nil // NO-API Idea //result, err := whois.Whois("0xsha.io") @@ -77,16 +73,16 @@ func CloudDetect(domain string , key string) (string , error) { // log.Fatal(err) // } - } +} -func CheckSupportedCloud(org string, c *Config) (string,error) { +func CheckSupportedCloud(org string, c *Config) (string, error) { - for _,company := range c.Providers{ + for _, company := range c.Providers { - if org==company { - return org,nil + if org == company { + return org, nil } } - return "",errors.New("unsupported cloud: ") + return "", errors.New("unsupported cloud: ") -} \ No newline at end of file +} diff --git a/internal/cloud_test.go b/internal/cloud_test.go index e69997d..cd1792d 100644 --- a/internal/cloud_test.go +++ b/internal/cloud_test.go @@ -7,17 +7,17 @@ import ( func TestCheckSupportedCloud(t *testing.T) { config := InitConfig("../config/config.yaml") - got , err := CheckSupportedCloud("amazon" ,config ) + got, err := CheckSupportedCloud("amazon", config) - if err!=nil{ + if err != nil { - t.Errorf("Err %s" , err) + t.Errorf("Err %s", err) } want := "amazon" - if got != want{ + if got != want { t.Errorf("Err") } -} \ No newline at end of file +} diff --git a/internal/config.go b/internal/config.go index f1ccaad..9a29b30 100644 --- a/internal/config.go +++ b/internal/config.go @@ -3,36 +3,32 @@ package internal import ( "gopkg.in/yaml.v3" "io/ioutil" + "path" "path/filepath" + "strings" ) - // Providers []string{"amazon","alibaba","amazon","microsoft","digitalocean","linode","vultr"} type Config struct { - - Author string `yaml:"author"` // - IPInfo string `yaml:"ipinfo"` // API KEY - ProxyType string `yaml:"proxytype"` - Providers []string `yaml:"providers"` - Environments []string `yaml:"Environments"` - - + Author string `yaml:"author"` // + IPInfo string `yaml:"ipinfo"` // API KEY + ProxyType string `yaml:"proxytype"` + Providers []string `yaml:"providers"` + Environments []string `yaml:"Environments"` } - - type RequestDetails struct { - ProxyList []string - ProxyType string + ProxyList []string + ProxyType string RandomAgent []string } type CloudConfig struct { - Regions []string `yaml:"regions"` - APPUrls []string `yaml:"app_urls"` + Regions []string `yaml:"regions"` + APPUrls []string `yaml:"app_urls"` StorageUrls []string `yaml:"storage_urls"` - RegionUrls []string `yaml:"region_urls"` + RegionUrls []string `yaml:"region_urls"` } func InitConfig(path string) *Config { @@ -42,30 +38,31 @@ func InitConfig(path string) *Config { filename, _ := filepath.Abs(path) configFile, err := ioutil.ReadFile(filename) - if err!=nil{ + if err != nil { panic(err) } err = yaml.Unmarshal(configFile, &config) - if err != nil{ + if err != nil { panic(err) } return &config } -func InitCloudConfig(cloud string , path string) (*CloudConfig, error) { - +func InitCloudConfig(cloud string, modulePath string) (*CloudConfig, error) { + if !strings.Contains(cloud,".yaml") { + cloud = cloud + ".yaml" + } var cloudConfig CloudConfig - filename, _ := filepath.Abs(path+cloud+".yaml") + filename, _ := filepath.Abs(path.Join(modulePath, cloud)) configFile, err := ioutil.ReadFile(filename) - if err!=nil{ - return nil,err + if err != nil { + return nil, err } err = yaml.Unmarshal(configFile, &cloudConfig) - if err != nil{ - return nil,err + if err != nil { + return nil, err } - return &cloudConfig,nil - + return &cloudConfig, nil } diff --git a/internal/config_test.go b/internal/config_test.go index 96d16a4..2acee53 100644 --- a/internal/config_test.go +++ b/internal/config_test.go @@ -5,7 +5,6 @@ import ( "testing" ) - func TestInitConfig(t *testing.T) { want := new(Config) @@ -22,13 +21,13 @@ func TestInitCloudConfig(t *testing.T) { config := InitConfig("../config/config.yaml") - for _, provider := range config.Providers{ + for _, provider := range config.Providers { - _ , err := InitCloudConfig(provider , "../config/modules/") + _, err := InitCloudConfig(provider, "../config/modules/") - if err != nil{ + if err != nil { t.Errorf("Cant load config for " + provider) } } -} \ No newline at end of file +} diff --git a/internal/utils.go b/internal/utils.go index aa2b6b2..ea364ca 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -9,14 +9,14 @@ import ( "time" ) -func ReadTextFile(path string) ([]string , error) { +func ReadTextFile(path string) ([]string, error) { - var buffer []string + var buffer []string - file , err := os.Open(path) - if err!=nil{ - log.Fatal().Err(err).Msg("Exiting ...") - } + file, err := os.Open(path) + if err != nil { + log.Fatal().Err(err).Msg("Exiting ...") + } defer file.Close() scanner := bufio.NewScanner(file) @@ -25,14 +25,30 @@ func ReadTextFile(path string) ([]string , error) { } if err := scanner.Err(); err != nil { - return nil,err + return nil, err } - return buffer , nil + return buffer, nil + +} +// AppendTo append string to a file +func AppendTo(filename string, data string) (string, error) { + // If the file doesn't exist, create it, or append to the file + f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + return "", err + } + if _, err := f.Write([]byte(data + "\n")); err != nil { + return "", err + } + if err := f.Close(); err != nil { + return "", err + } + return filename, nil } -func SelectRandomItem(agents []string) string { +func SelectRandomItem(agents []string) string { rand.Seed(time.Now().UnixNano()) @@ -43,8 +59,7 @@ func SelectRandomItem(agents []string) string { } -func WriteResultsToFile(results []string, output 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() @@ -61,7 +76,6 @@ func WriteResultsToFile(results []string, output string) { lineWriter.Flush() - } func Unique(input []string) []string { @@ -78,8 +92,7 @@ func Unique(input []string) []string { return list } - -func GenerateOutputName(output string) string { +func GenerateOutputName(output string) string { t := time.Now() result := fmt.Sprintf("%s-%d-%02d-%02dT%02d-%02d-%02d", @@ -87,4 +100,4 @@ func GenerateOutputName(output string) string { t.Hour(), t.Minute(), t.Second()) return result -} \ No newline at end of file +} diff --git a/cmd/main.go b/main.go similarity index 63% rename from cmd/main.go rename to main.go index 700ad12..8bfee83 100644 --- a/cmd/main.go +++ b/main.go @@ -1,4 +1,5 @@ package main + // an early version of cloud brute plugin for HunterSuite.io import ( "fmt" @@ -6,83 +7,84 @@ import ( "github.com/akamensky/argparse" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "io/ioutil" "os" + "path" ) - - - func main() { - - //banner - banner := ` ██████╗██╗ ██████╗ ██╗ ██╗██████╗ ██████╗ ██████╗ ██╗ ██╗████████╗███████╗ -██╔════╝██║ ██╔═══██╗██║ ██║██╔══██╗██╔══██╗██╔══██╗██║ ██║╚══██╔══╝██╔════╝ -██║ ██║ ██║ ██║██║ ██║██║ ██║██████╔╝██████╔╝██║ ██║ ██║ █████╗ -██║ ██║ ██║ ██║██║ ██║██║ ██║██╔══██╗██╔══██╗██║ ██║ ██║ ██╔══╝ -╚██████╗███████╗╚██████╔╝╚██████╔╝██████╔╝██████╔╝██║ ██║╚██████╔╝ ██║ ███████╗ - ╚═════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚══════╝ - V 1.0.0` - fmt.Println(banner) - - // beautify the results - log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) - zerolog.SetGlobalLevel(zerolog.InfoLevel) - parser := argparse.NewParser("CloudBrute", "Awesome Cloud Enumerator") domain := parser.String("d", "domain", &argparse.Options{ - Required: true, - Help: "domain"}) - + Required: true, + Help: "domain"}) keyword := parser.String("k", "keyword", &argparse.Options{ Required: true, - Help: "keyword used to generator urls"}) + Help: "keyword used to generator urls"}) wordList := parser.String("w", "wordlist", &argparse.Options{ Required: true, - Help: "path to wordlist"}) + Help: "path to wordlist"}) useProvider := parser.String("c", "cloud", &argparse.Options{ Required: false, - Help: "force a search, check config.yaml providers list"}) + Help: "force a search, check config.yaml providers list"}) threads := parser.Int("t", "threads", &argparse.Options{ Required: false, - Help: "number of threads", - Default: 80}) + Help: "number of threads", + Default: 80}) timeout := parser.Int("T", "timeout", &argparse.Options{ Required: false, - Help: "timeout per request in seconds", - Default: 10}) + Help: "timeout per request in seconds", + Default: 10}) useProxy := parser.String("p", "proxy", &argparse.Options{ Required: false, - Help: "use proxy list"}) - + Help: "use proxy list"}) useAgents := parser.String("a", "randomagent", &argparse.Options{ Required: false, - Help: "user agent randomization"}) - - + Help: "user agent randomization"}) debug := parser.Flag("D", "debug", &argparse.Options{ Required: false, - Help: "show debug logs", - Default: false}) + Help: "show debug logs", + Default: false}) + quite := parser.Flag("q", "quite", + &argparse.Options{ + Required: false, + Help: "suppress all output", + Default: false}) + level := parser.Int("L", "level", + &argparse.Options{ + Default: 1, + Required: false, + Help: "Set depth level for speed up the process"}) + output := parser.String("o", "output", + &argparse.Options{ + Default: "out.txt", + Required: false, + Help: "Output file"}) + + configFolder := parser.String("C", "configFolder", + &argparse.Options{ + Default: "config", + Required: false, + Help: "Config path"}) err := parser.Parse(os.Args) if err != nil { @@ -90,38 +92,57 @@ func main() { return } - //initialize config.yaml - config := engine.InitConfig("./config/config.yaml") + //banner + banner := ` ██████╗██╗ ██████╗ ██╗ ██╗██████╗ ██████╗ ██████╗ ██╗ ██╗████████╗███████╗ +██╔════╝██║ ██╔═══██╗██║ ██║██╔══██╗██╔══██╗██╔══██╗██║ ██║╚══██╔══╝██╔════╝ +██║ ██║ ██║ ██║██║ ██║██║ ██║██████╔╝██████╔╝██║ ██║ ██║ █████╗ +██║ ██║ ██║ ██║██║ ██║██║ ██║██╔══██╗██╔══██╗██║ ██║ ██║ ██╔══╝ +╚██████╗███████╗╚██████╔╝╚██████╔╝██████╔╝██████╔╝██║ ██║╚██████╔╝ ██║ ███████╗ + ╚═════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚══════╝ + V 1.0.0 +` + if !*quite { + fmt.Fprintf(os.Stderr, banner) + } + + // beautify the results + if !*quite { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + zerolog.SetGlobalLevel(zerolog.InfoLevel) + } else { + log.Logger = log.Output(zerolog.ConsoleWriter{Out: ioutil.Discard}) + zerolog.SetGlobalLevel(zerolog.PanicLevel) + } + + // initialize config.yaml + configPath := path.Join(*configFolder, "config.yaml") + providerPath := path.Join(*configFolder, "modules") + log.Info().Msg(fmt.Sprintf("Detect config path: %s", configPath)) + log.Info().Msg(fmt.Sprintf("Detect provider path: %s", providerPath)) + config := engine.InitConfig(configPath) apiKey := config.IPInfo log.Info().Msg("Initialized scan config") - if *debug { zerolog.SetGlobalLevel(zerolog.DebugLevel) } - - // check out args var details engine.RequestDetails if *useAgents != "" { - userAgents , err := engine.ReadTextFile(*useAgents) - - if err!=nil{ - + userAgents, err := engine.ReadTextFile(*useAgents) + if err != nil { log.Fatal().Err(err).Msg("Can't read agents file, check config.yaml") } - details.RandomAgent = userAgents } if *useProxy != "" { - proxyList , err := engine.ReadTextFile(*useProxy) + proxyList, err := engine.ReadTextFile(*useProxy) - - if err!=nil{ + if err != nil { log.Fatal().Err(err).Msg("Can't read proxy file , check config.yaml") } @@ -130,14 +151,12 @@ func main() { details.ProxyType = config.ProxyType } - - var cloud string if *useProvider != "" { cloud = *useProvider - }else { + } else { // Detect the cloud cloud, err = engine.CloudDetect(*domain, apiKey) @@ -155,25 +174,13 @@ func main() { log.Info().Msg(provider + " detected") - - - - - - urls, err := engine.GenerateMutatedUrls(*wordList, provider, "./config/modules/", *keyword , config.Environments) - //var p cb.Progress - //p.CurrentProgress = 0 - //p.TotalProgress = float64(len(urls)) + urls, err := engine.GenerateMutatedUrls(*wordList, *level, provider, providerPath, *keyword, config.Environments) if err != nil { log.Fatal().Err(err).Msg("Exiting...") } - - output := engine.GenerateOutputName(*keyword) - - engine.AsyncHTTPHead(urls, *threads, *timeout , details , output) - - + //output := engine.GenerateOutputName(*keyword) + engine.AsyncHTTPHead(urls, *threads, *timeout, details, *output) }