|
| 1 | +package main |
| 2 | + |
| 3 | +import ( |
| 4 | + "bufio" |
| 5 | + "crypto/tls" |
| 6 | + "errors" |
| 7 | + "flag" |
| 8 | + "fmt" |
| 9 | + "io/ioutil" |
| 10 | + "net" |
| 11 | + "net/http" |
| 12 | + "net/url" |
| 13 | + "os" |
| 14 | + "regexp" |
| 15 | + "strconv" |
| 16 | + "strings" |
| 17 | + "time" |
| 18 | + |
| 19 | + "github.com/fatih/color" |
| 20 | + log "github.com/projectdiscovery/gologger" |
| 21 | +) |
| 22 | + |
| 23 | +func Err(e error) { |
| 24 | + if e != nil { |
| 25 | + log.Errorf("%s\n", e) |
| 26 | + os.Exit(1) |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +type Options struct { |
| 31 | + Query, Engine, Proxy string |
| 32 | + Page int |
| 33 | + Headers []string |
| 34 | +} |
| 35 | + |
| 36 | +type Headers []string |
| 37 | + |
| 38 | +const banner = ` |
| 39 | + __ __ |
| 40 | + ___ ____ ______/ /__ ____/ /__ |
| 41 | + / _ '/ _ \/__/ _ / _ \/ __/ '_/ |
| 42 | + \_, /\___/ \_,_/\___/_/ /_/\_\ |
| 43 | +/___/ |
| 44 | + v0.0.1 - @dwisiswant0 |
| 45 | +` |
| 46 | + |
| 47 | +var query, engine, proxy string |
| 48 | +var headers Headers |
| 49 | +var page int |
| 50 | +var noColor, silent bool |
| 51 | + |
| 52 | +func (h Headers) String() string { |
| 53 | + return strings.Join(h, ", ") |
| 54 | +} |
| 55 | + |
| 56 | +func (h *Headers) Set(val string) error { |
| 57 | + *h = append(*h, val) |
| 58 | + return nil |
| 59 | +} |
| 60 | + |
| 61 | +func init() { |
| 62 | + flag.StringVar(&query, "q", "", "") |
| 63 | + flag.StringVar(&query, "query", "", "") |
| 64 | + |
| 65 | + flag.StringVar(&engine, "e", "", "") |
| 66 | + flag.StringVar(&engine, "engine", "google", "") |
| 67 | + |
| 68 | + flag.IntVar(&page, "p", 1, "") |
| 69 | + flag.IntVar(&page, "page", 1, "") |
| 70 | + |
| 71 | + flag.Var(&headers, "header", "") |
| 72 | + flag.Var(&headers, "H", "") |
| 73 | + |
| 74 | + flag.StringVar(&proxy, "x", "", "") |
| 75 | + flag.StringVar(&proxy, "proxy", "", "") |
| 76 | + |
| 77 | + flag.BoolVar(&noColor, "nc", false, "") |
| 78 | + flag.BoolVar(&noColor, "no-color", false, "") |
| 79 | + |
| 80 | + flag.BoolVar(&silent, "s", false, "") |
| 81 | + flag.BoolVar(&silent, "silent", false, "") |
| 82 | + |
| 83 | + flag.Usage = func() { |
| 84 | + c := color.New(color.FgCyan, color.Bold).FprintfFunc() |
| 85 | + h := []string{ |
| 86 | + banner, |
| 87 | + "Options:", |
| 88 | + " -q, --query <query> Search query", |
| 89 | + " -e, --engine <engine> Provide search engine (default: Google)", |
| 90 | + " (options: Google, Shodan, Bing, Duck, Yahoo, Ask)", |
| 91 | + " -p, --page <i> Specify number of pages (default: 1)", |
| 92 | + " -H, --header <header> Pass custom header to search engine", |
| 93 | + " -x, --proxy <proxy_url> Use proxy to surfing", |
| 94 | + " -s, --silent Silent mode", |
| 95 | + " -nc, --no-color Disable colored output results", |
| 96 | + "", |
| 97 | + } |
| 98 | + |
| 99 | + c(os.Stderr, strings.Join(h, "\n")) |
| 100 | + } |
| 101 | +} |
| 102 | + |
| 103 | +func main() { |
| 104 | + flag.Parse() |
| 105 | + |
| 106 | + engine = strings.ToLower(engine) |
| 107 | + |
| 108 | + if !silent { |
| 109 | + c := color.New(color.FgCyan, color.Bold) |
| 110 | + c.Println(banner) |
| 111 | + log.Labelf("Use at your own risk! Developers assume no responsibility") |
| 112 | + log.Labelf("If your IP address has been blocked by search engine providers or other reason.") |
| 113 | + log.Infof("Query : %s", query) |
| 114 | + log.Infof("Page : %s", strconv.Itoa(page)) |
| 115 | + if proxy != "" { |
| 116 | + log.Infof("Proxy : %s", proxy) |
| 117 | + } |
| 118 | + if len(headers) > 0 { |
| 119 | + for _, h := range headers { |
| 120 | + log.Infof("Header: %s", h) |
| 121 | + } |
| 122 | + } |
| 123 | + log.Infof("Engine: %s\n\n", strings.Title(engine)) |
| 124 | + } |
| 125 | + |
| 126 | + stat, _ := os.Stdin.Stat() |
| 127 | + if (stat.Mode() & os.ModeCharDevice) == 0 { |
| 128 | + sc := bufio.NewScanner(os.Stdin) |
| 129 | + for sc.Scan() { |
| 130 | + query = sc.Text() |
| 131 | + opts := Options{ |
| 132 | + Query: query, |
| 133 | + Engine: engine, |
| 134 | + Page: page, |
| 135 | + Proxy: proxy, |
| 136 | + Headers: headers, |
| 137 | + } |
| 138 | + |
| 139 | + err := opts.Search(noColor) |
| 140 | + Err(err) |
| 141 | + } |
| 142 | + } else { |
| 143 | + if query == "" { |
| 144 | + log.Fatalf("Missing required -q flag!") |
| 145 | + os.Exit(2) |
| 146 | + } |
| 147 | + |
| 148 | + opts := Options{ |
| 149 | + Query: query, |
| 150 | + Engine: engine, |
| 151 | + Page: page, |
| 152 | + Proxy: proxy, |
| 153 | + Headers: headers, |
| 154 | + } |
| 155 | + |
| 156 | + err := opts.Search(noColor) |
| 157 | + Err(err) |
| 158 | + } |
| 159 | +} |
| 160 | + |
| 161 | +func Parser(html string, pattern string) [][]string { |
| 162 | + regex := regexp.MustCompile(pattern) |
| 163 | + match := regex.FindAllStringSubmatch(html, -1)[0:] |
| 164 | + return match |
| 165 | +} |
| 166 | + |
| 167 | +func (opt *Options) Get(url string) string { |
| 168 | + client := Client(opt.Proxy) |
| 169 | + req, err := http.NewRequest("GET", url, nil) |
| 170 | + for _, h := range opt.Headers { |
| 171 | + parts := strings.SplitN(h, ":", 2) |
| 172 | + |
| 173 | + if len(parts) != 2 { |
| 174 | + continue |
| 175 | + } |
| 176 | + req.Header.Set(parts[0], parts[1]) |
| 177 | + } |
| 178 | + Err(err) |
| 179 | + |
| 180 | + resp, err := client.Do(req) |
| 181 | + Err(err) |
| 182 | + defer resp.Body.Close() |
| 183 | + |
| 184 | + data, _ := ioutil.ReadAll(resp.Body) |
| 185 | + body := string(data) |
| 186 | + return body |
| 187 | +} |
| 188 | + |
| 189 | +func (opt *Options) Search(noColor bool) error { |
| 190 | + queryEsc := url.QueryEscape(opt.Query) |
| 191 | + var regexes, baseURL, params string |
| 192 | + var o = color.New(color.FgGreen) |
| 193 | + |
| 194 | + switch opt.Engine { |
| 195 | + case "google": |
| 196 | + regexes = `"><a href="\/url\?q=(.*?)&sa=U&` |
| 197 | + baseURL = "https://www.google.com/search" |
| 198 | + params = ("q=" + queryEsc + "&gws_rd=cr,ssl&client=ubuntu&ie=UTF-8&start=") |
| 199 | + case "shodan": |
| 200 | + regexes = `\"><a href=\"/host/(.*?)\">` |
| 201 | + baseURL = "https://www.shodan.io/search" |
| 202 | + params = ("query=" + queryEsc + "&page=") |
| 203 | + case "bing": |
| 204 | + regexes = `</li><li class=\"b_algo\"><h2><a href=\"(.*?)\" h=\"ID=SERP,` |
| 205 | + baseURL = "https://www.bing.com/search" |
| 206 | + params = ("q=" + queryEsc + "&first=") |
| 207 | + case "duck": |
| 208 | + regexes = `<a rel=\"nofollow\" href=\"//duckduckgo.com/l/\?kh=-1&uddg=(.*?)\">` |
| 209 | + baseURL = "https://html.duckduckgo.com/html/" |
| 210 | + params = ("q=" + queryEsc + "&_=") |
| 211 | + case "yahoo": |
| 212 | + regexes = `\" ac-algo fz-l ac-21th lh-24\" href=\"(.*?)\" referrerpolicy=\"origin` |
| 213 | + baseURL = "https://search.yahoo.com/search" |
| 214 | + params = ("q=" + queryEsc + "&b=") |
| 215 | + case "ask": |
| 216 | + regexes = `target=\"_blank\" href='(.*?)' data-unified=` |
| 217 | + baseURL = "https://www.ask.com/web" |
| 218 | + params = ("q=" + queryEsc + "&page=") |
| 219 | + default: |
| 220 | + return errors.New("Engine not found! Please choose one available.") |
| 221 | + } |
| 222 | + |
| 223 | + for p := 1; p <= opt.Page; p++ { |
| 224 | + page := strconv.Itoa(p) |
| 225 | + |
| 226 | + switch opt.Engine { |
| 227 | + case "google": |
| 228 | + page += "0" |
| 229 | + case "yahoo", "bing": |
| 230 | + page += "1" |
| 231 | + } |
| 232 | + |
| 233 | + html := opt.Get(baseURL + "?" + params + page) |
| 234 | + result := Parser(html, regexes) |
| 235 | + for i := range result { |
| 236 | + url, err := url.QueryUnescape(result[i][1]) |
| 237 | + Err(err) |
| 238 | + if noColor { |
| 239 | + fmt.Println(url) |
| 240 | + } else { |
| 241 | + o.Println(url) |
| 242 | + } |
| 243 | + } |
| 244 | + |
| 245 | + if opt.Engine == "duck" && p == 1 { |
| 246 | + break |
| 247 | + } |
| 248 | + } |
| 249 | + |
| 250 | + return nil |
| 251 | +} |
| 252 | + |
| 253 | +func Client(proxy string) *http.Client { |
| 254 | + tr := &http.Transport{ |
| 255 | + MaxIdleConns: 30, |
| 256 | + IdleConnTimeout: time.Second, |
| 257 | + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, |
| 258 | + DialContext: (&net.Dialer{ |
| 259 | + Timeout: time.Second * 10, |
| 260 | + KeepAlive: time.Second, |
| 261 | + }).DialContext, |
| 262 | + } |
| 263 | + |
| 264 | + if proxy != "" { |
| 265 | + if p, err := url.Parse(proxy); err == nil { |
| 266 | + tr.Proxy = http.ProxyURL(p) |
| 267 | + } |
| 268 | + } |
| 269 | + |
| 270 | + re := func(req *http.Request, via []*http.Request) error { |
| 271 | + return http.ErrUseLastResponse |
| 272 | + } |
| 273 | + |
| 274 | + return &http.Client{ |
| 275 | + Transport: tr, |
| 276 | + CheckRedirect: re, |
| 277 | + Timeout: time.Second * 10, |
| 278 | + } |
| 279 | +} |
0 commit comments