Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding interactsh support to sni #3276

Merged
merged 1 commit into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 29 additions & 19 deletions v2/pkg/protocols/common/interactsh/interactsh.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,17 +239,17 @@ func (c *Client) processInteractionForRequest(interaction *server.Interaction, d
}

// URL returns a new URL that can be interacted with
func (c *Client) URL() string {
func (c *Client) URL() (string, error) {
c.firstTimeGroup.Do(func() {
if err := c.firstTimeInitializeClient(); err != nil {
gologger.Error().Msgf("Could not initialize interactsh client: %s", err)
}
})
if c.interactsh == nil {
return ""
return "", errors.New("interactsh client not initialized")
}
atomic.CompareAndSwapUint32(&c.generated, 0, 1)
return c.interactsh.URL()
return c.interactsh.URL(), nil
}

// Close closes the interactsh clients after waiting for cooldown period.
Expand All @@ -276,28 +276,38 @@ func (c *Client) Close() bool {
return c.matched
}

// ReplaceMarkers replaces the {{interactsh-url}} placeholders to actual
// URLs pointing to interactsh-server.
//
// It accepts data to replace as well as the URL to replace placeholders
// with generated uniquely for each request.
func (c *Client) ReplaceMarkers(data string, interactshURLs []string) (string, []string) {
for interactshURLMarkerRegex.Match([]byte(data)) {
url := c.URL()
interactshURLs = append(interactshURLs, url)
interactshURLMarker := interactshURLMarkerRegex.FindString(data)
if interactshURLMarker != "" {
// ReplaceMarkers replaces the default {{interactsh-url}} placeholders with interactsh urls
func (c *Client) Replace(data string, interactshURLs []string) (string, []string) {
return c.ReplaceWithMarker(data, interactshURLMarkerRegex, interactshURLs)
}

// ReplaceMarkers replaces the placeholders with interactsh urls and appends them to interactshURLs
func (c *Client) ReplaceWithMarker(data string, regex *regexp.Regexp, interactshURLs []string) (string, []string) {
for _, interactshURLMarker := range regex.FindAllString(data, -1) {
if url, err := c.NewURLWithData(interactshURLMarker); err == nil {
interactshURLs = append(interactshURLs, url)
data = strings.Replace(data, interactshURLMarker, url, 1)
urlIndex := strings.Index(url, ".")
if urlIndex == -1 {
continue
}
c.interactshURLs.Set(url, interactshURLMarker, defaultInteractionDuration)
}
}
return data, interactshURLs
}

func (c *Client) NewURL() (string, error) {
return c.NewURLWithData("")
}

func (c *Client) NewURLWithData(data string) (string, error) {
url, err := c.URL()
if err != nil {
return "", err
}
if url == "" {
return "", errors.New("empty interactsh url")
}
c.interactshURLs.Set(url, data, defaultInteractionDuration)
return url, nil
}

// MakePlaceholders does placeholders for interact URLs and other data to a map
func (c *Client) MakePlaceholders(urls []string, data map[string]interface{}) {
data["interactsh-server"] = c.getInteractServerHostname()
Expand Down
2 changes: 1 addition & 1 deletion v2/pkg/protocols/common/variables/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (variables *Variable) EvaluateWithInteractsh(values map[string]interface{},
variables.ForEach(func(key string, value interface{}) {
valueString := types.ToString(value)
if strings.Contains(valueString, "interactsh-url") {
valueString, interactURLs = interact.ReplaceMarkers(valueString, interactURLs)
valueString, interactURLs = interact.Replace(valueString, interactURLs)
}
result[key] = evaluateVariableValue(valueString, generators.MergeMaps(values, result), result)
})
Expand Down
2 changes: 1 addition & 1 deletion v2/pkg/protocols/headless/engine/page_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ func (p *Page) getActionArgWithValues(action *Action, arg string, values map[str
argValue = replaceWithValues(argValue, values)
if p.instance.interactsh != nil {
var interactshURLs []string
argValue, interactshURLs = p.instance.interactsh.ReplaceMarkers(argValue, p.InteractshURLs)
argValue, interactshURLs = p.instance.interactsh.Replace(argValue, p.InteractshURLs)
p.addInteractshURL(interactshURLs...)
}
return argValue
Expand Down
15 changes: 8 additions & 7 deletions v2/pkg/protocols/http/build_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ func (r *requestGenerator) Make(ctx context.Context, input *contextargs.Context,
isRawRequest := len(r.request.Raw) > 0
// replace interactsh variables with actual interactsh urls
if r.options.Interactsh != nil {
reqData, r.interactshURLs = r.options.Interactsh.ReplaceMarkers(reqData, []string{})
reqData, r.interactshURLs = r.options.Interactsh.Replace(reqData, []string{})
for payloadName, payloadValue := range payloads {
payloads[payloadName], r.interactshURLs = r.options.Interactsh.ReplaceMarkers(types.ToString(payloadValue), r.interactshURLs)
payloads[payloadName], r.interactshURLs = r.options.Interactsh.Replace(types.ToString(payloadValue), r.interactshURLs)
}
} else {
for payloadName, payloadValue := range payloads {
Expand Down Expand Up @@ -281,9 +281,10 @@ func (r *requestGenerator) generateRawRequest(ctx context.Context, rawRequest st
interactshURLs: r.interactshURLs,
}

if reqWithAnnotations, cancelFunc, hasAnnotations := r.request.parseAnnotations(rawRequest, req); hasAnnotations {
generatedRequest.request = reqWithAnnotations
generatedRequest.customCancelFunction = cancelFunc
if reqWithOverrides, hasAnnotations := r.request.parseAnnotations(rawRequest, req); hasAnnotations {
generatedRequest.request = reqWithOverrides.request
generatedRequest.customCancelFunction = reqWithOverrides.cancelFunc
generatedRequest.interactshURLs = append(generatedRequest.interactshURLs, reqWithOverrides.interactshURLs...)
}

return generatedRequest, nil
Expand All @@ -294,7 +295,7 @@ func (r *requestGenerator) fillRequest(req *retryablehttp.Request, values map[st
// Set the header values requested
for header, value := range r.request.Headers {
if r.options.Interactsh != nil {
value, r.interactshURLs = r.options.Interactsh.ReplaceMarkers(value, r.interactshURLs)
value, r.interactshURLs = r.options.Interactsh.Replace(value, r.interactshURLs)
}
value, err := expressions.Evaluate(value, values)
if err != nil {
Expand All @@ -315,7 +316,7 @@ func (r *requestGenerator) fillRequest(req *retryablehttp.Request, values map[st
if r.request.Body != "" {
body := r.request.Body
if r.options.Interactsh != nil {
body, r.interactshURLs = r.options.Interactsh.ReplaceMarkers(r.request.Body, r.interactshURLs)
body, r.interactshURLs = r.options.Interactsh.Replace(r.request.Body, r.interactshURLs)
}
body, err := expressions.Evaluate(body, values)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion v2/pkg/protocols/http/fuzz/parts.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func (rule *Rule) executeEvaluate(input *ExecuteRuleInput, key, value, payload s
"value": value,
})
firstpass, _ := expressions.Evaluate(payload, values)
interactData, interactshURLs := rule.options.Interactsh.ReplaceMarkers(firstpass, interactshURLs)
interactData, interactshURLs := rule.options.Interactsh.Replace(firstpass, interactshURLs)
evaluated, _ := expressions.Evaluate(interactData, values)
replaced := rule.executeReplaceRule(input, value, evaluated)
return replaced, interactshURLs
Expand Down
29 changes: 20 additions & 9 deletions v2/pkg/protocols/http/request_annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ func parseFlowAnnotations(rawRequest string) (flowMark, bool) {
return fm, hasFlowOveride
}

type annotationOverrides struct {
request *retryablehttp.Request
cancelFunc context.CancelFunc
interactshURLs []string
}

// parseAnnotations and override requests settings
func (r *Request) parseAnnotations(rawRequest string, request *retryablehttp.Request) (*retryablehttp.Request, context.CancelFunc, bool) {
var (
modified bool
cancelFunc context.CancelFunc
)
func (r *Request) parseAnnotations(rawRequest string, request *retryablehttp.Request) (overrides annotationOverrides, modified bool) {
// parse request for known ovverride annotations

// @Host:target
Expand Down Expand Up @@ -89,8 +91,14 @@ func (r *Request) parseAnnotations(rawRequest string, request *retryablehttp.Req
value = value[:idxForwardSlash]
}

if stringsutil.EqualFoldAny(value, "request.host") {
switch value {
case "request.host":
value = request.Host
case "interactsh-url":
if interactshURL, err := r.options.Interactsh.NewURLWithData("interactsh-url"); err == nil {
value = interactshURL
}
overrides.interactshURLs = append(overrides.interactshURLs, value)
}
ctx := context.WithValue(request.Context(), fastdialer.SniName, value)
request = request.Clone(ctx)
Expand All @@ -106,16 +114,19 @@ func (r *Request) parseAnnotations(rawRequest string, request *retryablehttp.Req
value := strings.TrimSpace(duration[1])
if parsed, err := time.ParseDuration(value); err == nil {
//nolint:govet // cancelled automatically by withTimeout
ctx, cancelFunc = context.WithTimeout(context.Background(), parsed)
ctx, overrides.cancelFunc = context.WithTimeout(context.Background(), parsed)
request = request.Clone(ctx)
}
} else {
//nolint:govet // cancelled automatically by withTimeout
ctx, cancelFunc = context.WithTimeout(context.Background(), time.Duration(r.options.Options.Timeout)*time.Second)
ctx, overrides.cancelFunc = context.WithTimeout(context.Background(), time.Duration(r.options.Options.Timeout)*time.Second)
request = request.Clone(ctx)
}
}
return request, cancelFunc, modified

overrides.request = request

return
}

func isHostPort(value string) bool {
Expand Down
12 changes: 6 additions & 6 deletions v2/pkg/protocols/http/request_annotations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ func TestRequestParseAnnotationsTimeout(t *testing.T) {
httpReq, err := retryablehttp.NewRequest(http.MethodGet, "https://example.com", nil)
require.Nil(t, err, "could not create http request")

newRequest, cancelFunc, modified := request.parseAnnotations(rawRequest, httpReq)
require.NotNil(t, cancelFunc, "could not initialize valid cancel function")
overrides, modified := request.parseAnnotations(rawRequest, httpReq)
require.NotNil(t, overrides.cancelFunc, "could not initialize valid cancel function")
require.True(t, modified, "could not get correct modified value")
_, deadlined := newRequest.Context().Deadline()
_, deadlined := overrides.request.Context().Deadline()
require.True(t, deadlined, "could not get set request deadline")
})

Expand All @@ -39,10 +39,10 @@ func TestRequestParseAnnotationsTimeout(t *testing.T) {
httpReq, err := retryablehttp.NewRequestWithContext(context.Background(), http.MethodGet, "https://example.com", nil)
require.Nil(t, err, "could not create http request")

newRequest, cancelFunc, modified := request.parseAnnotations(rawRequest, httpReq)
require.Nil(t, cancelFunc, "cancel function should be nil")
newRequestWithOverrides, modified := request.parseAnnotations(rawRequest, httpReq)
require.Nil(t, newRequestWithOverrides.cancelFunc, "cancel function should be nil")
require.False(t, modified, "could not get correct modified value")
_, deadlined := newRequest.Context().Deadline()
_, deadlined := newRequestWithOverrides.request.Context().Deadline()
require.False(t, deadlined, "could not get set request deadline")
})
}
2 changes: 1 addition & 1 deletion v2/pkg/protocols/network/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func (request *Request) executeRequestWithPayloads(variables map[string]interfac

if request.options.Interactsh != nil {
var transformedData string
transformedData, interactshURLs = request.options.Interactsh.ReplaceMarkers(string(data), []string{})
transformedData, interactshURLs = request.options.Interactsh.Replace(string(data), []string{})
data = []byte(transformedData)
}

Expand Down