nuclei/v2/pkg/protocols/http/build_request_test.go
Ice3man543 ef5b476c6d Reusing dynamically extracted values as iterators in http request
This PR adds the support in http module to iterate over the dynamically extracted data from extractors
and use it in other requests. This allows nuclei to follow links on pages, do operations with
multiple versions of the same extracted value, etc.
2021-11-24 21:08:08 +05:30

264 lines
11 KiB
Go

package http
import (
"net/url"
"testing"
"time"
"github.com/projectdiscovery/nuclei/v2/pkg/model"
"github.com/projectdiscovery/nuclei/v2/pkg/model/types/severity"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/interactsh"
"github.com/projectdiscovery/nuclei/v2/pkg/testutils"
"github.com/stretchr/testify/require"
)
func TestBaseURLWithTemplatePrefs(t *testing.T) {
baseURL := "http://localhost:53/test"
parsed, _ := url.Parse(baseURL)
data := "{{BaseURL}}:8000/newpath"
data, parsed = baseURLWithTemplatePrefs(data, parsed)
require.Equal(t, "http://localhost:8000/test", parsed.String(), "could not get correct value")
require.Equal(t, "{{BaseURL}}/newpath", data, "could not get correct data")
}
func TestVariables(t *testing.T) {
baseURL := "http://localhost:9001/test/123"
parsed, _ := url.Parse(baseURL)
values := generateVariables(parsed, true)
require.Equal(t, values["BaseURL"], parsed.String(), "incorrect baseurl")
require.Equal(t, values["RootURL"], "http://localhost:9001", "incorrect rootURL")
require.Equal(t, values["Host"], "localhost", "incorrect domain name")
require.Equal(t, values["Path"], "/test", "incorrect path")
require.Equal(t, values["File"], "123", "incorrect file")
require.Equal(t, values["Port"], "9001", "incorrect port number")
require.Equal(t, values["Scheme"], "http", "incorrect scheme")
require.Equal(t, values["Hostname"], "localhost:9001", "incorrect hostname")
baseURL = "https://example.com"
parsed, _ = url.Parse(baseURL)
values = generateVariables(parsed, false)
require.Equal(t, values["BaseURL"], parsed.String(), "incorrect baseurl")
require.Equal(t, values["Host"], "example.com", "incorrect domain name")
require.Equal(t, values["RootURL"], "https://example.com", "incorrect rootURL")
require.Equal(t, values["Path"], "", "incorrect path")
require.Equal(t, values["Port"], "443", "incorrect port number")
require.Equal(t, values["Scheme"], "https", "incorrect scheme")
require.Equal(t, values["Hostname"], "example.com", "incorrect hostname")
baseURL = "ftp://foobar.com/"
parsed, _ = url.Parse(baseURL)
values = generateVariables(parsed, true)
require.Equal(t, values["BaseURL"], parsed.String(), "incorrect baseurl")
require.Equal(t, values["Host"], "foobar.com", "incorrect domain name")
require.Equal(t, values["RootURL"], "ftp://foobar.com", "incorrect rootURL")
require.Equal(t, values["Path"], "", "incorrect path")
require.Equal(t, values["Port"], "", "incorrect port number") // Unsupported protocol results in a blank port
require.Equal(t, values["Scheme"], "ftp", "incorrect scheme")
require.Equal(t, values["Hostname"], "foobar.com", "incorrect hostname")
}
func TestMakeRequestFromModal(t *testing.T) {
options := testutils.DefaultOptions
testutils.Init(options)
templateID := "testing-http"
request := &Request{
ID: templateID,
Name: "testing",
Path: []string{"{{BaseURL}}/login.php"},
Method: HTTPMethodTypeHolder{MethodType: HTTPPost},
Body: "username=test&password=pass",
Headers: map[string]string{
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": "1",
},
}
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID,
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
})
err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile http request")
generator := request.newGenerator()
inputData, payloads, _ := generator.nextValue()
req, err := generator.Make("https://example.com", inputData, payloads, map[string]interface{}{})
require.Nil(t, err, "could not make http request")
bodyBytes, _ := req.request.BodyBytes()
require.Equal(t, "/login.php", req.request.URL.Path, "could not get correct request path")
require.Equal(t, "username=test&password=pass", string(bodyBytes), "could not get correct request body")
}
func TestMakeRequestFromModalTrimSuffixSlash(t *testing.T) {
options := testutils.DefaultOptions
testutils.Init(options)
templateID := "testing-http"
request := &Request{
ID: templateID,
Name: "testing",
Path: []string{"{{BaseURL}}?query=example"},
Method: HTTPMethodTypeHolder{MethodType: HTTPGet},
}
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID,
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
})
err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile http request")
generator := request.newGenerator()
inputData, payloads, _ := generator.nextValue()
req, err := generator.Make("https://example.com/test.php", inputData, payloads, map[string]interface{}{})
require.Nil(t, err, "could not make http request")
require.Equal(t, "https://example.com/test.php?query=example", req.request.URL.String(), "could not get correct request path")
generator = request.newGenerator()
inputData, payloads, _ = generator.nextValue()
req, err = generator.Make("https://example.com/test/", inputData, payloads, map[string]interface{}{})
require.Nil(t, err, "could not make http request")
require.Equal(t, "https://example.com/test/?query=example", req.request.URL.String(), "could not get correct request path")
}
func TestMakeRequestFromRawWithPayloads(t *testing.T) {
options := testutils.DefaultOptions
testutils.Init(options)
templateID := "testing-http"
request := &Request{
ID: templateID,
Name: "testing",
Payloads: map[string]interface{}{
"username": []string{"admin"},
"password": []string{"admin", "guest", "password", "test", "12345", "123456"},
},
AttackType: generators.AttackTypeHolder{Value: generators.ClusterbombAttack},
Raw: []string{`GET /manager/html HTTP/1.1
Host: {{Hostname}}
User-Agent: Nuclei - Open-source project (github.com/projectdiscovery/nuclei)
Connection: close
Authorization: Basic {{username + ':' + password}}
Accept-Encoding: gzip`},
}
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID,
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
})
err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile http request")
generator := request.newGenerator()
inputData, payloads, _ := generator.nextValue()
req, err := generator.Make("https://example.com", inputData, payloads, map[string]interface{}{})
require.Nil(t, err, "could not make http request")
authorization := req.request.Header.Get("Authorization")
require.Equal(t, "Basic admin:admin", authorization, "could not get correct authorization headers from raw")
inputData, payloads, _ = generator.nextValue()
req, err = generator.Make("https://example.com", inputData, payloads, map[string]interface{}{})
require.Nil(t, err, "could not make http request")
authorization = req.request.Header.Get("Authorization")
require.Equal(t, "Basic admin:guest", authorization, "could not get correct authorization headers from raw")
}
func TestMakeRequestFromRawPayloadExpressions(t *testing.T) {
options := testutils.DefaultOptions
testutils.Init(options)
templateID := "testing-http"
request := &Request{
ID: templateID,
Name: "testing",
Payloads: map[string]interface{}{
"username": []string{"admin"},
"password": []string{"admin", "guest", "password", "test", "12345", "123456"},
},
AttackType: generators.AttackTypeHolder{Value: generators.ClusterbombAttack},
Raw: []string{`GET /manager/html HTTP/1.1
Host: {{Hostname}}
User-Agent: Nuclei - Open-source project (github.com/projectdiscovery/nuclei)
Connection: close
Authorization: Basic {{base64(username + ':' + password)}}
Accept-Encoding: gzip`},
}
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID,
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
})
err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile http request")
generator := request.newGenerator()
inputData, payloads, _ := generator.nextValue()
req, err := generator.Make("https://example.com", inputData, payloads, map[string]interface{}{})
require.Nil(t, err, "could not make http request")
authorization := req.request.Header.Get("Authorization")
require.Equal(t, "Basic YWRtaW46YWRtaW4=", authorization, "could not get correct authorization headers from raw")
inputData, payloads, _ = generator.nextValue()
req, err = generator.Make("https://example.com", inputData, payloads, map[string]interface{}{})
require.Nil(t, err, "could not make http request")
authorization = req.request.Header.Get("Authorization")
require.Equal(t, "Basic YWRtaW46Z3Vlc3Q=", authorization, "could not get correct authorization headers from raw")
}
func TestMakeRequestFromModelUniqueInteractsh(t *testing.T) {
options := testutils.DefaultOptions
testutils.Init(options)
templateID := "testing-unique-interactsh"
request := &Request{
ID: templateID,
Name: "testing",
Path: []string{"{{BaseURL}}/?u=http://{{interactsh-url}}/&href=http://{{interactsh-url}}/&action=http://{{interactsh-url}}/&host={{interactsh-url}}"},
Method: HTTPMethodTypeHolder{MethodType: HTTPGet},
}
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
ID: templateID,
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "test"},
})
err := request.Compile(executerOpts)
require.Nil(t, err, "could not compile http request")
generator := request.newGenerator()
generator.options.Interactsh, err = interactsh.New(&interactsh.Options{
ServerURL: options.InteractshURL,
CacheSize: int64(options.InteractionsCacheSize),
Eviction: time.Duration(options.InteractionsEviction) * time.Second,
ColldownPeriod: time.Duration(options.InteractionsCooldownPeriod) * time.Second,
PollDuration: time.Duration(options.InteractionsPollDuration) * time.Second,
})
require.Nil(t, err, "could not create interactsh client")
inputData, payloads, _ := generator.nextValue()
got, err := generator.Make("https://example.com", inputData, payloads, map[string]interface{}{})
require.Nil(t, err, "could not make http request")
// check if all the interactsh markers are replaced with unique urls
require.NotContains(t, got.request.URL.String(), "{{interactsh-url}}", "could not get correct interactsh url")
// check the length of returned urls
require.Equal(t, len(got.interactshURLs), 4, "could not get correct interactsh url")
// check if the interactsh urls are unique
require.True(t, areUnique(got.interactshURLs), "interactsh urls are not unique")
}
// areUnique checks if the elements of string slice are unique
func areUnique(elements []string) bool {
encountered := map[string]bool{}
for v := range elements {
if encountered[elements[v]] {
return false
}
encountered[elements[v]] = true
}
return true
}