2021-10-28 17:20:07 +05:30
|
|
|
// Package hybrid implements a hybrid hmap/filekv backed input provider
|
2021-10-27 15:53:04 +05:30
|
|
|
// for nuclei that can either stream or store results using different kv stores.
|
2021-10-28 17:20:07 +05:30
|
|
|
package hybrid
|
2021-10-27 15:53:04 +05:30
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"io"
|
2022-11-09 14:18:56 +01:00
|
|
|
"net/url"
|
2021-10-27 15:53:04 +05:30
|
|
|
"os"
|
|
|
|
|
"strings"
|
2022-06-30 14:21:54 +02:00
|
|
|
"time"
|
2021-10-27 15:53:04 +05:30
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
2021-11-25 17:09:20 +02:00
|
|
|
|
2021-10-27 15:53:04 +05:30
|
|
|
"github.com/projectdiscovery/filekv"
|
|
|
|
|
"github.com/projectdiscovery/gologger"
|
|
|
|
|
"github.com/projectdiscovery/hmap/store/hybrid"
|
2022-10-07 14:28:02 +05:30
|
|
|
"github.com/projectdiscovery/mapcidr"
|
2022-10-14 00:25:00 +05:30
|
|
|
asn "github.com/projectdiscovery/mapcidr/asn"
|
2022-11-09 14:18:56 +01:00
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
|
|
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate"
|
2021-10-27 15:53:04 +05:30
|
|
|
"github.com/projectdiscovery/nuclei/v2/pkg/types"
|
2022-11-06 21:24:23 +01:00
|
|
|
fileutil "github.com/projectdiscovery/utils/file"
|
|
|
|
|
iputil "github.com/projectdiscovery/utils/ip"
|
2022-11-09 14:18:56 +01:00
|
|
|
sliceutil "github.com/projectdiscovery/utils/slice"
|
2021-10-27 15:53:04 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Input is a hmap/filekv backed nuclei Input provider
|
|
|
|
|
type Input struct {
|
2022-11-09 14:18:56 +01:00
|
|
|
ipOptions *ipOptions
|
2021-10-27 15:53:04 +05:30
|
|
|
inputCount int64
|
|
|
|
|
dupeCount int64
|
|
|
|
|
hostMap *hybrid.HybridMap
|
|
|
|
|
hostMapStream *filekv.FileDB
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// New creates a new hmap backed nuclei Input Provider
|
|
|
|
|
// and initializes it based on the passed options Model.
|
|
|
|
|
func New(options *types.Options) (*Input, error) {
|
|
|
|
|
hm, err := hybrid.New(hybrid.DefaultDiskOptions)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "could not create temporary input file")
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-09 14:18:56 +01:00
|
|
|
input := &Input{
|
|
|
|
|
hostMap: hm,
|
|
|
|
|
ipOptions: &ipOptions{
|
|
|
|
|
ScanAllIPs: options.ScanAllIPs,
|
|
|
|
|
IPV4: sliceutil.Contains(options.IPVersion, "4"),
|
|
|
|
|
IPV6: sliceutil.Contains(options.IPVersion, "6"),
|
|
|
|
|
},
|
|
|
|
|
}
|
2021-10-27 15:53:04 +05:30
|
|
|
if options.Stream {
|
|
|
|
|
fkvOptions := filekv.DefaultOptions
|
|
|
|
|
if tmpFileName, err := fileutil.GetTempFileName(); err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "could not create temporary input file")
|
|
|
|
|
} else {
|
|
|
|
|
fkvOptions.Path = tmpFileName
|
|
|
|
|
}
|
|
|
|
|
fkv, err := filekv.Open(fkvOptions)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrap(err, "could not create temporary unsorted input file")
|
|
|
|
|
}
|
|
|
|
|
input.hostMapStream = fkv
|
|
|
|
|
}
|
|
|
|
|
if initErr := input.initializeInputSources(options); initErr != nil {
|
|
|
|
|
return nil, initErr
|
|
|
|
|
}
|
|
|
|
|
if input.dupeCount > 0 {
|
|
|
|
|
gologger.Info().Msgf("Supplied input was automatically deduplicated (%d removed).", input.dupeCount)
|
|
|
|
|
}
|
|
|
|
|
return input, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Close closes the input provider
|
|
|
|
|
func (i *Input) Close() {
|
|
|
|
|
i.hostMap.Close()
|
|
|
|
|
if i.hostMapStream != nil {
|
|
|
|
|
i.hostMapStream.Close()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// initializeInputSources initializes the input sources for hmap input
|
|
|
|
|
func (i *Input) initializeInputSources(options *types.Options) error {
|
|
|
|
|
// Handle targets flags
|
|
|
|
|
for _, target := range options.Targets {
|
2022-11-09 14:18:56 +01:00
|
|
|
switch {
|
|
|
|
|
case iputil.IsCIDR(target):
|
2022-10-07 14:28:02 +05:30
|
|
|
i.expandCIDRInputValue(target)
|
2022-11-09 14:18:56 +01:00
|
|
|
case asn.IsASN(target):
|
2022-10-14 00:25:00 +05:30
|
|
|
i.expandASNInputValue(target)
|
2022-11-09 14:18:56 +01:00
|
|
|
default:
|
|
|
|
|
i.normalizeStoreInputValue(target)
|
2022-10-14 00:25:00 +05:30
|
|
|
}
|
2021-10-27 15:53:04 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle stdin
|
|
|
|
|
if options.Stdin {
|
2022-06-30 14:21:54 +02:00
|
|
|
i.scanInputFromReader(fileutil.TimeoutReader{Reader: os.Stdin, Timeout: time.Duration(options.InputReadTimeout)})
|
2021-10-27 15:53:04 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle target file
|
|
|
|
|
if options.TargetsFilePath != "" {
|
|
|
|
|
input, inputErr := os.Open(options.TargetsFilePath)
|
|
|
|
|
if inputErr != nil {
|
|
|
|
|
return errors.Wrap(inputErr, "could not open targets file")
|
|
|
|
|
}
|
2022-11-09 14:18:56 +01:00
|
|
|
defer input.Close()
|
|
|
|
|
|
2021-10-27 15:53:04 +05:30
|
|
|
i.scanInputFromReader(input)
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// scanInputFromReader scans a line of input from reader and passes it for storage
|
|
|
|
|
func (i *Input) scanInputFromReader(reader io.Reader) {
|
|
|
|
|
scanner := bufio.NewScanner(reader)
|
|
|
|
|
for scanner.Scan() {
|
2022-11-09 14:18:56 +01:00
|
|
|
item := scanner.Text()
|
|
|
|
|
switch {
|
|
|
|
|
case iputil.IsCIDR(item):
|
|
|
|
|
i.expandCIDRInputValue(item)
|
|
|
|
|
case asn.IsASN(item):
|
|
|
|
|
i.expandASNInputValue(item)
|
|
|
|
|
default:
|
|
|
|
|
i.normalizeStoreInputValue(item)
|
2022-10-07 14:28:02 +05:30
|
|
|
}
|
2021-10-27 15:53:04 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// normalizeStoreInputValue normalizes and stores passed input values
|
|
|
|
|
func (i *Input) normalizeStoreInputValue(value string) {
|
2022-11-09 14:18:56 +01:00
|
|
|
URL := strings.TrimSpace(value)
|
|
|
|
|
if URL == "" {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
metaInput := &contextargs.MetaInput{Input: URL}
|
|
|
|
|
keyURL, err := metaInput.MarshalString()
|
|
|
|
|
if err != nil {
|
|
|
|
|
gologger.Warning().Msgf("%s\n", err)
|
2021-10-27 15:53:04 +05:30
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-09 14:18:56 +01:00
|
|
|
if _, ok := i.hostMap.Get(keyURL); ok {
|
2021-10-27 15:53:04 +05:30
|
|
|
i.dupeCount++
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-09 14:18:56 +01:00
|
|
|
switch {
|
|
|
|
|
case i.ipOptions.ScanAllIPs:
|
|
|
|
|
// we need to resolve the hostname
|
|
|
|
|
// check if it's an url
|
|
|
|
|
var host string
|
|
|
|
|
parsedURL, err := url.Parse(value)
|
|
|
|
|
if err == nil && parsedURL.Host != "" {
|
|
|
|
|
host = parsedURL.Host
|
|
|
|
|
} else {
|
|
|
|
|
parsedURL = nil
|
|
|
|
|
host = value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dnsData, err := protocolstate.Dialer.GetDNSData(host)
|
|
|
|
|
if err == nil && (len(dnsData.A)+len(dnsData.AAAA)) > 0 {
|
|
|
|
|
var ips []string
|
|
|
|
|
if i.ipOptions.IPV4 {
|
|
|
|
|
ips = append(ips, dnsData.A...)
|
|
|
|
|
}
|
|
|
|
|
if i.ipOptions.IPV6 {
|
|
|
|
|
ips = append(ips, dnsData.AAAA...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, ip := range ips {
|
|
|
|
|
if ip == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
metaInput := &contextargs.MetaInput{Input: value, CustomIP: ip}
|
|
|
|
|
key, err := metaInput.MarshalString()
|
|
|
|
|
if err != nil {
|
|
|
|
|
gologger.Warning().Msgf("%s\n", err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
_ = i.hostMap.Set(key, nil)
|
|
|
|
|
if i.hostMapStream != nil {
|
|
|
|
|
_ = i.hostMapStream.Set([]byte(key), nil)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
fallthrough
|
|
|
|
|
default:
|
|
|
|
|
i.setItem(keyURL)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// setItem in the kv store
|
|
|
|
|
func (i *Input) setItem(k string) {
|
2021-10-27 15:53:04 +05:30
|
|
|
i.inputCount++
|
2022-11-09 14:18:56 +01:00
|
|
|
_ = i.hostMap.Set(k, nil)
|
2021-10-27 15:53:04 +05:30
|
|
|
if i.hostMapStream != nil {
|
2022-11-09 14:18:56 +01:00
|
|
|
_ = i.hostMapStream.Set([]byte(k), nil)
|
2021-10-27 15:53:04 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Count returns the input count
|
|
|
|
|
func (i *Input) Count() int64 {
|
|
|
|
|
return i.inputCount
|
|
|
|
|
}
|
2021-10-28 17:20:07 +05:30
|
|
|
|
2021-11-05 03:01:41 +05:30
|
|
|
// Scan iterates the input and each found item is passed to the
|
|
|
|
|
// callback consumer.
|
2022-11-09 14:18:56 +01:00
|
|
|
func (i *Input) Scan(callback func(value *contextargs.MetaInput) bool) {
|
2021-10-28 17:20:07 +05:30
|
|
|
callbackFunc := func(k, _ []byte) error {
|
2022-11-09 14:18:56 +01:00
|
|
|
metaInput := &contextargs.MetaInput{}
|
|
|
|
|
if err := metaInput.Unmarshal(string(k)); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !callback(metaInput) {
|
2022-10-20 17:23:00 +05:30
|
|
|
return io.EOF
|
|
|
|
|
}
|
2021-10-28 17:20:07 +05:30
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if i.hostMapStream != nil {
|
|
|
|
|
_ = i.hostMapStream.Scan(callbackFunc)
|
|
|
|
|
} else {
|
|
|
|
|
i.hostMap.Scan(callbackFunc)
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-07 14:28:02 +05:30
|
|
|
|
|
|
|
|
// expandCIDRInputValue expands CIDR and stores expanded IPs
|
|
|
|
|
func (i *Input) expandCIDRInputValue(value string) {
|
|
|
|
|
ips, _ := mapcidr.IPAddressesAsStream(value)
|
|
|
|
|
for ip := range ips {
|
2022-11-09 14:18:56 +01:00
|
|
|
metaInput := &contextargs.MetaInput{Input: ip}
|
|
|
|
|
key, err := metaInput.MarshalString()
|
|
|
|
|
if err != nil {
|
|
|
|
|
gologger.Warning().Msgf("%s\n", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if _, ok := i.hostMap.Get(key); ok {
|
2022-10-07 14:28:02 +05:30
|
|
|
i.dupeCount++
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
i.inputCount++
|
2022-11-09 14:18:56 +01:00
|
|
|
_ = i.hostMap.Set(key, nil)
|
2022-10-07 14:28:02 +05:30
|
|
|
if i.hostMapStream != nil {
|
2022-11-09 14:18:56 +01:00
|
|
|
_ = i.hostMapStream.Set([]byte(key), nil)
|
2022-10-07 14:28:02 +05:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-14 00:25:00 +05:30
|
|
|
|
|
|
|
|
// expandASNInputValue expands CIDRs for given ASN and stores expanded IPs
|
|
|
|
|
func (i *Input) expandASNInputValue(value string) {
|
|
|
|
|
asnClient := asn.New()
|
|
|
|
|
cidrs, _ := asnClient.GetCIDRsForASNNum(value)
|
|
|
|
|
for _, cidr := range cidrs {
|
|
|
|
|
i.expandCIDRInputValue(cidr.String())
|
|
|
|
|
}
|
|
|
|
|
}
|