nuclei/v2/pkg/protocols/common/generators/generators.go

241 lines
5.6 KiB
Go
Raw Normal View History

2020-12-21 19:32:38 +00:00
// Inspired from https://github.com/ffuf/ffuf/blob/master/pkg/input/input.go
package generators
2020-12-26 17:22:33 +00:00
import (
"errors"
)
2020-12-21 19:32:38 +00:00
// Generator is the generator struct for generating payloads
type Generator struct {
Type Type
payloads map[string][]string
}
// Type is type of attack
type Type int
const (
// Sniper replaces each variables with values at a time.
Sniper Type = iota + 1
// PitchFork replaces variables with positional value from multiple wordlists
PitchFork
// ClusterBomb replaces variables with all possible combinations of values
ClusterBomb
)
// StringToType is an table for conversion of attack type from string.
var StringToType = map[string]Type{
"sniper": Sniper,
"pitchfork": PitchFork,
"clusterbomb": ClusterBomb,
}
// New creates a new generator structure for payload generation
2021-02-26 07:43:11 +00:00
func New(payloads map[string]interface{}, payloadType Type, templatePath string) (*Generator, error) {
2020-12-29 06:38:46 +00:00
generator := &Generator{}
if err := generator.validate(payloads, templatePath); err != nil {
return nil, err
}
2020-12-21 19:32:38 +00:00
compiled, err := loadPayloads(payloads)
if err != nil {
return nil, err
}
2021-02-26 07:43:11 +00:00
generator.Type = payloadType
2020-12-29 06:38:46 +00:00
generator.payloads = compiled
2020-12-26 17:22:33 +00:00
// Validate the payload types
2021-02-26 07:43:11 +00:00
if payloadType == PitchFork {
2020-12-26 17:22:33 +00:00
var totalLength int
for v := range compiled {
if totalLength != 0 && totalLength != len(v) {
return nil, errors.New("pitchfork payloads must be of equal number")
}
totalLength = len(v)
}
}
return generator, nil
2020-12-21 19:32:38 +00:00
}
// Iterator is a single instance of an iterator for a generator structure
type Iterator struct {
Type Type
position int
msbIterator int
2020-12-26 17:22:33 +00:00
total int
2020-12-21 19:32:38 +00:00
payloads []*payloadIterator
}
// NewIterator creates a new iterator for the payloads generator
func (g *Generator) NewIterator() *Iterator {
var payloads []*payloadIterator
for name, values := range g.payloads {
payloads = append(payloads, &payloadIterator{name: name, values: values})
}
2020-12-26 17:22:33 +00:00
iterator := &Iterator{
Type: g.Type,
payloads: payloads,
}
iterator.total = iterator.Total()
return iterator
2020-12-21 19:32:38 +00:00
}
2020-12-26 17:22:33 +00:00
// Reset resets the iterator back to its initial value
func (i *Iterator) Reset() {
i.position = 0
i.msbIterator = 0
for _, payload := range i.payloads {
payload.resetPosition()
2020-12-21 19:32:38 +00:00
}
}
// Remaining returns the amount of requests left for the generator.
func (i *Iterator) Remaining() int {
return i.total - i.position
}
// Total returns the amount of input combinations available
2020-12-21 19:32:38 +00:00
func (i *Iterator) Total() int {
count := 0
switch i.Type {
case Sniper:
for _, p := range i.payloads {
count += len(p.values)
}
case PitchFork:
2020-12-26 17:22:33 +00:00
count = len(i.payloads[0].values)
2020-12-21 19:32:38 +00:00
case ClusterBomb:
count = 1
for _, p := range i.payloads {
2021-02-26 07:43:11 +00:00
count *= len(p.values)
2020-12-21 19:32:38 +00:00
}
}
return count
}
// Value returns the next value for an iterator
2020-12-26 17:22:33 +00:00
func (i *Iterator) Value() (map[string]interface{}, bool) {
2020-12-21 19:32:38 +00:00
switch i.Type {
case Sniper:
return i.sniperValue()
case PitchFork:
return i.pitchforkValue()
case ClusterBomb:
return i.clusterbombValue()
default:
return i.sniperValue()
}
}
// sniperValue returns a list of all payloads for the iterator
2020-12-26 17:22:33 +00:00
func (i *Iterator) sniperValue() (map[string]interface{}, bool) {
values := make(map[string]interface{}, 1)
2020-12-21 19:32:38 +00:00
currentIndex := i.msbIterator
payload := i.payloads[currentIndex]
2020-12-26 17:22:33 +00:00
if !payload.next() {
i.msbIterator++
if i.msbIterator == len(i.payloads) {
return nil, false
}
return i.sniperValue()
2020-12-21 19:32:38 +00:00
}
2020-12-26 17:22:33 +00:00
values[payload.name] = payload.value()
payload.incrementPosition()
i.position++
2020-12-26 17:22:33 +00:00
return values, true
2020-12-21 19:32:38 +00:00
}
// pitchforkValue returns a map of keyword:value pairs in same index
2020-12-26 17:22:33 +00:00
func (i *Iterator) pitchforkValue() (map[string]interface{}, bool) {
2020-12-21 19:32:38 +00:00
values := make(map[string]interface{}, len(i.payloads))
for _, p := range i.payloads {
2020-12-26 17:22:33 +00:00
if !p.next() {
return nil, false
2020-12-21 19:32:38 +00:00
}
2020-12-26 17:22:33 +00:00
values[p.name] = p.value()
p.incrementPosition()
2020-12-21 19:32:38 +00:00
}
i.position++
2020-12-26 17:22:33 +00:00
return values, true
2020-12-21 19:32:38 +00:00
}
// clusterbombValue returns a combination of all input pairs in key:value format.
2020-12-26 17:22:33 +00:00
func (i *Iterator) clusterbombValue() (map[string]interface{}, bool) {
if i.position >= i.total {
return nil, false
}
2020-12-21 19:32:38 +00:00
values := make(map[string]interface{}, len(i.payloads))
// Should we signal the next InputProvider in the slice to increment
signalNext := false
first := true
for index, p := range i.payloads {
if signalNext {
2020-12-26 17:22:33 +00:00
p.incrementPosition()
2020-12-21 19:32:38 +00:00
signalNext = false
}
2020-12-26 17:22:33 +00:00
if !p.next() {
2020-12-21 19:32:38 +00:00
// No more inputs in this inputprovider
if index == i.msbIterator {
// Reset all previous wordlists and increment the msb counter
i.msbIterator++
i.clusterbombIteratorReset()
// Start again
return i.clusterbombValue()
}
2020-12-26 17:22:33 +00:00
p.resetPosition()
2020-12-21 19:32:38 +00:00
signalNext = true
}
2020-12-26 17:22:33 +00:00
values[p.name] = p.value()
2020-12-21 19:32:38 +00:00
if first {
2020-12-26 17:22:33 +00:00
p.incrementPosition()
2020-12-21 19:32:38 +00:00
first = false
}
}
2020-12-26 17:22:33 +00:00
i.position++
return values, true
2020-12-21 19:32:38 +00:00
}
func (i *Iterator) clusterbombIteratorReset() {
for index, p := range i.payloads {
if index < i.msbIterator {
2020-12-26 17:22:33 +00:00
p.resetPosition()
2020-12-21 19:32:38 +00:00
}
if index == i.msbIterator {
2020-12-26 17:22:33 +00:00
p.incrementPosition()
2020-12-21 19:32:38 +00:00
}
}
}
// payloadIterator is a single instance of an iterator for a single payload list.
type payloadIterator struct {
index int
name string
values []string
}
2020-12-26 17:22:33 +00:00
// next returns true if there are more values in payload iterator
func (i *payloadIterator) next() bool {
return i.index < len(i.values)
2020-12-21 19:32:38 +00:00
}
2020-12-26 17:22:33 +00:00
// resetPosition resets the position of the payload iterator
func (i *payloadIterator) resetPosition() {
2020-12-21 19:32:38 +00:00
i.index = 0
}
2020-12-26 17:22:33 +00:00
// incrementPosition increments the position of the payload iterator
func (i *payloadIterator) incrementPosition() {
2020-12-21 19:32:38 +00:00
i.index++
}
2020-12-26 17:22:33 +00:00
// value returns the value of the payload at an index
func (i *payloadIterator) value() string {
return i.values[i.index]
2020-12-21 19:32:38 +00:00
}