Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/common/generators/generators.go
2072 views
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
"maps"
8
9
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
10
"github.com/projectdiscovery/nuclei/v3/pkg/types"
11
)
12
13
// PayloadGenerator is the generator struct for generating payloads
14
type PayloadGenerator struct {
15
Type AttackType
16
catalog catalog.Catalog
17
payloads map[string][]string
18
options *types.Options
19
}
20
21
// New creates a new generator structure for payload generation
22
func New(payloads map[string]interface{}, attackType AttackType, templatePath string, catalog catalog.Catalog, customAttackType string, opts *types.Options) (*PayloadGenerator, error) {
23
if attackType.String() == "" {
24
attackType = BatteringRamAttack
25
}
26
27
// Resolve payload paths if they are files.
28
payloadsFinal := make(map[string]interface{})
29
for payloadName, v := range payloads {
30
switch value := v.(type) {
31
case map[interface{}]interface{}:
32
values, err := parsePayloadsWithAggression(payloadName, value, opts.FuzzAggressionLevel)
33
if err != nil {
34
return nil, errors.Wrap(err, "could not parse payloads with aggression")
35
}
36
maps.Copy(payloadsFinal, values)
37
default:
38
payloadsFinal[payloadName] = v
39
}
40
}
41
42
generator := &PayloadGenerator{catalog: catalog, options: opts}
43
if err := generator.validate(payloadsFinal, templatePath); err != nil {
44
return nil, err
45
}
46
47
compiled, err := generator.loadPayloads(payloadsFinal, templatePath)
48
if err != nil {
49
return nil, err
50
}
51
generator.Type = attackType
52
generator.payloads = compiled
53
54
if customAttackType != "" {
55
attackTypeNew, err := toAttackType(customAttackType)
56
if err != nil {
57
return nil, errors.Wrap(err, "could not parse custom attack-type")
58
}
59
generator.Type = attackTypeNew
60
}
61
// Validate the batteringram payload set
62
if attackType == BatteringRamAttack {
63
if len(payloads) != 1 {
64
return nil, errors.New("batteringram must have single payload set")
65
}
66
}
67
return generator, nil
68
}
69
70
type aggressionLevelToPayloads struct {
71
Low []interface{}
72
Medium []interface{}
73
High []interface{}
74
}
75
76
// parsePayloadsWithAggression parses the payloads with the aggression level
77
//
78
// Three agression are supported -
79
// - low
80
// - medium
81
// - high
82
//
83
// low is the default level. If medium is specified, all templates from
84
// low and medium are executed. Similarly with high, including all templates
85
// from low, medium, high.
86
func parsePayloadsWithAggression(name string, v map[interface{}]interface{}, agression string) (map[string]interface{}, error) {
87
payloadsLevels := &aggressionLevelToPayloads{}
88
89
for k, v := range v {
90
if _, ok := v.([]interface{}); !ok {
91
return nil, errors.Errorf("only lists are supported for aggression levels payloads")
92
}
93
var ok bool
94
switch k {
95
case "low":
96
payloadsLevels.Low, ok = v.([]interface{})
97
case "medium":
98
payloadsLevels.Medium, ok = v.([]interface{})
99
case "high":
100
payloadsLevels.High, ok = v.([]interface{})
101
default:
102
return nil, errors.Errorf("invalid aggression level %s specified for %s", k, name)
103
}
104
if !ok {
105
return nil, errors.Errorf("invalid aggression level %s specified for %s", k, name)
106
}
107
}
108
109
payloads := make(map[string]interface{})
110
switch agression {
111
case "low":
112
payloads[name] = payloadsLevels.Low
113
case "medium":
114
payloads[name] = append(payloadsLevels.Low, payloadsLevels.Medium...)
115
case "high":
116
payloads[name] = append(payloadsLevels.Low, payloadsLevels.Medium...)
117
payloads[name] = append(payloads[name].([]interface{}), payloadsLevels.High...)
118
default:
119
return nil, errors.Errorf("invalid aggression level %s specified for %s", agression, name)
120
}
121
return payloads, nil
122
}
123
124
// Iterator is a single instance of an iterator for a generator structure
125
type Iterator struct {
126
Type AttackType
127
position int
128
msbIterator int
129
total int
130
payloads []*payloadIterator
131
}
132
133
// NewIterator creates a new iterator for the payloads generator
134
func (g *PayloadGenerator) NewIterator() *Iterator {
135
var payloads []*payloadIterator
136
137
for name, values := range g.payloads {
138
payloads = append(payloads, &payloadIterator{name: name, values: values})
139
}
140
iterator := &Iterator{
141
Type: g.Type,
142
payloads: payloads,
143
}
144
iterator.total = iterator.Total()
145
return iterator
146
}
147
148
// Reset resets the iterator back to its initial value
149
func (i *Iterator) Reset() {
150
i.position = 0
151
i.msbIterator = 0
152
153
for _, payload := range i.payloads {
154
payload.resetPosition()
155
}
156
}
157
158
// Remaining returns the amount of requests left for the generator.
159
func (i *Iterator) Remaining() int {
160
return i.total - i.position
161
}
162
163
// Total returns the amount of input combinations available
164
func (i *Iterator) Total() int {
165
count := 0
166
switch i.Type {
167
case BatteringRamAttack:
168
for _, p := range i.payloads {
169
count += len(p.values)
170
}
171
case PitchForkAttack:
172
count = len(i.payloads[0].values)
173
for _, p := range i.payloads {
174
if count > len(p.values) {
175
count = len(p.values)
176
}
177
}
178
case ClusterBombAttack:
179
count = 1
180
for _, p := range i.payloads {
181
count *= len(p.values)
182
}
183
}
184
return count
185
}
186
187
// Value returns the next value for an iterator
188
func (i *Iterator) Value() (map[string]interface{}, bool) {
189
switch i.Type {
190
case BatteringRamAttack:
191
return i.batteringRamValue()
192
case PitchForkAttack:
193
return i.pitchforkValue()
194
case ClusterBombAttack:
195
return i.clusterbombValue()
196
default:
197
return i.batteringRamValue()
198
}
199
}
200
201
// batteringRamValue returns a list of all payloads for the iterator
202
func (i *Iterator) batteringRamValue() (map[string]interface{}, bool) {
203
values := make(map[string]interface{}, 1)
204
205
currentIndex := i.msbIterator
206
payload := i.payloads[currentIndex]
207
if !payload.next() {
208
i.msbIterator++
209
if i.msbIterator == len(i.payloads) {
210
return nil, false
211
}
212
return i.batteringRamValue()
213
}
214
values[payload.name] = payload.value()
215
payload.incrementPosition()
216
i.position++
217
return values, true
218
}
219
220
// pitchforkValue returns a map of keyword:value pairs in same index
221
func (i *Iterator) pitchforkValue() (map[string]interface{}, bool) {
222
values := make(map[string]interface{}, len(i.payloads))
223
224
for _, p := range i.payloads {
225
if !p.next() {
226
return nil, false
227
}
228
values[p.name] = p.value()
229
p.incrementPosition()
230
}
231
i.position++
232
return values, true
233
}
234
235
// clusterbombValue returns a combination of all input pairs in key:value format.
236
func (i *Iterator) clusterbombValue() (map[string]interface{}, bool) {
237
if i.position >= i.total {
238
return nil, false
239
}
240
values := make(map[string]interface{}, len(i.payloads))
241
242
// Should we signal the next InputProvider in the slice to increment
243
signalNext := false
244
first := true
245
for index, p := range i.payloads {
246
if signalNext {
247
p.incrementPosition()
248
signalNext = false
249
}
250
if !p.next() {
251
// No more inputs in this input provider
252
if index == i.msbIterator {
253
// Reset all previous wordlists and increment the msb counter
254
i.msbIterator++
255
i.clusterbombIteratorReset()
256
// Start again
257
return i.clusterbombValue()
258
}
259
p.resetPosition()
260
signalNext = true
261
}
262
values[p.name] = p.value()
263
if first {
264
p.incrementPosition()
265
first = false
266
}
267
}
268
i.position++
269
return values, true
270
}
271
272
func (i *Iterator) clusterbombIteratorReset() {
273
for index, p := range i.payloads {
274
if index < i.msbIterator {
275
p.resetPosition()
276
}
277
if index == i.msbIterator {
278
p.incrementPosition()
279
}
280
}
281
}
282
283
// payloadIterator is a single instance of an iterator for a single payload list.
284
type payloadIterator struct {
285
index int
286
name string
287
values []string
288
}
289
290
// next returns true if there are more values in payload iterator
291
func (i *payloadIterator) next() bool {
292
return i.index < len(i.values)
293
}
294
295
// resetPosition resets the position of the payload iterator
296
func (i *payloadIterator) resetPosition() {
297
i.index = 0
298
}
299
300
// incrementPosition increments the position of the payload iterator
301
func (i *payloadIterator) incrementPosition() {
302
i.index++
303
}
304
305
// value returns the value of the payload at an index
306
func (i *payloadIterator) value() string {
307
return i.values[i.index]
308
}
309
310