github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/common/generators/generators.go (about) 1 // Inspired from https://github.com/ffuf/ffuf/blob/master/pkg/input/input.go 2 3 package generators 4 5 import ( 6 "github.com/pkg/errors" 7 8 "github.com/projectdiscovery/nuclei/v2/pkg/catalog" 9 "github.com/projectdiscovery/nuclei/v2/pkg/catalog/config" 10 ) 11 12 // PayloadGenerator is the generator struct for generating payloads 13 type PayloadGenerator struct { 14 Type AttackType 15 catalog catalog.Catalog 16 payloads map[string][]string 17 } 18 19 // New creates a new generator structure for payload generation 20 func New(payloads map[string]interface{}, attackType AttackType, templatePath string, allowLocalFileAccess bool, catalog catalog.Catalog, customAttackType string) (*PayloadGenerator, error) { 21 if attackType.String() == "" { 22 attackType = BatteringRamAttack 23 } 24 25 // Resolve payload paths if they are files. 26 payloadsFinal := make(map[string]interface{}) 27 for name, payload := range payloads { 28 payloadsFinal[name] = payload 29 } 30 for name, payload := range payloads { 31 payloadStr, ok := payload.(string) 32 if ok { 33 final, resolveErr := catalog.ResolvePath(payloadStr, templatePath) 34 if resolveErr != nil { 35 return nil, errors.Wrap(resolveErr, "could not read payload file") 36 } 37 payloadsFinal[name] = final 38 } 39 } 40 41 generator := &PayloadGenerator{catalog: catalog} 42 if err := generator.validate(payloadsFinal, templatePath); err != nil { 43 return nil, err 44 } 45 46 compiled, err := generator.loadPayloads(payloadsFinal, templatePath, config.DefaultConfig.TemplatesDirectory, allowLocalFileAccess) 47 if err != nil { 48 return nil, err 49 } 50 generator.Type = attackType 51 generator.payloads = compiled 52 53 if customAttackType != "" { 54 attackTypeNew, err := toAttackType(customAttackType) 55 if err != nil { 56 return nil, errors.Wrap(err, "could not parse custom attack-type") 57 } 58 generator.Type = attackTypeNew 59 } 60 // Validate the batteringram payload set 61 if attackType == BatteringRamAttack { 62 if len(payloads) != 1 { 63 return nil, errors.New("batteringram must have single payload set") 64 } 65 } 66 return generator, nil 67 } 68 69 // Iterator is a single instance of an iterator for a generator structure 70 type Iterator struct { 71 Type AttackType 72 position int 73 msbIterator int 74 total int 75 payloads []*payloadIterator 76 } 77 78 // NewIterator creates a new iterator for the payloads generator 79 func (g *PayloadGenerator) NewIterator() *Iterator { 80 var payloads []*payloadIterator 81 82 for name, values := range g.payloads { 83 payloads = append(payloads, &payloadIterator{name: name, values: values}) 84 } 85 iterator := &Iterator{ 86 Type: g.Type, 87 payloads: payloads, 88 } 89 iterator.total = iterator.Total() 90 return iterator 91 } 92 93 // Reset resets the iterator back to its initial value 94 func (i *Iterator) Reset() { 95 i.position = 0 96 i.msbIterator = 0 97 98 for _, payload := range i.payloads { 99 payload.resetPosition() 100 } 101 } 102 103 // Remaining returns the amount of requests left for the generator. 104 func (i *Iterator) Remaining() int { 105 return i.total - i.position 106 } 107 108 // Total returns the amount of input combinations available 109 func (i *Iterator) Total() int { 110 count := 0 111 switch i.Type { 112 case BatteringRamAttack: 113 for _, p := range i.payloads { 114 count += len(p.values) 115 } 116 case PitchForkAttack: 117 count = len(i.payloads[0].values) 118 for _, p := range i.payloads { 119 if count > len(p.values) { 120 count = len(p.values) 121 } 122 } 123 case ClusterBombAttack: 124 count = 1 125 for _, p := range i.payloads { 126 count *= len(p.values) 127 } 128 } 129 return count 130 } 131 132 // Value returns the next value for an iterator 133 func (i *Iterator) Value() (map[string]interface{}, bool) { 134 switch i.Type { 135 case BatteringRamAttack: 136 return i.batteringRamValue() 137 case PitchForkAttack: 138 return i.pitchforkValue() 139 case ClusterBombAttack: 140 return i.clusterbombValue() 141 default: 142 return i.batteringRamValue() 143 } 144 } 145 146 // batteringRamValue returns a list of all payloads for the iterator 147 func (i *Iterator) batteringRamValue() (map[string]interface{}, bool) { 148 values := make(map[string]interface{}, 1) 149 150 currentIndex := i.msbIterator 151 payload := i.payloads[currentIndex] 152 if !payload.next() { 153 i.msbIterator++ 154 if i.msbIterator == len(i.payloads) { 155 return nil, false 156 } 157 return i.batteringRamValue() 158 } 159 values[payload.name] = payload.value() 160 payload.incrementPosition() 161 i.position++ 162 return values, true 163 } 164 165 // pitchforkValue returns a map of keyword:value pairs in same index 166 func (i *Iterator) pitchforkValue() (map[string]interface{}, bool) { 167 values := make(map[string]interface{}, len(i.payloads)) 168 169 for _, p := range i.payloads { 170 if !p.next() { 171 return nil, false 172 } 173 values[p.name] = p.value() 174 p.incrementPosition() 175 } 176 i.position++ 177 return values, true 178 } 179 180 // clusterbombValue returns a combination of all input pairs in key:value format. 181 func (i *Iterator) clusterbombValue() (map[string]interface{}, bool) { 182 if i.position >= i.total { 183 return nil, false 184 } 185 values := make(map[string]interface{}, len(i.payloads)) 186 187 // Should we signal the next InputProvider in the slice to increment 188 signalNext := false 189 first := true 190 for index, p := range i.payloads { 191 if signalNext { 192 p.incrementPosition() 193 signalNext = false 194 } 195 if !p.next() { 196 // No more inputs in this input provider 197 if index == i.msbIterator { 198 // Reset all previous wordlists and increment the msb counter 199 i.msbIterator++ 200 i.clusterbombIteratorReset() 201 // Start again 202 return i.clusterbombValue() 203 } 204 p.resetPosition() 205 signalNext = true 206 } 207 values[p.name] = p.value() 208 if first { 209 p.incrementPosition() 210 first = false 211 } 212 } 213 i.position++ 214 return values, true 215 } 216 217 func (i *Iterator) clusterbombIteratorReset() { 218 for index, p := range i.payloads { 219 if index < i.msbIterator { 220 p.resetPosition() 221 } 222 if index == i.msbIterator { 223 p.incrementPosition() 224 } 225 } 226 } 227 228 // payloadIterator is a single instance of an iterator for a single payload list. 229 type payloadIterator struct { 230 index int 231 name string 232 values []string 233 } 234 235 // next returns true if there are more values in payload iterator 236 func (i *payloadIterator) next() bool { 237 return i.index < len(i.values) 238 } 239 240 // resetPosition resets the position of the payload iterator 241 func (i *payloadIterator) resetPosition() { 242 i.index = 0 243 } 244 245 // incrementPosition increments the position of the payload iterator 246 func (i *payloadIterator) incrementPosition() { 247 i.index++ 248 } 249 250 // value returns the value of the payload at an index 251 func (i *payloadIterator) value() string { 252 return i.values[i.index] 253 }