├── README.md └── main.go /README.md: -------------------------------------------------------------------------------- 1 | # WARNING: DEPRECATED TOOL! 2 | Use https://github.com/sw33tLie/bbscope 3 | 4 | # bcscope 5 | Get the scope of your bugcrowd programs! 6 | 7 | ## Install 8 | ``` 9 | go get github.com/sw33tLie/bcscope 10 | ``` 11 | 12 | ## Example Command 13 | ``` 14 | bcscope -t -c 2 -p 15 | ``` 16 | This will print all the scope of all your Bugcrowd private programs. 17 | Remove the -p flag to get public programs too. 18 | Keep the concurrency low otherwise arcwhite may not be so happy :) 19 | 20 | ``` 21 | go run main.go -h 22 | 23 | Arguments: 24 | 25 | -h --help Print help information 26 | -t --token Bugcrowd session token (_crowdcontrol_session) 27 | -p --private Only show private invites. Default: false 28 | -l --list List programs instead of grabbing their scope. Default: 29 | false 30 | -b --bbp Only show programs offering monetary rewards. Default: 31 | false 32 | -c --concurrency Set concurrency. Default: 2 33 | -u --url Also print the program URL. Default: false 34 | 35 | ``` 36 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | "os" 9 | "strconv" 10 | "strings" 11 | "sync" 12 | 13 | "github.com/PuerkitoBio/goquery" 14 | "github.com/akamensky/argparse" 15 | "github.com/tidwall/gjson" 16 | ) 17 | 18 | func GetProgramPagePaths(sessionToken string, privatesOnly bool, bbpOnly bool) []string { 19 | allProgramsCount := 0 20 | currentProgramIndex := 0 21 | listEndpointURL := "https://bugcrowd.com/programs.json?" 22 | if privatesOnly { 23 | listEndpointURL = listEndpointURL + "accepted_invite[]=true&" 24 | } 25 | if bbpOnly { 26 | listEndpointURL = listEndpointURL + "points_only[]=false&" 27 | } 28 | listEndpointURL = listEndpointURL + "hidden[]=false&sort[]=invited-desc&sort[]=promoted-desc&offset[]=" 29 | paths := []string{} 30 | 31 | for { 32 | req, err := http.NewRequest("GET", listEndpointURL+strconv.Itoa(currentProgramIndex), nil) 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | 37 | req.Header.Set("Cookie", "_crowdcontrol_session="+sessionToken) 38 | req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0") 39 | 40 | client := &http.Client{} 41 | resp, err := client.Do(req) 42 | if err != nil { 43 | panic(err) 44 | } 45 | defer resp.Body.Close() 46 | 47 | body, _ := ioutil.ReadAll(resp.Body) 48 | 49 | if allProgramsCount == 0 { 50 | allProgramsCount = int(gjson.Get(string(body), "meta.totalHits").Int()) 51 | } 52 | 53 | chunkData := gjson.Get(string(body), "programs.#.program_url") 54 | for i := 0; i < len(chunkData.Array()); i++ { 55 | paths = append(paths, chunkData.Array()[i].Str) 56 | } 57 | currentProgramIndex += 25 58 | 59 | if allProgramsCount <= currentProgramIndex { 60 | break 61 | } 62 | } 63 | 64 | return paths 65 | } 66 | 67 | func PrintProgramScope(url string, sessionToken string, programURL bool) { 68 | req, err := http.NewRequest("GET", url, nil) 69 | if err != nil { 70 | log.Fatal(err) 71 | } 72 | 73 | req.Header.Set("Cookie", "_crowdcontrol_session="+sessionToken) 74 | req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0") 75 | client := &http.Client{} 76 | resp, err := client.Do(req) 77 | if err != nil { 78 | panic(err) 79 | } 80 | defer resp.Body.Close() 81 | 82 | body, _ := ioutil.ReadAll(resp.Body) 83 | 84 | // Yeah, HTML parsing is a pain @arcwhite do something damn it :D 85 | // Or at least, don't break this tool aka don't change HTML stuff <3 86 | 87 | var scope []string 88 | doc, err := goquery.NewDocumentFromReader(strings.NewReader(string(body))) 89 | if err != nil { 90 | fmt.Println("No url found") 91 | log.Fatal(err) 92 | } 93 | 94 | doc.Find("#user-guides__bounty-brief__targets-table").Each(func(index int, tablehtml *goquery.Selection) { 95 | tablehtml.Find("tr").Each(func(indextr int, rowhtml *goquery.Selection) { 96 | rowhtml.Find("tbody td").Each(func(indexth int, tablecell *goquery.Selection) { 97 | if indexth == 0 { 98 | if programURL { 99 | scope = append(scope, strings.TrimSpace(tablecell.Text())+" "+url) 100 | } else { 101 | scope = append(scope, strings.TrimSpace(tablecell.Text())) 102 | } 103 | } 104 | }) 105 | }) 106 | }) 107 | 108 | for _, s := range scope { 109 | fmt.Println(s) 110 | } 111 | } 112 | 113 | func main() { 114 | parser := argparse.NewParser("bcscope", "Get the scope of your Bugcrowd programs") 115 | 116 | sessionToken := parser.String("t", "token", &argparse.Options{Required: true, Help: "Bugcrowd session token (_crowdcontrol_session)"}) 117 | privateInvitesOnly := parser.Flag("p", "private", &argparse.Options{Required: false, Default: false, Help: "Only show private invites"}) 118 | listOnly := parser.Flag("l", "list", &argparse.Options{Required: false, Default: false, Help: "List programs instead of grabbing their scope"}) 119 | bbpOnly := parser.Flag("b", "bbp", &argparse.Options{Required: false, Default: false, Help: "Only show programs offering monetary rewards"}) 120 | concurrency := parser.Int("c", "concurrency", &argparse.Options{Required: false, Default: 2, Help: "Set concurrency"}) 121 | programURL := parser.Flag("u", "url", &argparse.Options{Required: false, Default: false, Help: "Also print the program URL"}) 122 | 123 | err := parser.Parse(os.Args) 124 | if err != nil { 125 | fmt.Print(parser.Usage(err)) 126 | os.Exit(1) 127 | } 128 | 129 | programPaths := GetProgramPagePaths(*sessionToken, *privateInvitesOnly, *bbpOnly) 130 | 131 | if *listOnly { 132 | for _, path := range programPaths { 133 | fmt.Println("https://bugcrowd.com" + path) 134 | } 135 | } else { 136 | 137 | urls := make(chan string, *concurrency) 138 | processGroup := new(sync.WaitGroup) 139 | processGroup.Add(*concurrency) 140 | 141 | for i := 0; i < *concurrency; i++ { 142 | go func() { 143 | for { 144 | url := <-urls 145 | 146 | if url == "" { 147 | break 148 | } 149 | 150 | PrintProgramScope(url, *sessionToken, *programURL) 151 | } 152 | processGroup.Done() 153 | }() 154 | } 155 | 156 | for _, path := range programPaths { 157 | urls <- "https://bugcrowd.com" + path 158 | } 159 | 160 | close(urls) 161 | processGroup.Wait() 162 | 163 | } 164 | } 165 | --------------------------------------------------------------------------------