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)
|
|
|
|
}
|
|
|
|
}
|
2020-12-28 14:32:26 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-28 14:32:26 +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 {
|
2020-12-30 08:27:15 +00:00
|
|
|
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
|
|
|
|
2020-12-30 08:27:15 +00:00
|
|
|
currentIndex := i.msbIterator
|
|
|
|
payload := i.payloads[currentIndex]
|
2020-12-26 17:22:33 +00:00
|
|
|
if !payload.next() {
|
2020-12-30 08:27:15 +00:00
|
|
|
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()
|
2020-12-28 14:32:26 +00:00
|
|
|
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
|
|
|
}
|
2020-12-28 14:32:26 +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
|
|
|
}
|