Path: blob/dev/pkg/protocols/common/uncover/uncover.go
2072 views
package uncover12import (3"context"4"fmt"5"runtime"6"strings"78"github.com/projectdiscovery/gologger"9"github.com/projectdiscovery/nuclei/v3/pkg/templates"10"github.com/projectdiscovery/uncover"11"github.com/projectdiscovery/uncover/sources"12mapsutil "github.com/projectdiscovery/utils/maps"13sliceutil "github.com/projectdiscovery/utils/slice"14stringsutil "github.com/projectdiscovery/utils/strings"15)1617// returns csv string of uncover supported agents18func GetUncoverSupportedAgents() string {19u, _ := uncover.New(&uncover.Options{})20return strings.Join(u.AllAgents(), ",")21}2223// GetTargetsFromUncover returns targets from uncover24func GetTargetsFromUncover(ctx context.Context, outputFormat string, opts *uncover.Options) (chan string, error) {25u, err := uncover.New(opts)26if err != nil {27return nil, err28}29resChan, err := u.Execute(ctx)30if err != nil {31return nil, err32}33outputChan := make(chan string) // buffered channel34go func() {35defer close(outputChan)36for {37select {38case <-ctx.Done():39return40case res, ok := <-resChan:41if !ok {42return43}44if res.Error != nil {45// only log in verbose mode46gologger.Verbose().Msgf("uncover: %v", res.Error)47continue48}49outputChan <- processUncoverOutput(res, outputFormat)50}51}52}()53return outputChan, nil54}5556// processUncoverOutput returns output string depending on uncover field57func processUncoverOutput(result sources.Result, outputFormat string) string {58if (result.IP == "" || result.Port == 0) && stringsutil.ContainsAny(outputFormat, "ip", "port") {59// if ip or port is not present, fallback to using host60outputFormat = "host"61}62replacer := strings.NewReplacer(63"ip", result.IP,64"host", result.Host,65"port", fmt.Sprint(result.Port),66"url", result.Url,67)68return replacer.Replace(outputFormat)69}7071// GetUncoverTargetsFromMetadata returns targets from uncover metadata72func GetUncoverTargetsFromMetadata(ctx context.Context, templates []*templates.Template, outputFormat string, opts *uncover.Options) chan string {73// contains map[engine]queries74queriesMap := make(map[string][]string)75for _, template := range templates {76innerLoop:77for k, v := range template.Info.Metadata {78if !strings.HasSuffix(k, "-query") {79// this is not a query80// query keys are like shodan-query, fofa-query, etc81continue innerLoop82}83engine := strings.TrimSuffix(k, "-query")84if queriesMap[engine] == nil {85queriesMap[engine] = []string{}86}87switch v := v.(type) {88case []interface{}:89qs := queriesMap[engine]90for _, vv := range v {91qs = append(qs, fmt.Sprint(vv))92}93queriesMap[engine] = qs94default:95queriesMap[engine] = append(queriesMap[engine], fmt.Sprint(v))96}97}98}99for engine, queries := range queriesMap {100queriesMap[engine] = sliceutil.Dedupe(queries)101}102keys := mapsutil.GetKeys(queriesMap)103gologger.Info().Msgf("Running uncover queries from template against: %s", strings.Join(keys, ","))104result := make(chan string, runtime.NumCPU())105go func() {106defer close(result)107// unfortunately uncover doesn't support execution of map[engine]queries108// if queries are given they are executed against all engines which is not what we want109// TODO: add support for map[engine]queries in uncover110// Note below implementation is intentionally sequential to avoid burning all the API keys111counter := 0112outerLoop:113for eng, queries := range queriesMap {114if opts.Limit > 0 && counter >= opts.Limit {115break116}117// create new uncover options for each engine118uncoverOpts := &uncover.Options{119Agents: []string{eng},120Queries: queries,121Limit: opts.Limit,122MaxRetry: opts.MaxRetry,123Timeout: opts.Timeout,124RateLimit: opts.RateLimit,125RateLimitUnit: opts.RateLimitUnit,126}127ch, err := GetTargetsFromUncover(ctx, outputFormat, uncoverOpts)128if err != nil {129gologger.Error().Msgf("Could not get targets using %v engine from uncover: %s", eng, err)130return131}132133innerLoop:134for {135select {136case <-ctx.Done():137return138case res, ok := <-ch:139if !ok {140continue outerLoop141}142result <- res143counter++144if opts.Limit > 0 && counter >= opts.Limit {145break innerLoop146}147}148}149}150}()151return result152}153154155