mirror of https://github.com/daffainfo/nuclei.git
136 lines
3.8 KiB
Go
136 lines
3.8 KiB
Go
|
package uncover
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"fmt"
|
||
|
"runtime"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/projectdiscovery/gologger"
|
||
|
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||
|
"github.com/projectdiscovery/uncover"
|
||
|
"github.com/projectdiscovery/uncover/sources"
|
||
|
mapsutil "github.com/projectdiscovery/utils/maps"
|
||
|
stringsutil "github.com/projectdiscovery/utils/strings"
|
||
|
)
|
||
|
|
||
|
// returns csv string of uncover supported agents
|
||
|
func GetUncoverSupportedAgents() string {
|
||
|
u, _ := uncover.New(&uncover.Options{})
|
||
|
return strings.Join(u.AllAgents(), ",")
|
||
|
}
|
||
|
|
||
|
// GetTargetsFromUncover returns targets from uncover
|
||
|
func GetTargetsFromUncover(ctx context.Context, outputFormat string, opts *uncover.Options) (chan string, error) {
|
||
|
u, err := uncover.New(opts)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
resChan, err := u.Execute(ctx)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
outputChan := make(chan string) // buffered channel
|
||
|
go func() {
|
||
|
defer close(outputChan)
|
||
|
for {
|
||
|
select {
|
||
|
case <-ctx.Done():
|
||
|
return
|
||
|
case res, ok := <-resChan:
|
||
|
if !ok {
|
||
|
return
|
||
|
}
|
||
|
if res.Error != nil {
|
||
|
// only log in verbose mode
|
||
|
gologger.Verbose().Msgf("uncover: %v", res.Error)
|
||
|
continue
|
||
|
}
|
||
|
outputChan <- processUncoverOutput(res, outputFormat)
|
||
|
}
|
||
|
}
|
||
|
}()
|
||
|
return outputChan, nil
|
||
|
}
|
||
|
|
||
|
// processUncoverOutput returns output string depending on uncover field
|
||
|
func processUncoverOutput(result sources.Result, outputFormat string) string {
|
||
|
if (result.IP == "" || result.Port == 0) && stringsutil.ContainsAny(outputFormat, "ip", "port") {
|
||
|
// if ip or port is not present, fallback to using host
|
||
|
outputFormat = "host"
|
||
|
}
|
||
|
replacer := strings.NewReplacer(
|
||
|
"ip", result.IP,
|
||
|
"host", result.Host,
|
||
|
"port", fmt.Sprint(result.Port),
|
||
|
"url", result.Url,
|
||
|
)
|
||
|
return replacer.Replace(outputFormat)
|
||
|
}
|
||
|
|
||
|
// GetUncoverTargetsFromMetadata returns targets from uncover metadata
|
||
|
func GetUncoverTargetsFromMetadata(ctx context.Context, templates []*templates.Template, outputFormat string, opts *uncover.Options) chan string {
|
||
|
// contains map[engine]queries
|
||
|
queriesMap := make(map[string][]string)
|
||
|
for _, template := range templates {
|
||
|
innerLoop:
|
||
|
for k, v := range template.Info.Metadata {
|
||
|
if !strings.HasSuffix(k, "-query") {
|
||
|
// this is not a query
|
||
|
// query keys are like shodan-query, fofa-query, etc
|
||
|
continue innerLoop
|
||
|
}
|
||
|
engine := strings.TrimSuffix(k, "-query")
|
||
|
if queriesMap[engine] == nil {
|
||
|
queriesMap[engine] = []string{}
|
||
|
}
|
||
|
queriesMap[engine] = append(queriesMap[engine], fmt.Sprint(v))
|
||
|
}
|
||
|
}
|
||
|
keys := mapsutil.GetKeys(queriesMap)
|
||
|
gologger.Info().Msgf("Running uncover queries from template against: %s", strings.Join(keys, ","))
|
||
|
result := make(chan string, runtime.NumCPU())
|
||
|
go func() {
|
||
|
defer close(result)
|
||
|
// unfortunately uncover doesn't support execution of map[engine]queries
|
||
|
// if queries are given they are executed against all engines which is not what we want
|
||
|
// TODO: add support for map[engine]queries in uncover
|
||
|
// Note below implementation is intentionally sequential to avoid burning all the API keys
|
||
|
counter := 0
|
||
|
|
||
|
for eng, queries := range queriesMap {
|
||
|
// create new uncover options for each engine
|
||
|
uncoverOpts := &uncover.Options{
|
||
|
Agents: []string{eng},
|
||
|
Queries: queries,
|
||
|
Limit: opts.Limit,
|
||
|
MaxRetry: opts.MaxRetry,
|
||
|
Timeout: opts.Timeout,
|
||
|
RateLimit: opts.RateLimit,
|
||
|
RateLimitUnit: opts.RateLimitUnit,
|
||
|
}
|
||
|
ch, err := GetTargetsFromUncover(ctx, outputFormat, uncoverOpts)
|
||
|
if err != nil {
|
||
|
gologger.Error().Msgf("Could not get targets using %v engine from uncover: %s", eng, err)
|
||
|
return
|
||
|
}
|
||
|
for {
|
||
|
select {
|
||
|
case <-ctx.Done():
|
||
|
return
|
||
|
case res, ok := <-ch:
|
||
|
if !ok {
|
||
|
return
|
||
|
}
|
||
|
result <- res
|
||
|
counter++
|
||
|
if opts.Limit > 0 && counter >= opts.Limit {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}()
|
||
|
return result
|
||
|
}
|