├── README.md ├── go.mod └── recon.go /README.md: -------------------------------------------------------------------------------- 1 | # lazyRecon 2 | 3 | lazyRecon is an all in one Recon tool, Complete with subdomain enumeration, "Alive checks", and tech detection with more features planned in the future, including domain flyover screenshots, and subdomain takeover detection 4 | 5 | # TODO eventually................ 6 | Create custom subdomain enumeration instead of using subfinders library, will probably keep wappalyzergo for ease of use 7 | potentially create a cred harvester from js source code 8 | implement custom AEM recon 9 | implement graphql introspection 10 | ### REFACTOR 11 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module recon 2 | 3 | go 1.21.0 4 | 5 | require ( 6 | github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89 7 | github.com/chromedp/chromedp v0.9.2 8 | github.com/jpillora/go-tld v1.2.1 9 | github.com/projectdiscovery/subfinder/v2 v2.6.2 10 | github.com/projectdiscovery/wappalyzergo v0.0.108 11 | golang.org/x/time v0.4.0 12 | ) 13 | 14 | require ( 15 | aead.dev/minisign v0.2.0 // indirect 16 | github.com/Masterminds/semver/v3 v3.2.1 // indirect 17 | github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809 // indirect 18 | github.com/VividCortex/ewma v1.2.0 // indirect 19 | github.com/akrylysov/pogreb v0.10.1 // indirect 20 | github.com/alecthomas/chroma v0.10.0 // indirect 21 | github.com/andybalholm/brotli v1.0.4 // indirect 22 | github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect 23 | github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect 24 | github.com/aymerick/douceur v0.2.0 // indirect 25 | github.com/charmbracelet/glamour v0.6.0 // indirect 26 | github.com/cheggaaa/pb/v3 v3.1.4 // indirect 27 | github.com/chromedp/sysutil v1.0.0 // indirect 28 | github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect 29 | github.com/corpix/uarand v0.2.0 // indirect 30 | github.com/dimchansky/utfbom v1.1.1 // indirect 31 | github.com/dlclark/regexp2 v1.8.1 // indirect 32 | github.com/dsnet/compress v0.0.1 // indirect 33 | github.com/fatih/color v1.15.0 // indirect 34 | github.com/gaukas/godicttls v0.0.3 // indirect 35 | github.com/gobwas/httphead v0.1.0 // indirect 36 | github.com/gobwas/pool v0.2.1 // indirect 37 | github.com/gobwas/ws v1.2.1 // indirect 38 | github.com/golang/protobuf v1.5.3 // indirect 39 | github.com/golang/snappy v0.0.4 // indirect 40 | github.com/google/go-github/v30 v30.1.0 // indirect 41 | github.com/google/go-querystring v1.1.0 // indirect 42 | github.com/gorilla/css v1.0.0 // indirect 43 | github.com/hako/durafmt v0.0.0-20210316092057-3a2c319c1acd // indirect 44 | github.com/josharian/intern v1.0.0 // indirect 45 | github.com/json-iterator/go v1.1.12 // indirect 46 | github.com/klauspost/compress v1.15.15 // indirect 47 | github.com/lib/pq v1.10.9 // indirect 48 | github.com/logrusorgru/aurora v2.0.3+incompatible // indirect 49 | github.com/lucasb-eyer/go-colorful v1.2.0 // indirect 50 | github.com/mailru/easyjson v0.7.7 // indirect 51 | github.com/mattn/go-colorable v0.1.13 // indirect 52 | github.com/mattn/go-isatty v0.0.19 // indirect 53 | github.com/mattn/go-runewidth v0.0.14 // indirect 54 | github.com/mholt/archiver v3.1.1+incompatible // indirect 55 | github.com/microcosm-cc/bluemonday v1.0.25 // indirect 56 | github.com/miekg/dns v1.1.55 // indirect 57 | github.com/minio/selfupdate v0.6.0 // indirect 58 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 59 | github.com/modern-go/reflect2 v1.0.2 // indirect 60 | github.com/muesli/reflow v0.3.0 // indirect 61 | github.com/muesli/termenv v0.15.1 // indirect 62 | github.com/nwaples/rardecode v1.1.3 // indirect 63 | github.com/olekukonko/tablewriter v0.0.5 // indirect 64 | github.com/pierrec/lz4 v2.6.1+incompatible // indirect 65 | github.com/pkg/errors v0.9.1 // indirect 66 | github.com/projectdiscovery/blackrock v0.0.1 // indirect 67 | github.com/projectdiscovery/cdncheck v1.0.1 // indirect 68 | github.com/projectdiscovery/chaos-client v0.5.1 // indirect 69 | github.com/projectdiscovery/dnsx v1.1.4 // indirect 70 | github.com/projectdiscovery/fastdialer v0.0.35 // indirect 71 | github.com/projectdiscovery/goflags v0.1.16 // indirect 72 | github.com/projectdiscovery/gologger v1.1.11 // indirect 73 | github.com/projectdiscovery/hmap v0.0.13 // indirect 74 | github.com/projectdiscovery/networkpolicy v0.0.6 // indirect 75 | github.com/projectdiscovery/ratelimit v0.0.9 // indirect 76 | github.com/projectdiscovery/retryabledns v1.0.33 // indirect 77 | github.com/projectdiscovery/retryablehttp-go v1.0.21 // indirect 78 | github.com/projectdiscovery/utils v0.0.49 // indirect 79 | github.com/refraction-networking/utls v1.3.2 // indirect 80 | github.com/rivo/uniseg v0.4.4 // indirect 81 | github.com/rs/xid v1.5.0 // indirect 82 | github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect 83 | github.com/syndtr/goleveldb v1.0.0 // indirect 84 | github.com/tidwall/btree v1.4.3 // indirect 85 | github.com/tidwall/buntdb v1.3.0 // indirect 86 | github.com/tidwall/gjson v1.14.3 // indirect 87 | github.com/tidwall/grect v0.1.4 // indirect 88 | github.com/tidwall/match v1.1.1 // indirect 89 | github.com/tidwall/pretty v1.2.0 // indirect 90 | github.com/tidwall/rtred v0.1.2 // indirect 91 | github.com/tidwall/tinyqueue v0.1.1 // indirect 92 | github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect 93 | github.com/ulikunitz/xz v0.5.11 // indirect 94 | github.com/ulule/deepcopier v0.0.0-20200430083143-45decc6639b6 // indirect 95 | github.com/weppos/publicsuffix-go v0.30.1-0.20230422193905-8fecedd899db // indirect 96 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect 97 | github.com/yl2chen/cidranger v1.0.2 // indirect 98 | github.com/yuin/goldmark v1.5.4 // indirect 99 | github.com/yuin/goldmark-emoji v1.0.1 // indirect 100 | github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 // indirect 101 | github.com/zmap/zcrypto v0.0.0-20230422215203-9a665e1e9968 // indirect 102 | go.etcd.io/bbolt v1.3.7 // indirect 103 | go.uber.org/multierr v1.11.0 // indirect 104 | golang.org/x/crypto v0.12.0 // indirect 105 | golang.org/x/exp v0.0.0-20230420155640-133eef4313cb // indirect 106 | golang.org/x/mod v0.12.0 // indirect 107 | golang.org/x/net v0.14.0 // indirect 108 | golang.org/x/oauth2 v0.11.0 // indirect 109 | golang.org/x/sys v0.11.0 // indirect 110 | golang.org/x/text v0.12.0 // indirect 111 | golang.org/x/tools v0.11.0 // indirect 112 | google.golang.org/appengine v1.6.7 // indirect 113 | google.golang.org/protobuf v1.31.0 // indirect 114 | gopkg.in/djherbis/times.v1 v1.3.0 // indirect 115 | gopkg.in/yaml.v3 v3.0.1 // indirect 116 | ) 117 | -------------------------------------------------------------------------------- /recon.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // https://github.com/projectdiscovery/subfinder/blob/main/v2/examples/main.go 4 | // https://github.com/projectdiscovery/wappalyzergo 5 | // https://github.com/slotix/pageres-go-wrapper 6 | // https://github.com/chromedp/examples 7 | // https://github.com/projectdiscovery/subfinder/blob/dev/v2/pkg/runner/runner.go 8 | // scrape from https://bgp.he.net/ -> run prips -> run hakip2host 9 | // possibly write a bbot golang wrapper and use anew 10 | import ( 11 | "bufio" 12 | "bytes" 13 | "context" 14 | "fmt" 15 | "io" 16 | "log" 17 | "net/http" 18 | "os" 19 | "regexp" 20 | "strconv" 21 | "strings" 22 | "time" 23 | 24 | "github.com/chromedp/cdproto/page" 25 | "github.com/chromedp/chromedp" 26 | tld "github.com/jpillora/go-tld" 27 | "github.com/projectdiscovery/subfinder/v2/pkg/runner" 28 | wappalyzer "github.com/projectdiscovery/wappalyzergo" 29 | "golang.org/x/time/rate" 30 | ) 31 | 32 | // lazy as fuck implementation of http ratelimit, might just do my own http client at this point 33 | // https://gist.github.com/MelchiSalins/27c11566184116ec1629a0726e0f9af5 34 | var rl string 35 | var rlnum int 36 | 37 | // var tech string 38 | var stackarr []string 39 | var buf []byte 40 | 41 | type info struct { 42 | url string 43 | tech string 44 | } 45 | 46 | func addInfo(url string, tech string) *info { 47 | p := info{url: url, tech: tech} 48 | return &p 49 | } 50 | 51 | // ThrottledTransport Rate Limited HTTP Client 52 | type ThrottledTransport struct { 53 | roundTripperWrap http.RoundTripper 54 | ratelimiter *rate.Limiter 55 | } 56 | 57 | func (c *ThrottledTransport) RoundTrip(r *http.Request) (*http.Response, error) { 58 | err := c.ratelimiter.Wait(r.Context()) // This is a blocking call. Honors the rate limit 59 | if err != nil { 60 | return nil, err 61 | } 62 | return c.roundTripperWrap.RoundTrip(r) 63 | } 64 | 65 | func NewThrottledTransport(limitPeriod time.Duration, requestCount int, transportWrap http.RoundTripper) http.RoundTripper { 66 | return &ThrottledTransport{ 67 | roundTripperWrap: transportWrap, 68 | ratelimiter: rate.NewLimiter(rate.Every(limitPeriod), requestCount), 69 | } 70 | } 71 | 72 | // ^\*\. to match .* for wildcards 73 | func CheckError(e error, message string) { 74 | if e != nil { 75 | log.Print(message+" ", e) 76 | } 77 | } 78 | func fullScreenshot(urlstr string, quality int, res *[]byte) chromedp.Tasks { 79 | if rlnum != 0 { 80 | return chromedp.Tasks{ 81 | chromedp.Navigate(urlstr), 82 | chromedp.Sleep(time.Duration(rlnum) * time.Millisecond), 83 | chromedp.FullScreenshot(res, quality), 84 | } 85 | } else { 86 | return chromedp.Tasks{ 87 | chromedp.Navigate(urlstr), 88 | chromedp.Sleep(1000 * time.Millisecond), 89 | chromedp.FullScreenshot(res, quality), 90 | } 91 | } 92 | } 93 | 94 | func main() { 95 | var rltrans = NewThrottledTransport(time.Duration(rlnum)*time.Millisecond, 1, http.DefaultTransport) 96 | 97 | var netClient = &http.Client{ 98 | Transport: rltrans, 99 | } 100 | os.Mkdir("screenshots", 0o644) 101 | // ctx, cancel := chromedp.NewContext( 102 | // context.Background(), 103 | // // chromedp.WithDebugf(log.Printf), 104 | // ) 105 | // defer cancel() 106 | thing := 0 107 | log.SetFlags(0) 108 | // fmt.Println("arglen", len(os.Args)) 109 | if len(os.Args) == 1 { 110 | fmt.Println("Please supply a file") 111 | os.Exit(0) 112 | } 113 | if len(os.Args) == 3 { 114 | fmt.Println("ratelimit supplied") 115 | //fmt.Println(rl) 116 | rl = string(os.Args[2]) 117 | rlnum, _ = strconv.Atoi(rl) 118 | //fmt.Println(rl) 119 | } 120 | err := os.Truncate("alive.txt", 0) 121 | CheckError(err, "Can't Truncate alive.txt; likely does not exist yet") 122 | content, err := os.ReadFile(os.Args[1]) 123 | r, _ := regexp.Compile(`^\*\.`) 124 | fmt.Println(string(content)) 125 | CheckError(err, "file does not exist") 126 | f, err := os.Create("parsed.txt") 127 | CheckError(err, "parsing problem") 128 | defer f.Close() 129 | _, err = f.WriteString(r.ReplaceAllString(string(content), "")) 130 | CheckError(err, "problem with regex") 131 | 132 | subfinderOpts := &runner.Options{ 133 | Threads: 2, // Thread controls the number of threads to use for active enumerations 134 | Timeout: 3, // Timeout is the seconds to wait for sources to respond 135 | MaxEnumerationTime: 10, // MaxEnumerationTime is the maximum amount of time in mins to wait for enumeration 136 | 137 | // ProviderConfig: "your_provider_config.yaml", need to give options to create custom 138 | } 139 | 140 | opts := append(chromedp.DefaultExecAllocatorOptions[:], 141 | chromedp.Flag("ignore-certificate-errors", "1"), 142 | chromedp.Flag("headless", "1"), 143 | ) 144 | allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...) 145 | defer cancel() 146 | ctx, cancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf)) 147 | defer cancel() 148 | // disable timestamps in logs / configure logger 149 | 150 | subfinder, err := runner.NewRunner(subfinderOpts) 151 | CheckError(err, "Failed to create subfinder runner") 152 | 153 | output := &bytes.Buffer{} 154 | 155 | // To run subdomain enumeration on a list of domains from file/reader 156 | file, err := os.Open("parsed.txt") 157 | CheckError(err, "Can't open domain file") 158 | defer file.Close() 159 | err = subfinder.EnumerateMultipleDomainsWithCtx(context.Background(), file, []io.Writer{output}) 160 | CheckError(err, "Failed to Enumerate Subdomains from file") 161 | 162 | // print the output 163 | sd := output.String() 164 | log.Println(sd) 165 | f2, err := os.Create("subdomains.txt") 166 | CheckError(err, "Can't create subdomains file") 167 | defer f2.Close() 168 | _, err = f2.WriteString(sd) 169 | CheckError(err, "failed to write to subdomain file") 170 | 171 | readFile, err := os.Open("subdomains.txt") 172 | CheckError(err, "failed to read subdomain file") 173 | fileScanner := bufio.NewScanner(readFile) 174 | 175 | fileScanner.Split(bufio.ScanLines) 176 | 177 | for fileScanner.Scan() { 178 | text := fileScanner.Text() 179 | fmt.Println(text) 180 | // fmt.Println(netClient.Transport) 181 | resp, err := netClient.Get(fmt.Sprintf("http://%s", text)) 182 | if err != nil { 183 | log.Print("not alive, skipping") 184 | if strings.Contains(err.Error(), "no such host") { //todo fix error for certs 185 | log.Print(err) 186 | log.Print("not alive, skipping") 187 | } 188 | } else { 189 | data, err := io.ReadAll(resp.Body) // Ignoring error for example //this breaks 190 | CheckError(err, "failed to read body") 191 | //check for aws buckets 192 | 193 | //\s*.*<\/Key> literally worst regex in the world but it works kinda 194 | wappalyzerClient, _ := wappalyzer.New() 195 | fingerprints := wappalyzerClient.Fingerprint(resp.Header, data) 196 | for key := range fingerprints { 197 | 198 | stackarr = append(stackarr, key) 199 | // fmt.Printf(stackarr[thing]) 200 | fmt.Printf(key + " ") 201 | thing++ 202 | } 203 | chromedp.ListenTarget(ctx, func(ev interface{}) { 204 | if ev, ok := ev.(*page.EventJavascriptDialogOpening); ok { 205 | fmt.Println("closing alert:", ev.Message) 206 | go func() { 207 | chromedp.Run(ctx, 208 | page.HandleJavaScriptDialog(true), 209 | ) 210 | }() 211 | } 212 | }) 213 | chromedp.Run(ctx, fullScreenshot(fmt.Sprintf("http://%s", text), 90, &buf)) 214 | 215 | // err != nil{} 216 | new := strings.ReplaceAll(text, "www.", "") 217 | fmt.Println(new) 218 | parse, _ := tld.Parse("https://" + text) 219 | //check if screenshots folder exists, if so delete 220 | os.Mkdir("screenshots/"+parse.Domain, 0o644) 221 | fmt.Println(string(parse.Domain)) 222 | // os.Mkdir(fmt.Sprintf("screenshots/%s", strings.Split(text, ".")[1]), 0o644) 223 | if err := os.WriteFile( 224 | fmt.Sprintf("screenshots/%s/", parse.Domain)+strings.Join(strings.Split(new, "."), "_")+".png", buf, 0o644); err != nil { 225 | log.Fatal(err) 226 | } 227 | 228 | fmt.Printf("\n") 229 | f3, err := os.OpenFile("alive.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 230 | CheckError(err, "failed to create alive.txt") 231 | defer f3.Close() 232 | info := addInfo(text, strings.Join(stackarr, ", ")) 233 | fmt.Println(info.url, info.tech) 234 | _, err = f3.WriteString(text + "|" + strings.Join(stackarr, ", ") + "\n") 235 | CheckError(err, "failed to write to alive.txt") 236 | stackarr = stackarr[:0] 237 | thing = 0 238 | 239 | } 240 | } 241 | readFile.Close() 242 | } 243 | --------------------------------------------------------------------------------