Path: blob/dev/pkg/protocols/common/generators/generators.go
2072 views
// Inspired from https://github.com/ffuf/ffuf/blob/master/pkg/input/input.go12package generators34import (5"github.com/pkg/errors"6"maps"78"github.com/projectdiscovery/nuclei/v3/pkg/catalog"9"github.com/projectdiscovery/nuclei/v3/pkg/types"10)1112// PayloadGenerator is the generator struct for generating payloads13type PayloadGenerator struct {14Type AttackType15catalog catalog.Catalog16payloads map[string][]string17options *types.Options18}1920// New creates a new generator structure for payload generation21func New(payloads map[string]interface{}, attackType AttackType, templatePath string, catalog catalog.Catalog, customAttackType string, opts *types.Options) (*PayloadGenerator, error) {22if attackType.String() == "" {23attackType = BatteringRamAttack24}2526// Resolve payload paths if they are files.27payloadsFinal := make(map[string]interface{})28for payloadName, v := range payloads {29switch value := v.(type) {30case map[interface{}]interface{}:31values, err := parsePayloadsWithAggression(payloadName, value, opts.FuzzAggressionLevel)32if err != nil {33return nil, errors.Wrap(err, "could not parse payloads with aggression")34}35maps.Copy(payloadsFinal, values)36default:37payloadsFinal[payloadName] = v38}39}4041generator := &PayloadGenerator{catalog: catalog, options: opts}42if err := generator.validate(payloadsFinal, templatePath); err != nil {43return nil, err44}4546compiled, err := generator.loadPayloads(payloadsFinal, templatePath)47if err != nil {48return nil, err49}50generator.Type = attackType51generator.payloads = compiled5253if customAttackType != "" {54attackTypeNew, err := toAttackType(customAttackType)55if err != nil {56return nil, errors.Wrap(err, "could not parse custom attack-type")57}58generator.Type = attackTypeNew59}60// Validate the batteringram payload set61if attackType == BatteringRamAttack {62if len(payloads) != 1 {63return nil, errors.New("batteringram must have single payload set")64}65}66return generator, nil67}6869type aggressionLevelToPayloads struct {70Low []interface{}71Medium []interface{}72High []interface{}73}7475// parsePayloadsWithAggression parses the payloads with the aggression level76//77// Three agression are supported -78// - low79// - medium80// - high81//82// low is the default level. If medium is specified, all templates from83// low and medium are executed. Similarly with high, including all templates84// from low, medium, high.85func parsePayloadsWithAggression(name string, v map[interface{}]interface{}, agression string) (map[string]interface{}, error) {86payloadsLevels := &aggressionLevelToPayloads{}8788for k, v := range v {89if _, ok := v.([]interface{}); !ok {90return nil, errors.Errorf("only lists are supported for aggression levels payloads")91}92var ok bool93switch k {94case "low":95payloadsLevels.Low, ok = v.([]interface{})96case "medium":97payloadsLevels.Medium, ok = v.([]interface{})98case "high":99payloadsLevels.High, ok = v.([]interface{})100default:101return nil, errors.Errorf("invalid aggression level %s specified for %s", k, name)102}103if !ok {104return nil, errors.Errorf("invalid aggression level %s specified for %s", k, name)105}106}107108payloads := make(map[string]interface{})109switch agression {110case "low":111payloads[name] = payloadsLevels.Low112case "medium":113payloads[name] = append(payloadsLevels.Low, payloadsLevels.Medium...)114case "high":115payloads[name] = append(payloadsLevels.Low, payloadsLevels.Medium...)116payloads[name] = append(payloads[name].([]interface{}), payloadsLevels.High...)117default:118return nil, errors.Errorf("invalid aggression level %s specified for %s", agression, name)119}120return payloads, nil121}122123// Iterator is a single instance of an iterator for a generator structure124type Iterator struct {125Type AttackType126position int127msbIterator int128total int129payloads []*payloadIterator130}131132// NewIterator creates a new iterator for the payloads generator133func (g *PayloadGenerator) NewIterator() *Iterator {134var payloads []*payloadIterator135136for name, values := range g.payloads {137payloads = append(payloads, &payloadIterator{name: name, values: values})138}139iterator := &Iterator{140Type: g.Type,141payloads: payloads,142}143iterator.total = iterator.Total()144return iterator145}146147// Reset resets the iterator back to its initial value148func (i *Iterator) Reset() {149i.position = 0150i.msbIterator = 0151152for _, payload := range i.payloads {153payload.resetPosition()154}155}156157// Remaining returns the amount of requests left for the generator.158func (i *Iterator) Remaining() int {159return i.total - i.position160}161162// Total returns the amount of input combinations available163func (i *Iterator) Total() int {164count := 0165switch i.Type {166case BatteringRamAttack:167for _, p := range i.payloads {168count += len(p.values)169}170case PitchForkAttack:171count = len(i.payloads[0].values)172for _, p := range i.payloads {173if count > len(p.values) {174count = len(p.values)175}176}177case ClusterBombAttack:178count = 1179for _, p := range i.payloads {180count *= len(p.values)181}182}183return count184}185186// Value returns the next value for an iterator187func (i *Iterator) Value() (map[string]interface{}, bool) {188switch i.Type {189case BatteringRamAttack:190return i.batteringRamValue()191case PitchForkAttack:192return i.pitchforkValue()193case ClusterBombAttack:194return i.clusterbombValue()195default:196return i.batteringRamValue()197}198}199200// batteringRamValue returns a list of all payloads for the iterator201func (i *Iterator) batteringRamValue() (map[string]interface{}, bool) {202values := make(map[string]interface{}, 1)203204currentIndex := i.msbIterator205payload := i.payloads[currentIndex]206if !payload.next() {207i.msbIterator++208if i.msbIterator == len(i.payloads) {209return nil, false210}211return i.batteringRamValue()212}213values[payload.name] = payload.value()214payload.incrementPosition()215i.position++216return values, true217}218219// pitchforkValue returns a map of keyword:value pairs in same index220func (i *Iterator) pitchforkValue() (map[string]interface{}, bool) {221values := make(map[string]interface{}, len(i.payloads))222223for _, p := range i.payloads {224if !p.next() {225return nil, false226}227values[p.name] = p.value()228p.incrementPosition()229}230i.position++231return values, true232}233234// clusterbombValue returns a combination of all input pairs in key:value format.235func (i *Iterator) clusterbombValue() (map[string]interface{}, bool) {236if i.position >= i.total {237return nil, false238}239values := make(map[string]interface{}, len(i.payloads))240241// Should we signal the next InputProvider in the slice to increment242signalNext := false243first := true244for index, p := range i.payloads {245if signalNext {246p.incrementPosition()247signalNext = false248}249if !p.next() {250// No more inputs in this input provider251if index == i.msbIterator {252// Reset all previous wordlists and increment the msb counter253i.msbIterator++254i.clusterbombIteratorReset()255// Start again256return i.clusterbombValue()257}258p.resetPosition()259signalNext = true260}261values[p.name] = p.value()262if first {263p.incrementPosition()264first = false265}266}267i.position++268return values, true269}270271func (i *Iterator) clusterbombIteratorReset() {272for index, p := range i.payloads {273if index < i.msbIterator {274p.resetPosition()275}276if index == i.msbIterator {277p.incrementPosition()278}279}280}281282// payloadIterator is a single instance of an iterator for a single payload list.283type payloadIterator struct {284index int285name string286values []string287}288289// next returns true if there are more values in payload iterator290func (i *payloadIterator) next() bool {291return i.index < len(i.values)292}293294// resetPosition resets the position of the payload iterator295func (i *payloadIterator) resetPosition() {296i.index = 0297}298299// incrementPosition increments the position of the payload iterator300func (i *payloadIterator) incrementPosition() {301i.index++302}303304// value returns the value of the payload at an index305func (i *payloadIterator) value() string {306return i.values[i.index]307}308309310