Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/cmd/integration-test/fuzz.go
2070 views
1
package main
2
3
import (
4
"fmt"
5
"net/http"
6
"net/http/httptest"
7
"net/url"
8
9
"github.com/julienschmidt/httprouter"
10
"github.com/projectdiscovery/nuclei/v3/pkg/output"
11
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
12
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
13
)
14
15
const (
16
targetFile = "fuzz/testData/ginandjuice.proxify.yaml"
17
)
18
19
var fuzzingTestCases = []TestCaseInfo{
20
{Path: "fuzz/fuzz-mode.yaml", TestCase: &fuzzModeOverride{}},
21
{Path: "fuzz/fuzz-multi-mode.yaml", TestCase: &fuzzMultipleMode{}},
22
{Path: "fuzz/fuzz-type.yaml", TestCase: &fuzzTypeOverride{}},
23
{Path: "fuzz/fuzz-query.yaml", TestCase: &httpFuzzQuery{}},
24
{Path: "fuzz/fuzz-headless.yaml", TestCase: &HeadlessFuzzingQuery{}},
25
// for fuzzing we should prioritize adding test case related backend
26
// logic in fuzz playground server instead of adding them here
27
{Path: "fuzz/fuzz-query-num-replace.yaml", TestCase: &genericFuzzTestCase{expectedResults: 2}},
28
{Path: "fuzz/fuzz-host-header-injection.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}},
29
{Path: "fuzz/fuzz-path-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}},
30
{Path: "fuzz/fuzz-cookie-error-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}},
31
{Path: "fuzz/fuzz-body-json-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}},
32
{Path: "fuzz/fuzz-body-multipart-form-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}},
33
{Path: "fuzz/fuzz-body-params-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}},
34
{Path: "fuzz/fuzz-body-xml-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 1}},
35
{Path: "fuzz/fuzz-body-generic-sqli.yaml", TestCase: &genericFuzzTestCase{expectedResults: 4}},
36
}
37
38
type genericFuzzTestCase struct {
39
expectedResults int
40
}
41
42
func (g *genericFuzzTestCase) Execute(filePath string) error {
43
results, err := testutils.RunNucleiWithArgsAndGetResults(debug, "-t", filePath, "-l", targetFile, "-im", "yaml")
44
if err != nil {
45
return err
46
}
47
return expectResultsCount(results, g.expectedResults)
48
}
49
50
type httpFuzzQuery struct{}
51
52
// Execute executes a test case and returns an error if occurred
53
func (h *httpFuzzQuery) Execute(filePath string) error {
54
router := httprouter.New()
55
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
56
w.Header().Set("Content-Type", "text/html")
57
value := r.URL.Query().Get("id")
58
_, _ = fmt.Fprintf(w, "This is test matcher text: %v", value)
59
})
60
ts := httptest.NewTLSServer(router)
61
defer ts.Close()
62
63
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/?id=example", debug, "-fuzz")
64
if err != nil {
65
return err
66
}
67
return expectResultsCount(results, 1)
68
}
69
70
type fuzzModeOverride struct{}
71
72
// Execute executes a test case and returns an error if occurred
73
func (h *fuzzModeOverride) Execute(filePath string) error {
74
router := httprouter.New()
75
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
76
w.Header().Set("Content-Type", "text/html")
77
value := r.URL.Query().Get("id")
78
_, _ = fmt.Fprintf(w, "This is test matcher text: %v", value)
79
})
80
ts := httptest.NewTLSServer(router)
81
defer ts.Close()
82
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"/?id=example&name=nuclei", debug, "-fuzzing-mode", "single", "-jsonl", "-fuzz")
83
if err != nil {
84
return err
85
}
86
if err = expectResultsCount(results, 1); err != nil {
87
return err
88
}
89
var event output.ResultEvent
90
err = json.Unmarshal([]byte(results[0]), &event)
91
if err != nil {
92
return fmt.Errorf("could not unmarshal event: %s", err)
93
}
94
95
// Check whether the matched value url query params are correct
96
// default fuzzing mode is multiple in template, so all query params should be fuzzed
97
// but using -fm flag we are overriding fuzzing mode to single,
98
// so only one query param should be fuzzed, and the other should be the same
99
100
//parse url to get query params
101
matchedURL, err := url.Parse(event.Matched)
102
if err != nil {
103
return err
104
}
105
values, err := url.ParseQuery(matchedURL.RawQuery)
106
if err != nil {
107
return err
108
}
109
if values.Get("name") != "nuclei" {
110
return fmt.Errorf("expected fuzzing should not override the name nuclei got %s", values.Get("name"))
111
}
112
return nil
113
}
114
115
type fuzzTypeOverride struct{}
116
117
// Execute executes a test case and returns an error if occurred
118
func (h *fuzzTypeOverride) Execute(filePath string) error {
119
router := httprouter.New()
120
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
121
w.Header().Set("Content-Type", "text/html")
122
value := r.URL.Query().Get("id")
123
_, _ = fmt.Fprintf(w, "This is test matcher text: %v", value)
124
})
125
ts := httptest.NewTLSServer(router)
126
defer ts.Close()
127
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"?id=example", debug, "-fuzzing-type", "replace", "-jsonl", "-fuzz")
128
if err != nil {
129
return err
130
}
131
if err = expectResultsCount(results, 1); err != nil {
132
return err
133
}
134
var event output.ResultEvent
135
err = json.Unmarshal([]byte(results[0]), &event)
136
if err != nil {
137
return fmt.Errorf("could not unmarshal event: %s", err)
138
}
139
140
// check whether the matched url query params are fuzzed
141
// default fuzzing type in template is postfix but we are overriding it to replace
142
// so the matched url query param should be replaced with fuzz-word
143
144
//parse url to get query params
145
matchedURL, err := url.Parse(event.Matched)
146
if err != nil {
147
return err
148
}
149
values, err := url.ParseQuery(matchedURL.RawQuery)
150
if err != nil {
151
return err
152
}
153
if values.Get("id") != "fuzz-word" {
154
return fmt.Errorf("expected id to be fuzz-word, got %s", values.Get("id"))
155
}
156
return nil
157
}
158
159
// HeadlessFuzzingQuery tests fuzzing is working not in headless mode
160
type HeadlessFuzzingQuery struct{}
161
162
// Execute executes a test case and returns an error if occurred
163
func (h *HeadlessFuzzingQuery) Execute(filePath string) error {
164
router := httprouter.New()
165
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
166
resp := fmt.Sprintf("<html><body>%s</body></html>", r.URL.Query().Get("url"))
167
_, _ = fmt.Fprint(w, resp)
168
})
169
ts := httptest.NewTLSServer(router)
170
defer ts.Close()
171
172
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"?url=https://scanme.sh", debug, "-headless", "-fuzz")
173
if err != nil {
174
return err
175
}
176
return expectResultsCount(got, 2)
177
}
178
179
type fuzzMultipleMode struct{}
180
181
// Execute executes a test case and returns an error if occurred
182
func (h *fuzzMultipleMode) Execute(filePath string) error {
183
router := httprouter.New()
184
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
185
xClientId := r.Header.Get("X-Client-Id")
186
xSecretId := r.Header.Get("X-Secret-Id")
187
if xClientId != "nuclei-v3" || xSecretId != "nuclei-v3" {
188
w.WriteHeader(http.StatusUnauthorized)
189
return
190
}
191
w.Header().Set("Content-Type", "text/html")
192
resp := fmt.Sprintf("<html><body><h1>This is multi-mode fuzzing test: %v <h1></body></html>", xClientId)
193
_, _ = fmt.Fprint(w, resp)
194
})
195
ts := httptest.NewTLSServer(router)
196
defer ts.Close()
197
198
got, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL+"?url=https://scanme.sh", debug, "-jsonl", "-fuzz")
199
if err != nil {
200
return err
201
}
202
return expectResultsCount(got, 1)
203
}
204
205