Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
projectdiscovery
GitHub Repository: projectdiscovery/nuclei
Path: blob/dev/pkg/protocols/http/request_test.go
2070 views
1
package http
2
3
import (
4
"context"
5
"fmt"
6
"net/http"
7
"net/http/httptest"
8
"testing"
9
10
"github.com/stretchr/testify/require"
11
12
"github.com/projectdiscovery/nuclei/v3/pkg/model"
13
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
14
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
15
"github.com/projectdiscovery/nuclei/v3/pkg/operators/extractors"
16
"github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers"
17
"github.com/projectdiscovery/nuclei/v3/pkg/output"
18
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
19
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
20
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
21
)
22
23
func TestHTTPExtractMultipleReuse(t *testing.T) {
24
options := testutils.DefaultOptions
25
26
testutils.Init(options)
27
templateID := "testing-http"
28
request := &Request{
29
ID: templateID,
30
Raw: []string{
31
`GET /robots.txt HTTP/1.1
32
Host: {{Hostname}}
33
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0
34
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
35
Accept-Language: en-US,en;q=0.5
36
`,
37
38
`GET {{endpoint}} HTTP/1.1
39
Host: {{Hostname}}
40
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0
41
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
42
Accept-Language: en-US,en;q=0.5
43
`,
44
},
45
Operators: operators.Operators{
46
Matchers: []*matchers.Matcher{{
47
Part: "body",
48
Type: matchers.MatcherTypeHolder{MatcherType: matchers.WordsMatcher},
49
Words: []string{"match /a", "match /b", "match /c"},
50
}},
51
Extractors: []*extractors.Extractor{{
52
Part: "body",
53
Name: "endpoint",
54
Type: extractors.ExtractorTypeHolder{ExtractorType: extractors.RegexExtractor},
55
Regex: []string{"(?m)/([a-zA-Z0-9-_/\\\\]+)"},
56
Internal: true,
57
}},
58
},
59
IterateAll: true,
60
}
61
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
62
switch r.URL.Path {
63
case "/robots.txt":
64
_, _ = fmt.Fprintf(w, `User-agent: Googlebot
65
Disallow: /a
66
Disallow: /b
67
Disallow: /c`)
68
default:
69
_, _ = fmt.Fprintf(w, `match %v`, r.URL.Path)
70
}
71
}))
72
defer ts.Close()
73
74
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
75
ID: templateID,
76
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
77
})
78
79
err := request.Compile(executerOpts)
80
require.Nil(t, err, "could not compile network request")
81
82
var finalEvent *output.InternalWrappedEvent
83
var matchCount int
84
t.Run("test", func(t *testing.T) {
85
metadata := make(output.InternalEvent)
86
previous := make(output.InternalEvent)
87
ctxArgs := contextargs.NewWithInput(context.Background(), ts.URL)
88
err := request.ExecuteWithResults(ctxArgs, metadata, previous, func(event *output.InternalWrappedEvent) {
89
if event.OperatorsResult != nil && event.OperatorsResult.Matched {
90
matchCount++
91
}
92
finalEvent = event
93
})
94
require.Nil(t, err, "could not execute network request")
95
})
96
require.NotNil(t, finalEvent, "could not get event output from request")
97
require.Equal(t, 3, matchCount, "could not get correct match count")
98
}
99
100
func TestDisableTE(t *testing.T) {
101
options := testutils.DefaultOptions
102
103
testutils.Init(options)
104
templateID := "http-disable-transfer-encoding"
105
106
// in raw request format
107
request := &Request{
108
ID: templateID,
109
Raw: []string{
110
`POST / HTTP/1.1
111
Host: {{Hostname}}
112
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0
113
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
114
Accept-Language: en-US,en;q=0.5
115
116
login=1&username=admin&password=admin
117
`,
118
},
119
Operators: operators.Operators{
120
Matchers: []*matchers.Matcher{{
121
Type: matchers.MatcherTypeHolder{MatcherType: matchers.StatusMatcher},
122
Status: []int{200},
123
}},
124
},
125
}
126
127
// in base request format
128
request2 := &Request{
129
ID: templateID,
130
Method: HTTPMethodTypeHolder{MethodType: HTTPPost},
131
Path: []string{"{{BaseURL}}"},
132
Body: "login=1&username=admin&password=admin",
133
Operators: operators.Operators{
134
Matchers: []*matchers.Matcher{{
135
Type: matchers.MatcherTypeHolder{MatcherType: matchers.StatusMatcher},
136
Status: []int{200},
137
}},
138
},
139
}
140
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
141
if len(r.TransferEncoding) > 0 || r.ContentLength <= 0 {
142
t.Error("Transfer-Encoding header should not be set")
143
}
144
}))
145
defer ts.Close()
146
147
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
148
ID: templateID,
149
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
150
})
151
152
err := request.Compile(executerOpts)
153
require.Nil(t, err, "could not compile http raw request")
154
155
err = request2.Compile(executerOpts)
156
require.Nil(t, err, "could not compile http base request")
157
158
var finalEvent *output.InternalWrappedEvent
159
var matchCount int
160
t.Run("test", func(t *testing.T) {
161
metadata := make(output.InternalEvent)
162
previous := make(output.InternalEvent)
163
ctxArgs := contextargs.NewWithInput(context.Background(), ts.URL)
164
err := request.ExecuteWithResults(ctxArgs, metadata, previous, func(event *output.InternalWrappedEvent) {
165
if event.OperatorsResult != nil && event.OperatorsResult.Matched {
166
matchCount++
167
}
168
finalEvent = event
169
})
170
require.Nil(t, err, "could not execute network request")
171
})
172
173
t.Run("test2", func(t *testing.T) {
174
metadata := make(output.InternalEvent)
175
previous := make(output.InternalEvent)
176
ctxArgs := contextargs.NewWithInput(context.Background(), ts.URL)
177
err := request2.ExecuteWithResults(ctxArgs, metadata, previous, func(event *output.InternalWrappedEvent) {
178
if event.OperatorsResult != nil && event.OperatorsResult.Matched {
179
matchCount++
180
}
181
finalEvent = event
182
})
183
require.Nil(t, err, "could not execute network request")
184
})
185
186
require.NotNil(t, finalEvent, "could not get event output from request")
187
require.Equal(t, 2, matchCount, "could not get correct match count")
188
}
189
190
// consult @Ice3man543 before making any breaking changes to this test (context: vuln_hash)
191
func TestReqURLPattern(t *testing.T) {
192
options := testutils.DefaultOptions
193
194
// assume this was a preprocessor
195
// {{randstr}} => 2eNU2kbrOcUDzhnUL1RGvSo1it7
196
testutils.Init(options)
197
templateID := "testing-http"
198
request := &Request{
199
ID: templateID,
200
Raw: []string{
201
`GET /{{rand_char("abc")}}/{{interactsh-url}}/123?query={{rand_int(1, 10)}}&data=2eNU2kbrOcUDzhnUL1RGvSo1it7 HTTP/1.1
202
Host: {{Hostname}}
203
User-Agent: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:80.0) Gecko/20100101 Firefox/80.0
204
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
205
Accept-Language: en-US,en;q=0.5
206
`,
207
},
208
Operators: operators.Operators{
209
Matchers: []*matchers.Matcher{{
210
Type: matchers.MatcherTypeHolder{MatcherType: matchers.DSLMatcher},
211
DSL: []string{"true"},
212
}},
213
},
214
IterateAll: true,
215
}
216
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
217
// always return 200
218
w.WriteHeader(200)
219
_, _ = w.Write([]byte(`match`))
220
}))
221
defer ts.Close()
222
223
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
224
ID: templateID,
225
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
226
})
227
client, _ := interactsh.New(interactsh.DefaultOptions(executerOpts.Output, nil, executerOpts.Progress))
228
executerOpts.Interactsh = client
229
defer client.Close()
230
executerOpts.ExportReqURLPattern = true
231
232
// this is how generated constants are added to template
233
// generated constants are preprocessors that are executed while loading once
234
executerOpts.Constants = map[string]interface{}{
235
"{{randstr}}": "2eNU2kbrOcUDzhnUL1RGvSo1it7",
236
}
237
238
err := request.Compile(executerOpts)
239
require.Nil(t, err, "could not compile network request")
240
241
var finalEvent *output.InternalWrappedEvent
242
var matchCount int
243
t.Run("test", func(t *testing.T) {
244
metadata := make(output.InternalEvent)
245
previous := make(output.InternalEvent)
246
ctxArgs := contextargs.NewWithInput(context.Background(), ts.URL)
247
err := request.ExecuteWithResults(ctxArgs, metadata, previous, func(event *output.InternalWrappedEvent) {
248
if event.OperatorsResult != nil && event.OperatorsResult.Matched {
249
matchCount++
250
}
251
finalEvent = event
252
})
253
require.Nil(t, err, "could not execute network request")
254
})
255
require.NotNil(t, finalEvent, "could not get event output from request")
256
require.Equal(t, 1, matchCount, "could not get correct match count")
257
require.NotEmpty(t, finalEvent.Results[0].ReqURLPattern, "could not get req url pattern")
258
require.Equal(t, `/{{rand_char("abc")}}/{{interactsh-url}}/123?query={{rand_int(1, 10)}}&data={{randstr}}`, finalEvent.Results[0].ReqURLPattern)
259
}
260
261