2020-12-26 14:55:15 +05:30
package http
2020-12-28 20:02:26 +05:30
import (
2021-02-08 01:55:53 +05:30
"bytes"
2022-07-21 21:29:34 +05:30
"context"
2021-10-30 13:17:47 +03:00
"encoding/hex"
2021-03-08 19:01:40 +05:30
"fmt"
2020-12-28 20:02:26 +05:30
"io"
"net/http"
"net/http/httputil"
2023-09-16 14:20:35 +05:30
"strconv"
2020-12-28 20:02:26 +05:30
"strings"
"sync"
"time"
"github.com/pkg/errors"
2021-09-07 17:31:46 +03:00
"github.com/remeh/sizedwaitgroup"
"go.uber.org/multierr"
2021-10-15 13:55:50 +05:30
"moul.io/http2curl"
2021-09-07 17:31:46 +03:00
2023-09-04 10:24:34 +02:00
"github.com/projectdiscovery/fastdialer/fastdialer"
2020-12-29 01:30:07 +05:30
"github.com/projectdiscovery/gologger"
2023-10-17 17:44:13 +05:30
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
"github.com/projectdiscovery/nuclei/v3/pkg/output"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/fuzz"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/eventcreator"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/responsehighlighter"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/tostring"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httpclientpool"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/signer"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/signerpool"
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
2020-12-28 20:02:26 +05:30
"github.com/projectdiscovery/rawhttp"
2023-09-16 14:20:35 +05:30
"github.com/projectdiscovery/utils/reader"
2023-08-18 02:37:35 +05:30
sliceutil "github.com/projectdiscovery/utils/slice"
2022-11-06 21:24:23 +01:00
stringsutil "github.com/projectdiscovery/utils/strings"
2023-01-24 22:04:52 +05:30
urlutil "github.com/projectdiscovery/utils/url"
2020-12-28 20:02:26 +05:30
)
const defaultMaxWorkers = 150
2021-11-03 19:53:45 +05:30
// Type returns the type of the protocol request
func ( request * Request ) Type ( ) templateTypes . ProtocolType {
return templateTypes . HTTPProtocol
}
2020-12-28 20:02:26 +05:30
// executeRaceRequest executes race condition request for a URL
2022-10-03 12:12:20 +02:00
func ( request * Request ) executeRaceRequest ( input * contextargs . Context , previous output . InternalEvent , callback protocols . OutputEventCallback ) error {
2022-11-09 14:18:56 +01:00
reqURL := input . MetaInput . Input
2021-10-01 14:30:04 +03:00
var generatedRequests [ ] * generatedRequest
2021-03-01 05:18:31 +01:00
// Requests within race condition should be dumped once and the output prefilled to allow DSL language to work
// This will introduce a delay and will populate in hacky way the field "request" of outputEvent
2022-11-01 20:28:50 +05:30
generator := request . newGenerator ( false )
2021-11-24 21:08:08 +05:30
inputData , payloads , ok := generator . nextValue ( )
if ! ok {
return nil
}
2022-11-09 14:18:56 +01:00
ctx := request . newContext ( input )
2022-12-09 19:47:03 +01:00
requestForDump , err := generator . Make ( ctx , input , inputData , payloads , nil )
2021-03-01 05:18:31 +01:00
if err != nil {
return err
}
2021-10-01 14:30:04 +03:00
request . setCustomHeaders ( requestForDump )
2021-03-01 05:18:31 +01:00
dumpedRequest , err := dump ( requestForDump , reqURL )
if err != nil {
return err
}
2022-04-01 14:29:02 -05:00
if request . options . Options . Debug || request . options . Options . DebugRequests || request . options . Options . StoreResponse {
msg := fmt . Sprintf ( "[%s] Dumped HTTP request for %s\n\n" , request . options . TemplateID , reqURL )
if request . options . Options . Debug || request . options . Options . DebugRequests {
gologger . Info ( ) . Msg ( msg )
gologger . Print ( ) . Msgf ( "%s" , string ( dumpedRequest ) )
}
if request . options . Options . StoreResponse {
request . options . Output . WriteStoreDebugData ( reqURL , request . options . TemplateID , request . Type ( ) . String ( ) , fmt . Sprintf ( "%s\n%s" , msg , dumpedRequest ) )
}
2021-03-01 05:18:31 +01:00
}
previous [ "request" ] = string ( dumpedRequest )
2020-12-26 14:55:15 +05:30
2021-03-01 05:18:31 +01:00
// Pre-Generate requests
2021-10-01 14:30:04 +03:00
for i := 0 ; i < request . RaceNumberRequests ; i ++ {
2022-11-01 20:28:50 +05:30
generator := request . newGenerator ( false )
2021-11-24 21:08:08 +05:30
inputData , payloads , ok := generator . nextValue ( )
if ! ok {
break
}
2022-11-09 14:18:56 +01:00
ctx := request . newContext ( input )
2022-12-09 19:47:03 +01:00
generatedRequest , err := generator . Make ( ctx , input , inputData , payloads , nil )
2021-03-01 05:18:31 +01:00
if err != nil {
return err
}
2021-10-01 14:30:04 +03:00
generatedRequests = append ( generatedRequests , generatedRequest )
2021-03-01 05:18:31 +01:00
}
2020-12-28 20:02:26 +05:30
2021-03-01 05:18:31 +01:00
wg := sync . WaitGroup { }
2020-12-28 20:02:26 +05:30
var requestErr error
2020-12-30 16:49:45 +05:30
mutex := & sync . Mutex { }
2021-10-01 14:30:04 +03:00
for i := 0 ; i < request . RaceNumberRequests ; i ++ {
2021-03-01 05:18:31 +01:00
wg . Add ( 1 )
2020-12-28 20:02:26 +05:30
go func ( httpRequest * generatedRequest ) {
2021-03-01 05:18:31 +01:00
defer wg . Done ( )
2022-10-03 12:12:20 +02:00
err := request . executeRequest ( input , httpRequest , previous , false , callback , 0 )
2020-12-28 20:02:26 +05:30
mutex . Lock ( )
2020-12-26 14:55:15 +05:30
if err != nil {
2020-12-28 20:02:26 +05:30
requestErr = multierr . Append ( requestErr , err )
2020-12-26 14:55:15 +05:30
}
2020-12-28 20:02:26 +05:30
mutex . Unlock ( )
2021-10-01 14:30:04 +03:00
} ( generatedRequests [ i ] )
request . options . Progress . IncrementRequests ( )
2020-12-26 14:55:15 +05:30
}
2021-03-01 05:18:31 +01:00
wg . Wait ( )
2021-01-01 19:36:21 +05:30
return requestErr
2020-12-26 14:55:15 +05:30
}
2021-02-04 22:00:09 +05:30
// executeRaceRequest executes parallel requests for a template
2022-10-03 12:12:20 +02:00
func ( request * Request ) executeParallelHTTP ( input * contextargs . Context , dynamicValues output . InternalEvent , callback protocols . OutputEventCallback ) error {
2022-11-01 20:28:50 +05:30
generator := request . newGenerator ( false )
2020-12-26 14:55:15 +05:30
// Workers that keeps enqueuing new requests
2021-10-01 14:30:04 +03:00
maxWorkers := request . Threads
2020-12-26 14:55:15 +05:30
swg := sizedwaitgroup . New ( maxWorkers )
2020-12-28 20:02:26 +05:30
var requestErr error
2020-12-30 16:49:45 +05:30
mutex := & sync . Mutex { }
2020-12-28 20:02:26 +05:30
for {
2021-11-24 21:08:08 +05:30
inputData , payloads , ok := generator . nextValue ( )
if ! ok {
break
}
2022-11-09 14:18:56 +01:00
ctx := request . newContext ( input )
2022-12-09 19:47:03 +01:00
generatedHttpRequest , err := generator . Make ( ctx , input , inputData , payloads , dynamicValues )
2020-12-28 20:02:26 +05:30
if err != nil {
2023-07-13 00:51:06 +05:30
if err == types . ErrNoMoreRequests {
2021-11-10 18:11:42 +01:00
break
}
2021-10-01 14:30:04 +03:00
request . options . Progress . IncrementFailedRequestsBy ( int64 ( generator . Total ( ) ) )
2021-01-01 19:36:21 +05:30
return err
2020-12-26 14:55:15 +05:30
}
2022-11-09 14:18:56 +01:00
if input . MetaInput . Input == "" {
input . MetaInput . Input = generatedHttpRequest . URL ( )
2021-11-10 18:11:42 +01:00
}
2020-12-28 20:02:26 +05:30
swg . Add ( )
go func ( httpRequest * generatedRequest ) {
defer swg . Done ( )
2021-10-01 14:30:04 +03:00
request . options . RateLimiter . Take ( )
2021-07-10 14:54:49 +05:30
previous := make ( map [ string ] interface { } )
2022-10-03 12:12:20 +02:00
err := request . executeRequest ( input , httpRequest , previous , false , callback , 0 )
2020-12-28 20:02:26 +05:30
mutex . Lock ( )
if err != nil {
requestErr = multierr . Append ( requestErr , err )
}
mutex . Unlock ( )
2021-10-01 14:30:04 +03:00
} ( generatedHttpRequest )
request . options . Progress . IncrementRequests ( )
2020-12-26 14:55:15 +05:30
}
swg . Wait ( )
2021-01-01 19:36:21 +05:30
return requestErr
2020-12-26 14:55:15 +05:30
}
2021-06-09 11:15:21 +05:30
// executeTurboHTTP executes turbo http request for a URL
2022-10-03 12:12:20 +02:00
func ( request * Request ) executeTurboHTTP ( input * contextargs . Context , dynamicValues , previous output . InternalEvent , callback protocols . OutputEventCallback ) error {
2022-11-01 20:28:50 +05:30
generator := request . newGenerator ( false )
2020-12-26 14:55:15 +05:30
// need to extract the target from the url
2023-01-24 22:04:52 +05:30
URL , err := urlutil . Parse ( input . MetaInput . Input )
2020-12-26 14:55:15 +05:30
if err != nil {
2021-01-01 19:36:21 +05:30
return err
2020-12-26 14:55:15 +05:30
}
pipeOptions := rawhttp . DefaultPipelineOptions
pipeOptions . Host = URL . Host
pipeOptions . MaxConnections = 1
2021-10-01 14:30:04 +03:00
if request . PipelineConcurrentConnections > 0 {
pipeOptions . MaxConnections = request . PipelineConcurrentConnections
2020-12-26 14:55:15 +05:30
}
2021-10-01 14:30:04 +03:00
if request . PipelineRequestsPerConnection > 0 {
pipeOptions . MaxPendingRequests = request . PipelineRequestsPerConnection
2020-12-26 14:55:15 +05:30
}
2021-10-01 14:30:04 +03:00
pipeClient := rawhttp . NewPipelineClient ( pipeOptions )
2020-12-26 14:55:15 +05:30
// defaultMaxWorkers should be a sufficient value to keep queues always full
maxWorkers := defaultMaxWorkers
// in case the queue is bigger increase the workers
if pipeOptions . MaxPendingRequests > maxWorkers {
maxWorkers = pipeOptions . MaxPendingRequests
}
swg := sizedwaitgroup . New ( maxWorkers )
2020-12-28 20:02:26 +05:30
var requestErr error
2020-12-30 16:49:45 +05:30
mutex := & sync . Mutex { }
2020-12-28 20:02:26 +05:30
for {
2021-11-24 21:08:08 +05:30
inputData , payloads , ok := generator . nextValue ( )
if ! ok {
break
}
2022-11-09 14:18:56 +01:00
ctx := request . newContext ( input )
2022-12-09 19:47:03 +01:00
generatedHttpRequest , err := generator . Make ( ctx , input , inputData , payloads , dynamicValues )
2020-12-28 20:02:26 +05:30
if err != nil {
2021-10-01 14:30:04 +03:00
request . options . Progress . IncrementFailedRequestsBy ( int64 ( generator . Total ( ) ) )
2021-01-01 19:36:21 +05:30
return err
2020-12-28 20:02:26 +05:30
}
2022-11-09 14:18:56 +01:00
if input . MetaInput . Input == "" {
input . MetaInput . Input = generatedHttpRequest . URL ( )
2021-11-10 18:11:42 +01:00
}
2021-10-01 14:30:04 +03:00
generatedHttpRequest . pipelinedClient = pipeClient
2020-12-28 20:02:26 +05:30
swg . Add ( )
go func ( httpRequest * generatedRequest ) {
defer swg . Done ( )
2020-12-26 14:55:15 +05:30
2022-10-03 12:12:20 +02:00
err := request . executeRequest ( input , httpRequest , previous , false , callback , 0 )
2020-12-28 20:02:26 +05:30
mutex . Lock ( )
if err != nil {
requestErr = multierr . Append ( requestErr , err )
}
mutex . Unlock ( )
2021-10-01 14:30:04 +03:00
} ( generatedHttpRequest )
request . options . Progress . IncrementRequests ( )
2020-12-26 14:55:15 +05:30
}
swg . Wait ( )
2021-01-01 19:36:21 +05:30
return requestErr
2020-12-26 14:55:15 +05:30
}
2022-11-01 20:28:50 +05:30
// executeFuzzingRule executes fuzzing request for a URL
func ( request * Request ) executeFuzzingRule ( input * contextargs . Context , previous output . InternalEvent , callback protocols . OutputEventCallback ) error {
2023-09-18 21:31:32 +03:00
// If request is self-contained we don't need to parse any input.
if ! request . SelfContained {
// If it's not self-contained we parse user provided input
if _ , err := urlutil . Parse ( input . MetaInput . Input ) ; err != nil {
return errors . Wrap ( err , "could not parse url" )
}
2022-11-01 20:28:50 +05:30
}
fuzzRequestCallback := func ( gr fuzz . GeneratedRequest ) bool {
hasInteractMatchers := interactsh . HasMatchers ( request . CompiledOperators )
hasInteractMarkers := len ( gr . InteractURLs ) > 0
2022-11-09 14:18:56 +01:00
if request . options . HostErrorsCache != nil && request . options . HostErrorsCache . Check ( input . MetaInput . Input ) {
2022-11-01 20:28:50 +05:30
return false
}
2023-04-12 23:55:21 +05:30
request . options . RateLimiter . Take ( )
2022-11-01 20:28:50 +05:30
req := & generatedRequest {
request : gr . Request ,
dynamicValues : gr . DynamicValues ,
interactshURLs : gr . InteractURLs ,
original : request ,
}
var gotMatches bool
requestErr := request . executeRequest ( input , req , gr . DynamicValues , hasInteractMatchers , func ( event * output . InternalWrappedEvent ) {
if hasInteractMarkers && hasInteractMatchers && request . options . Interactsh != nil {
2023-04-16 19:49:35 +02:00
requestData := & interactsh . RequestData {
2022-11-01 20:28:50 +05:30
MakeResultFunc : request . MakeResultEvent ,
Event : event ,
Operators : request . CompiledOperators ,
MatchFunc : request . Match ,
ExtractFunc : request . Extract ,
2023-04-16 19:49:35 +02:00
}
request . options . Interactsh . RequestEvent ( gr . InteractURLs , requestData )
gotMatches = request . options . Interactsh . AlreadyMatched ( requestData )
2022-11-01 20:28:50 +05:30
} else {
callback ( event )
}
2023-04-16 19:49:35 +02:00
// Add the extracts to the dynamic values if any.
if event . OperatorsResult != nil {
gotMatches = event . OperatorsResult . Matched
}
2022-11-01 20:28:50 +05:30
} , 0 )
// If a variable is unresolved, skip all further requests
2023-04-16 19:49:35 +02:00
if errors . Is ( requestErr , errStopExecution ) {
2022-11-01 20:28:50 +05:30
return false
}
if requestErr != nil {
if request . options . HostErrorsCache != nil {
2022-11-09 14:18:56 +01:00
request . options . HostErrorsCache . MarkFailed ( input . MetaInput . Input , requestErr )
2022-11-01 20:28:50 +05:30
}
2023-09-18 21:31:32 +03:00
gologger . Verbose ( ) . Msgf ( "[%s] Error occurred in request: %s\n" , request . options . TemplateID , requestErr )
2022-11-01 20:28:50 +05:30
}
request . options . Progress . IncrementRequests ( )
// If this was a match, and we want to stop at first match, skip all further requests.
2023-04-16 19:49:35 +02:00
shouldStopAtFirstMatch := request . options . Options . StopAtFirstMatch || request . StopAtFirstMatch
if shouldStopAtFirstMatch && gotMatches {
2022-11-01 20:28:50 +05:30
return false
}
return true
}
// Iterate through all requests for template and queue them for fuzzing
generator := request . newGenerator ( true )
for {
value , payloads , result := generator . nextValue ( )
if ! result {
break
}
2022-12-09 19:47:03 +01:00
generated , err := generator . Make ( context . Background ( ) , input , value , payloads , nil )
2022-11-01 20:28:50 +05:30
if err != nil {
continue
}
for _ , rule := range request . Fuzzing {
err = rule . Execute ( & fuzz . ExecuteRuleInput {
2023-06-26 19:25:51 +02:00
Input : input ,
2022-11-01 20:28:50 +05:30
Callback : fuzzRequestCallback ,
Values : generated . dynamicValues ,
BaseRequest : generated . request ,
} )
2023-07-13 00:51:06 +05:30
if err == types . ErrNoMoreRequests {
2022-11-01 20:28:50 +05:30
return nil
}
if err != nil {
return errors . Wrap ( err , "could not execute rule" )
}
}
}
return nil
}
2020-12-29 11:42:46 +05:30
// ExecuteWithResults executes the final request on a URL
2022-10-03 12:12:20 +02:00
func ( request * Request ) ExecuteWithResults ( input * contextargs . Context , dynamicValues , previous output . InternalEvent , callback protocols . OutputEventCallback ) error {
2022-06-28 20:20:18 +05:30
if request . Pipeline || request . Race && request . RaceNumberRequests > 0 || request . Threads > 0 {
variablesMap := request . options . Variables . Evaluate ( generators . MergeMaps ( dynamicValues , previous ) )
2023-05-25 18:32:35 +02:00
dynamicValues = generators . MergeMaps ( variablesMap , dynamicValues , request . options . Constants )
2022-06-28 20:20:18 +05:30
}
2020-12-26 14:55:15 +05:30
// verify if pipeline was requested
2021-10-01 14:30:04 +03:00
if request . Pipeline {
2022-10-03 12:12:20 +02:00
return request . executeTurboHTTP ( input , dynamicValues , previous , callback )
2020-12-26 14:55:15 +05:30
}
// verify if a basic race condition was requested
2021-10-01 14:30:04 +03:00
if request . Race && request . RaceNumberRequests > 0 {
2022-10-03 12:12:20 +02:00
return request . executeRaceRequest ( input , dynamicValues , callback )
2020-12-26 14:55:15 +05:30
}
// verify if parallel elaboration was requested
2021-10-01 14:30:04 +03:00
if request . Threads > 0 {
2022-10-03 12:12:20 +02:00
return request . executeParallelHTTP ( input , dynamicValues , callback )
2020-12-26 14:55:15 +05:30
}
2022-11-01 20:28:50 +05:30
// verify if fuzz elaboration was requested
if len ( request . Fuzzing ) > 0 {
return request . executeFuzzingRule ( input , dynamicValues , callback )
}
generator := request . newGenerator ( false )
2020-12-26 14:55:15 +05:30
2021-11-24 21:08:08 +05:30
var gotDynamicValues map [ string ] [ ] string
2020-12-28 20:02:26 +05:30
var requestErr error
2023-08-18 02:37:35 +05:30
2020-12-28 20:02:26 +05:30
for {
2021-11-24 21:08:08 +05:30
// returns two values, error and skip, which skips the execution for the request instance.
executeFunc := func ( data string , payloads , dynamicValue map [ string ] interface { } ) ( bool , error ) {
2022-02-01 12:10:18 +01:00
hasInteractMatchers := interactsh . HasMatchers ( request . CompiledOperators )
2022-03-30 20:35:46 +05:30
2022-09-19 13:39:28 +02:00
request . options . RateLimiter . Take ( )
2022-11-09 14:18:56 +01:00
ctx := request . newContext ( input )
ctxWithTimeout , cancel := context . WithTimeout ( ctx , time . Duration ( request . options . Options . Timeout ) * time . Second )
2022-07-21 21:29:34 +05:30
defer cancel ( )
2022-12-09 19:47:03 +01:00
generatedHttpRequest , err := generator . Make ( ctxWithTimeout , input , data , payloads , dynamicValue )
2021-11-24 21:08:08 +05:30
if err != nil {
2023-07-13 00:51:06 +05:30
if err == types . ErrNoMoreRequests {
2021-11-24 21:08:08 +05:30
return true , nil
}
request . options . Progress . IncrementFailedRequestsBy ( int64 ( generator . Total ( ) ) )
return true , err
2021-11-10 18:11:42 +01:00
}
2022-10-10 08:10:07 +02:00
if generatedHttpRequest . customCancelFunction != nil {
defer generatedHttpRequest . customCancelFunction ( )
}
2022-02-01 12:34:12 +01:00
hasInteractMarkers := interactsh . HasMarkers ( data ) || len ( generatedHttpRequest . interactshURLs ) > 0
2022-11-09 14:18:56 +01:00
if input . MetaInput . Input == "" {
input . MetaInput . Input = generatedHttpRequest . URL ( )
2021-01-01 16:52:41 +05:30
}
2021-11-30 16:55:09 +05:30
// Check if hosts keep erroring
2022-11-09 14:18:56 +01:00
if request . options . HostErrorsCache != nil && request . options . HostErrorsCache . Check ( input . MetaInput . ID ( ) ) {
2021-11-24 21:08:08 +05:30
return true , nil
2021-04-16 16:56:41 +05:30
}
2022-02-01 11:25:29 +01:00
var gotMatches bool
2022-10-03 12:12:20 +02:00
err = request . executeRequest ( input , generatedHttpRequest , previous , hasInteractMatchers , func ( event * output . InternalWrappedEvent ) {
2023-08-18 02:37:35 +05:30
// a special case where operators has interactsh matchers and multiple request are made
// ex: status_code_2 , interactsh_protocol (from 1st request) etc
needsRequestEvent := interactsh . HasMatchers ( request . CompiledOperators ) && request . NeedsRequestCondition ( )
if ( hasInteractMarkers || needsRequestEvent ) && request . options . Interactsh != nil {
2023-04-16 19:49:35 +02:00
requestData := & interactsh . RequestData {
2021-11-24 21:08:08 +05:30
MakeResultFunc : request . MakeResultEvent ,
Event : event ,
Operators : request . CompiledOperators ,
MatchFunc : request . Match ,
ExtractFunc : request . Extract ,
2023-04-16 19:49:35 +02:00
}
2023-08-18 02:37:35 +05:30
allOASTUrls := getInteractshURLsFromEvent ( event . InternalEvent )
allOASTUrls = append ( allOASTUrls , generatedHttpRequest . interactshURLs ... )
request . options . Interactsh . RequestEvent ( sliceutil . Dedupe ( allOASTUrls ) , requestData )
2023-04-16 19:49:35 +02:00
gotMatches = request . options . Interactsh . AlreadyMatched ( requestData )
}
// Add the extracts to the dynamic values if any.
if event . OperatorsResult != nil {
gotMatches = event . OperatorsResult . Matched
gotDynamicValues = generators . MergeMapsMany ( event . OperatorsResult . DynamicValues , dynamicValues , gotDynamicValues )
2021-11-24 21:08:08 +05:30
}
2023-03-16 23:20:38 +05:30
// Note: This is a race condition prone zone i.e when request has interactsh_matchers
// Interactsh.RequestEvent tries to access/update output.InternalWrappedEvent depending on logic
// to avoid conflicts with `callback` mutex is used here and in Interactsh.RequestEvent
// Note: this only happens if requests > 1 and interactsh matcher is used
// TODO: interactsh logic in nuclei needs to be refactored to avoid such situations
2023-03-15 20:45:44 +05:30
callback ( event )
2022-06-11 14:29:05 +05:30
} , generator . currentIndex )
2021-11-30 16:55:09 +05:30
2021-11-24 21:08:08 +05:30
// If a variable is unresolved, skip all further requests
2023-04-16 19:49:35 +02:00
if errors . Is ( err , errStopExecution ) {
2021-11-24 21:08:08 +05:30
return true , nil
2021-01-01 16:52:41 +05:30
}
2021-11-24 21:08:08 +05:30
if err != nil {
2022-07-18 15:35:53 -05:00
if request . options . HostErrorsCache != nil {
2022-11-09 14:18:56 +01:00
request . options . HostErrorsCache . MarkFailed ( input . MetaInput . ID ( ) , err )
2021-11-24 21:08:08 +05:30
}
requestErr = err
2021-04-16 16:56:41 +05:30
}
2021-11-24 21:08:08 +05:30
request . options . Progress . IncrementRequests ( )
2021-12-02 17:03:02 +05:30
// If this was a match, and we want to stop at first match, skip all further requests.
2023-04-16 19:49:35 +02:00
shouldStopAtFirstMatch := generatedHttpRequest . original . options . Options . StopAtFirstMatch || generatedHttpRequest . original . options . StopAtFirstMatch || request . StopAtFirstMatch
if shouldStopAtFirstMatch && gotMatches {
2021-11-24 21:08:08 +05:30
return true , nil
2021-08-17 14:23:42 +05:30
}
2021-11-24 21:08:08 +05:30
return false , nil
2020-12-26 14:55:15 +05:30
}
2021-11-24 21:08:08 +05:30
inputData , payloads , ok := generator . nextValue ( )
if ! ok {
break
}
var gotErr error
var skip bool
if len ( gotDynamicValues ) > 0 {
2021-11-24 22:44:43 +05:30
operators . MakeDynamicValuesCallback ( gotDynamicValues , request . IterateAll , func ( data map [ string ] interface { } ) bool {
2021-11-24 21:08:08 +05:30
if skip , gotErr = executeFunc ( inputData , payloads , data ) ; skip || gotErr != nil {
return true
}
return false
} )
} else {
skip , gotErr = executeFunc ( inputData , payloads , dynamicValues )
}
if gotErr != nil && requestErr == nil {
requestErr = gotErr
}
if skip || gotErr != nil {
2020-12-26 14:55:15 +05:30
break
}
}
2021-01-01 19:36:21 +05:30
return requestErr
2020-12-26 14:55:15 +05:30
}
2021-02-04 22:09:32 +05:30
const drainReqSize = int64 ( 8 * 1024 )
2021-10-07 04:50:34 +05:30
var errStopExecution = errors . New ( "stop execution due to unresolved variables" )
2021-02-26 13:13:11 +05:30
// executeRequest executes the actual generated request and returns error if occurred
2022-10-03 12:12:20 +02:00
func ( request * Request ) executeRequest ( input * contextargs . Context , generatedRequest * generatedRequest , previousEvent output . InternalEvent , hasInteractMatchers bool , callback protocols . OutputEventCallback , requestCount int ) error {
2021-10-01 14:30:04 +03:00
request . setCustomHeaders ( generatedRequest )
2020-12-26 14:55:15 +05:30
2022-03-29 20:36:26 +05:30
// Try to evaluate any payloads before replacement
finalMap := generators . MergeMaps ( generatedRequest . dynamicValues , generatedRequest . meta )
2022-12-09 19:47:03 +01:00
// add known variables from metainput
if _ , ok := finalMap [ "ip" ] ; ! ok && input . MetaInput . CustomIP != "" {
finalMap [ "ip" ] = input . MetaInput . CustomIP
}
2022-03-29 20:36:26 +05:30
for payloadName , payloadValue := range generatedRequest . dynamicValues {
if data , err := expressions . Evaluate ( types . ToString ( payloadValue ) , finalMap ) ; err == nil {
generatedRequest . dynamicValues [ payloadName ] = data
}
}
for payloadName , payloadValue := range generatedRequest . meta {
if data , err := expressions . Evaluate ( types . ToString ( payloadValue ) , finalMap ) ; err == nil {
generatedRequest . meta [ payloadName ] = data
}
}
2020-12-26 14:55:15 +05:30
var (
2021-02-25 02:08:10 +01:00
resp * http . Response
2021-10-01 14:30:04 +03:00
fromCache bool
2021-02-25 02:08:10 +01:00
dumpedRequest [ ] byte
err error
2020-12-26 14:55:15 +05:30
)
2021-02-08 01:43:51 +05:30
2021-11-26 13:49:12 +01:00
// Dump request for variables checks
2021-10-07 01:40:49 +05:30
// For race conditions we can't dump the request body at this point as it's already waiting the open-gate event, already handled with a similar code within the race function
2021-10-11 13:58:20 +03:00
if ! generatedRequest . original . Race {
2023-09-16 14:20:35 +05:30
// change encoding type to content-length unless transfer-encoding header is manually set
if generatedRequest . request != nil && ! stringsutil . EqualFoldAny ( generatedRequest . request . Method , http . MethodGet , http . MethodHead ) && generatedRequest . request . Body != nil && generatedRequest . request . Header . Get ( "Transfer-Encoding" ) != "chunked" {
var newReqBody * reader . ReusableReadCloser
newReqBody , ok := generatedRequest . request . Body . ( * reader . ReusableReadCloser )
if ! ok {
newReqBody , err = reader . NewReusableReadCloser ( generatedRequest . request . Body )
}
if err == nil {
// update the request body with the reusable reader
generatedRequest . request . Body = newReqBody
// get content length
length , _ := io . Copy ( io . Discard , newReqBody )
generatedRequest . request . ContentLength = length
} else {
// log error and continue
gologger . Verbose ( ) . Msgf ( "[%v] Could not read request body while forcing transfer encoding: %s\n" , request . options . TemplateID , err )
err = nil
}
}
// do the same for unsafe requests
if generatedRequest . rawRequest != nil && ! stringsutil . EqualFoldAny ( generatedRequest . rawRequest . Method , http . MethodGet , http . MethodHead ) && generatedRequest . rawRequest . Data != "" && generatedRequest . rawRequest . Headers [ "Transfer-Encoding" ] != "chunked" {
generatedRequest . rawRequest . Headers [ "Content-Length" ] = strconv . Itoa ( len ( generatedRequest . rawRequest . Data ) )
}
2021-10-07 01:40:49 +05:30
var dumpError error
2021-11-13 03:17:05 +01:00
// TODO: dump is currently not working with post-processors - somehow it alters the signature
2022-11-09 14:18:56 +01:00
dumpedRequest , dumpError = dump ( generatedRequest , input . MetaInput . Input )
2021-10-07 01:40:49 +05:30
if dumpError != nil {
return dumpError
}
dumpedRequestString := string ( dumpedRequest )
2021-12-18 20:06:51 +01:00
if ignoreList := GetVariablesNamesSkipList ( generatedRequest . original . Signature . Value ) ; ignoreList != nil {
if varErr := expressions . ContainsVariablesWithIgnoreList ( ignoreList , dumpedRequestString ) ; varErr != nil && ! request . SkipVariablesCheck {
2022-11-09 14:18:56 +01:00
gologger . Warning ( ) . Msgf ( "[%s] Could not make http request for %s: %v\n" , request . options . TemplateID , input . MetaInput . Input , varErr )
2021-12-18 20:06:51 +01:00
return errStopExecution
}
} else { // Check if are there any unresolved variables. If yes, skip unless overridden by user.
if varErr := expressions . ContainsUnresolvedVariables ( dumpedRequestString ) ; varErr != nil && ! request . SkipVariablesCheck {
2022-11-09 14:18:56 +01:00
gologger . Warning ( ) . Msgf ( "[%s] Could not make http request for %s: %v\n" , request . options . TemplateID , input . MetaInput . Input , varErr )
2021-12-18 20:06:51 +01:00
return errStopExecution
}
2021-10-07 01:40:49 +05:30
}
}
2021-01-11 19:59:12 +05:30
var formedURL string
2021-01-16 12:06:27 +05:30
var hostname string
2020-12-26 14:55:15 +05:30
timeStart := time . Now ( )
2021-10-01 14:30:04 +03:00
if generatedRequest . original . Pipeline {
2023-09-16 14:20:35 +05:30
// if request is a pipeline request, use the pipelined client
2021-10-01 14:30:04 +03:00
if generatedRequest . rawRequest != nil {
formedURL = generatedRequest . rawRequest . FullURL
2023-01-24 22:04:52 +05:30
if parsed , parseErr := urlutil . ParseURL ( formedURL , true ) ; parseErr == nil {
2021-06-09 11:15:21 +05:30
hostname = parsed . Host
}
2022-11-09 14:18:56 +01:00
resp , err = generatedRequest . pipelinedClient . DoRaw ( generatedRequest . rawRequest . Method , input . MetaInput . Input , generatedRequest . rawRequest . Path , generators . ExpandMapValues ( generatedRequest . rawRequest . Headers ) , io . NopCloser ( strings . NewReader ( generatedRequest . rawRequest . Data ) ) )
2021-10-01 14:30:04 +03:00
} else if generatedRequest . request != nil {
resp , err = generatedRequest . pipelinedClient . Dor ( generatedRequest . request )
2021-01-16 12:06:27 +05:30
}
2021-10-01 14:30:04 +03:00
} else if generatedRequest . original . Unsafe && generatedRequest . rawRequest != nil {
2023-09-16 14:20:35 +05:30
// if request is a unsafe request, use the rawhttp client
2021-10-01 14:30:04 +03:00
formedURL = generatedRequest . rawRequest . FullURL
2022-07-26 06:38:53 -05:00
// use request url as matched url if empty
if formedURL == "" {
2023-01-24 22:04:52 +05:30
urlx , err := urlutil . Parse ( input . MetaInput . Input )
2023-01-05 21:02:36 +05:30
if err != nil {
2023-06-20 01:25:22 +05:30
formedURL = fmt . Sprintf ( "%s%s" , input . MetaInput . Input , generatedRequest . rawRequest . Path )
2023-01-05 21:02:36 +05:30
} else {
2023-06-20 01:25:22 +05:30
_ = urlx . MergePath ( generatedRequest . rawRequest . Path , true )
formedURL = urlx . String ( )
2022-10-27 20:09:38 +02:00
}
2022-07-26 06:38:53 -05:00
}
2023-01-24 22:04:52 +05:30
if parsed , parseErr := urlutil . ParseURL ( formedURL , true ) ; parseErr == nil {
2021-02-26 13:13:11 +05:30
hostname = parsed . Host
2021-01-16 12:06:27 +05:30
}
2022-07-19 01:08:30 +05:30
options := * generatedRequest . original . rawhttpClient . Options
2021-10-01 14:30:04 +03:00
options . FollowRedirects = request . Redirects
options . CustomRawBytes = generatedRequest . rawRequest . UnsafeRawBytes
2022-02-25 19:26:10 +05:30
options . ForceReadAllBody = request . ForceReadAllBody
2022-05-27 18:23:07 +02:00
options . SNI = request . options . Options . SNI
2023-07-04 19:04:13 +05:30
inputUrl := input . MetaInput . Input
if url , err := urlutil . ParseURL ( inputUrl , false ) ; err == nil {
2023-09-16 14:20:35 +05:30
url . Path = ""
url . Params = urlutil . NewOrderedParams ( ) // donot include query params
// inputUrl should only contain scheme://host:port
inputUrl = url . String ( )
2023-07-04 19:04:13 +05:30
}
formedURL = fmt . Sprintf ( "%s%s" , inputUrl , generatedRequest . rawRequest . Path )
resp , err = generatedRequest . original . rawhttpClient . DoRawWithOptions ( generatedRequest . rawRequest . Method , inputUrl , generatedRequest . rawRequest . Path , generators . ExpandMapValues ( generatedRequest . rawRequest . Headers ) , io . NopCloser ( strings . NewReader ( generatedRequest . rawRequest . Data ) ) , & options )
2020-12-26 14:55:15 +05:30
} else {
2023-09-16 14:20:35 +05:30
//** For Normal requests **//
2021-10-01 14:30:04 +03:00
hostname = generatedRequest . request . URL . Host
formedURL = generatedRequest . request . URL . String ( )
2020-12-26 14:55:15 +05:30
// if nuclei-project is available check if the request was already sent previously
2021-10-01 14:30:04 +03:00
if request . options . ProjectFile != nil {
2020-12-26 14:55:15 +05:30
// if unavailable fail silently
2021-10-01 14:30:04 +03:00
fromCache = true
resp , err = request . options . ProjectFile . Get ( dumpedRequest )
2020-12-26 14:55:15 +05:30
if err != nil {
2021-10-01 14:30:04 +03:00
fromCache = false
2020-12-26 14:55:15 +05:30
}
}
if resp == nil {
2021-12-02 15:57:52 +01:00
if errSignature := request . handleSignature ( generatedRequest ) ; errSignature != nil {
return errSignature
2021-11-12 19:29:45 +01:00
}
2022-10-03 12:12:20 +02:00
httpclient := request . httpClient
if input . CookieJar != nil {
connConfiguration := request . connConfiguration
2023-02-13 12:16:41 +01:00
connConfiguration . Connection . SetCookieJar ( input . CookieJar )
2022-10-03 12:12:20 +02:00
client , err := httpclientpool . Get ( request . options . Options , connConfiguration )
if err != nil {
return errors . Wrap ( err , "could not get http client" )
}
httpclient = client
}
resp , err = httpclient . Do ( generatedRequest . request )
2020-12-26 14:55:15 +05:30
}
}
2022-06-16 17:24:21 +05:30
// use request url as matched url if empty
if formedURL == "" {
2022-11-09 14:18:56 +01:00
formedURL = input . MetaInput . Input
2022-06-16 17:24:21 +05:30
}
2021-11-26 13:49:12 +01:00
2023-02-10 18:28:28 +05:30
// converts whitespace and other chars that cannot be printed to url encoded values
formedURL = urlutil . URLEncodeWithEscapes ( formedURL )
2021-11-26 13:49:12 +01:00
// Dump the requests containing all headers
if ! generatedRequest . original . Race {
var dumpError error
2022-11-09 14:18:56 +01:00
dumpedRequest , dumpError = dump ( generatedRequest , input . MetaInput . Input )
2021-11-26 13:49:12 +01:00
if dumpError != nil {
return dumpError
}
dumpedRequestString := string ( dumpedRequest )
2022-04-01 14:29:02 -05:00
if request . options . Options . Debug || request . options . Options . DebugRequests || request . options . Options . StoreResponse {
2022-06-16 17:24:21 +05:30
msg := fmt . Sprintf ( "[%s] Dumped HTTP request for %s\n\n" , request . options . TemplateID , formedURL )
2022-04-01 14:29:02 -05:00
if request . options . Options . Debug || request . options . Options . DebugRequests {
gologger . Info ( ) . Msg ( msg )
gologger . Print ( ) . Msgf ( "%s" , dumpedRequestString )
}
if request . options . Options . StoreResponse {
2022-11-09 14:18:56 +01:00
request . options . Output . WriteStoreDebugData ( input . MetaInput . Input , request . options . TemplateID , request . Type ( ) . String ( ) , fmt . Sprintf ( "%s\n%s" , msg , dumpedRequestString ) )
2022-04-01 14:29:02 -05:00
}
2021-11-26 13:49:12 +01:00
}
}
2020-12-29 01:30:07 +05:30
if err != nil {
2021-09-07 17:31:46 +03:00
// rawhttp doesn't support draining response bodies.
2022-05-04 19:49:33 +05:30
if resp != nil && resp . Body != nil && generatedRequest . rawRequest == nil && ! generatedRequest . original . Pipeline {
2022-08-25 13:22:08 +02:00
_ , _ = io . CopyN ( io . Discard , resp . Body , drainReqSize )
2020-12-29 01:30:07 +05:30
resp . Body . Close ( )
}
2021-11-05 03:01:41 +05:30
request . options . Output . Request ( request . options . TemplatePath , formedURL , request . Type ( ) . String ( ) , err )
2021-10-01 14:30:04 +03:00
request . options . Progress . IncrementErrorsBy ( 1 )
2021-08-26 02:43:58 +05:30
// If we have interactsh markers and request times out, still send
2021-09-07 17:31:46 +03:00
// a callback event so in case we receive an interaction, correlation is possible.
2022-02-01 12:10:18 +01:00
if hasInteractMatchers {
2022-11-09 14:18:56 +01:00
outputEvent := request . responseToDSLMap ( & http . Response { } , input . MetaInput . Input , formedURL , tostring . UnsafeToString ( dumpedRequest ) , "" , "" , "" , 0 , generatedRequest . meta )
2021-08-26 02:43:58 +05:30
if i := strings . LastIndex ( hostname , ":" ) ; i != - 1 {
hostname = hostname [ : i ]
}
2022-11-09 14:18:56 +01:00
if input . MetaInput . CustomIP != "" {
outputEvent [ "ip" ] = input . MetaInput . CustomIP
} else {
outputEvent [ "ip" ] = httpclientpool . Dialer . GetDialedIP ( hostname )
}
2021-08-26 02:43:58 +05:30
event := & output . InternalWrappedEvent { InternalEvent : outputEvent }
2021-10-01 14:30:04 +03:00
if request . CompiledOperators != nil {
2021-08-26 02:43:58 +05:30
event . InternalEvent = outputEvent
}
callback ( event )
}
2021-01-01 19:36:21 +05:30
return err
2020-12-29 01:30:07 +05:30
}
2021-02-26 13:13:11 +05:30
defer func ( ) {
2021-08-30 12:40:38 +05:30
if resp . StatusCode != http . StatusSwitchingProtocols {
2022-08-25 13:22:08 +02:00
_ , _ = io . CopyN ( io . Discard , resp . Body , drainReqSize )
2021-08-30 12:40:38 +05:30
}
2021-02-26 13:13:11 +05:30
resp . Body . Close ( )
} ( )
2021-02-05 12:36:01 +05:30
2021-10-15 13:55:50 +05:30
var curlCommand string
2022-01-17 11:31:14 +01:00
if ! request . Unsafe && resp != nil && generatedRequest . request != nil && resp . Request != nil && ! request . Race {
2021-10-15 13:55:50 +05:30
bodyBytes , _ := generatedRequest . request . BodyBytes ( )
2022-08-25 13:22:08 +02:00
resp . Request . Body = io . NopCloser ( bytes . NewReader ( bodyBytes ) )
2023-08-01 22:17:42 +03:00
command , err := http2curl . GetCurlCommand ( generatedRequest . request . Request )
2021-10-15 13:55:50 +05:30
if err == nil && command != nil {
curlCommand = command . String ( )
}
}
2021-10-01 14:30:04 +03:00
gologger . Verbose ( ) . Msgf ( "[%s] Sent HTTP request to %s" , request . options . TemplateID , formedURL )
2021-11-05 03:01:41 +05:30
request . options . Output . Request ( request . options . TemplatePath , formedURL , request . Type ( ) . String ( ) , err )
2020-12-26 14:55:15 +05:30
duration := time . Since ( timeStart )
2021-02-05 12:36:01 +05:30
2021-02-08 01:55:53 +05:30
dumpedResponseHeaders , err := httputil . DumpResponse ( resp , false )
2021-02-05 12:36:01 +05:30
if err != nil {
return errors . Wrap ( err , "could not dump http response" )
2020-12-26 14:55:15 +05:30
}
2021-11-09 06:00:30 +05:30
var dumpedResponse [ ] redirectedResponse
2021-11-26 18:51:02 +05:30
var gotData [ ] byte
2021-08-30 12:37:36 +05:30
// If the status code is HTTP 101, we should not proceed with reading body.
if resp . StatusCode != http . StatusSwitchingProtocols {
var bodyReader io . Reader
2021-10-01 14:30:04 +03:00
if request . MaxSize != 0 {
bodyReader = io . LimitReader ( resp . Body , int64 ( request . MaxSize ) )
2022-10-07 20:10:00 +05:30
} else if request . options . Options . ResponseReadSize != 0 {
bodyReader = io . LimitReader ( resp . Body , int64 ( request . options . Options . ResponseReadSize ) )
2021-08-30 12:37:36 +05:30
} else {
bodyReader = resp . Body
2021-06-09 11:15:21 +05:30
}
2022-02-23 13:54:46 +01:00
data , err := io . ReadAll ( bodyReader )
2021-08-30 12:37:36 +05:30
if err != nil {
// Ignore body read due to server misconfiguration errors
if stringsutil . ContainsAny ( err . Error ( ) , "gzip: invalid header" ) {
2021-10-01 14:30:04 +03:00
gologger . Warning ( ) . Msgf ( "[%s] Server sent an invalid gzip header and it was not possible to read the uncompressed body for %s: %s" , request . options . TemplateID , formedURL , err . Error ( ) )
2021-08-30 12:37:36 +05:30
} else if ! stringsutil . ContainsAny ( err . Error ( ) , "unexpected EOF" , "user canceled" ) { // ignore EOF and random error
return errors . Wrap ( err , "could not read http body" )
}
}
2021-11-26 18:51:02 +05:30
gotData = data
2021-08-30 12:37:36 +05:30
resp . Body . Close ( )
2020-12-26 14:55:15 +05:30
2021-11-09 06:00:30 +05:30
dumpedResponse , err = dumpResponseWithRedirectChain ( resp , data )
2021-08-30 12:37:36 +05:30
if err != nil {
return errors . Wrap ( err , "could not read http response with redirect chain" )
}
2021-08-30 13:28:34 +05:30
} else {
2022-05-27 22:36:42 +05:30
dumpedResponse = [ ] redirectedResponse { { resp : resp , fullResponse : dumpedResponseHeaders , headers : dumpedResponseHeaders } }
2021-02-07 03:34:07 +05:30
}
2021-11-26 18:51:02 +05:30
// if nuclei-project is enabled store the response if not previously done
if request . options . ProjectFile != nil && ! fromCache {
if err := request . options . ProjectFile . Set ( dumpedRequest , resp , gotData ) ; err != nil {
return errors . Wrap ( err , "could not store in project file" )
2021-11-09 06:00:30 +05:30
}
2021-11-26 18:51:02 +05:30
}
2020-12-26 14:55:15 +05:30
2021-11-26 18:51:02 +05:30
for _ , response := range dumpedResponse {
if response . resp == nil {
continue // Skip nil responses
}
2022-11-09 14:18:56 +01:00
matchedURL := input . MetaInput . Input
2023-01-05 21:02:36 +05:30
if generatedRequest . rawRequest != nil {
if generatedRequest . rawRequest . FullURL != "" {
matchedURL = generatedRequest . rawRequest . FullURL
} else {
matchedURL = formedURL
}
2021-09-10 21:19:05 +05:30
}
2021-11-09 06:00:30 +05:30
if generatedRequest . request != nil {
matchedURL = generatedRequest . request . URL . String ( )
2021-09-10 21:19:05 +05:30
}
2022-05-30 15:19:09 +05:30
// Give precedence to the final URL from response
if response . resp . Request != nil {
if responseURL := response . resp . Request . URL . String ( ) ; responseURL != "" {
matchedURL = responseURL
}
}
2021-11-09 06:00:30 +05:30
finalEvent := make ( output . InternalEvent )
2021-10-16 00:17:33 +02:00
2022-11-09 14:18:56 +01:00
outputEvent := request . responseToDSLMap ( response . resp , input . MetaInput . Input , matchedURL , tostring . UnsafeToString ( dumpedRequest ) , tostring . UnsafeToString ( response . fullResponse ) , tostring . UnsafeToString ( response . body ) , tostring . UnsafeToString ( response . headers ) , duration , generatedRequest . meta )
2023-06-09 19:52:56 +05:30
// add response fields to template context and merge templatectx variables to output event
2023-08-31 18:03:01 +05:30
request . options . AddTemplateVars ( input . MetaInput , request . Type ( ) , request . ID , outputEvent )
outputEvent = generators . MergeMaps ( outputEvent , request . options . GetTemplateCtx ( input . MetaInput ) . GetAll ( ) )
2021-11-09 06:00:30 +05:30
if i := strings . LastIndex ( hostname , ":" ) ; i != - 1 {
hostname = hostname [ : i ]
2021-10-16 00:17:33 +02:00
}
2021-11-09 06:00:30 +05:30
outputEvent [ "curl-command" ] = curlCommand
2022-11-09 14:18:56 +01:00
if input . MetaInput . CustomIP != "" {
outputEvent [ "ip" ] = input . MetaInput . CustomIP
} else {
outputEvent [ "ip" ] = httpclientpool . Dialer . GetDialedIP ( hostname )
}
2022-01-13 13:22:43 +05:30
if request . options . Interactsh != nil {
request . options . Interactsh . MakePlaceholders ( generatedRequest . interactshURLs , outputEvent )
}
2021-11-09 06:00:30 +05:30
for k , v := range previousEvent {
finalEvent [ k ] = v
2020-12-26 14:55:15 +05:30
}
2021-03-09 14:45:04 +05:30
for k , v := range outputEvent {
2021-11-09 06:00:30 +05:30
finalEvent [ k ] = v
}
2022-01-13 13:22:43 +05:30
2021-11-09 06:00:30 +05:30
// Add to history the current request number metadata if asked by the user.
2022-10-15 11:49:04 +02:00
if request . NeedsRequestCondition ( ) {
2021-11-09 06:00:30 +05:30
for k , v := range outputEvent {
key := fmt . Sprintf ( "%s_%d" , k , requestCount )
2023-10-17 17:44:13 +05:30
if previousEvent [ key ] != nil {
previousEvent [ key ] = v
}
2021-11-09 06:00:30 +05:30
finalEvent [ key ] = v
}
2021-03-09 14:45:04 +05:30
}
2022-01-09 13:39:50 +01:00
// prune signature internal values if any
request . pruneSignatureInternalValues ( generatedRequest . meta )
2021-11-24 22:19:42 +05:30
event := eventcreator . CreateEventWithAdditionalOptions ( request , generators . MergeMaps ( generatedRequest . dynamicValues , finalEvent ) , request . options . Options . Debug || request . options . Options . DebugResponse , func ( internalWrappedEvent * output . InternalWrappedEvent ) {
2021-11-09 06:00:30 +05:30
internalWrappedEvent . OperatorsResult . PayloadValues = generatedRequest . meta
} )
2022-02-01 12:10:18 +01:00
if hasInteractMatchers {
2021-11-22 17:53:25 +05:30
event . UsesInteractsh = true
}
2021-10-01 14:24:45 +03:00
2021-11-09 06:00:30 +05:30
responseContentType := resp . Header . Get ( "Content-Type" )
2022-04-20 17:11:14 +02:00
isResponseTruncated := request . MaxSize > 0 && len ( gotData ) >= request . MaxSize
2022-11-09 14:18:56 +01:00
dumpResponse ( event , request , response . fullResponse , formedURL , responseContentType , isResponseTruncated , input . MetaInput . Input )
2021-09-29 19:43:46 +03:00
2021-11-09 06:00:30 +05:30
callback ( event )
2022-05-30 15:19:09 +05:30
// Skip further responses if we have stop-at-first-match and a match
2023-04-16 19:49:35 +02:00
if ( request . options . Options . StopAtFirstMatch || request . options . StopAtFirstMatch || request . StopAtFirstMatch ) && event . HasResults ( ) {
2022-05-30 15:19:09 +05:30
return nil
}
2021-11-09 06:00:30 +05:30
}
2021-09-29 19:43:46 +03:00
return nil
}
2021-12-02 15:57:52 +01:00
// handleSignature of the http request
func ( request * Request ) handleSignature ( generatedRequest * generatedRequest ) error {
switch request . Signature . Value {
case AWSSignature :
var awsSigner signer . Signer
2023-01-24 20:50:20 +05:30
allvars := generators . MergeMaps ( request . options . Options . Vars . AsMap ( ) , generatedRequest . dynamicValues )
2022-12-03 07:10:57 +05:30
awsopts := signer . AWSOptions {
2023-01-24 20:50:20 +05:30
AwsID : types . ToString ( allvars [ "aws-id" ] ) ,
AwsSecretToken : types . ToString ( allvars [ "aws-secret" ] ) ,
2022-12-03 07:10:57 +05:30
}
awsSigner , err := signerpool . Get ( request . options . Options , & signerpool . Configuration { SignerArgs : & awsopts } )
2021-12-02 15:57:52 +01:00
if err != nil {
return err
}
2023-01-24 20:50:20 +05:30
ctx := signer . GetCtxWithArgs ( allvars , signer . AwsDefaultVars )
2022-12-03 07:10:57 +05:30
err = awsSigner . SignHTTP ( ctx , generatedRequest . request . Request )
2021-12-02 15:57:52 +01:00
if err != nil {
return err
}
}
return nil
}
2020-12-29 01:30:07 +05:30
// setCustomHeaders sets the custom headers for generated request
2021-10-01 14:30:04 +03:00
func ( request * Request ) setCustomHeaders ( req * generatedRequest ) {
for k , v := range request . customHeaders {
2021-02-04 22:00:09 +05:30
if req . rawRequest != nil {
req . rawRequest . Headers [ k ] = v
2020-12-29 01:30:07 +05:30
} else {
2021-05-04 14:36:04 +02:00
kk , vv := strings . TrimSpace ( k ) , strings . TrimSpace ( v )
req . request . Header . Set ( kk , vv )
if kk == "Host" {
req . request . Host = vv
}
2020-12-29 01:30:07 +05:30
}
2020-12-26 14:55:15 +05:30
}
}
2021-10-30 13:17:47 +03:00
const CRLF = "\r\n"
2022-04-01 14:29:02 -05:00
func dumpResponse ( event * output . InternalWrappedEvent , request * Request , redirectedResponse [ ] byte , formedURL string , responseContentType string , isResponseTruncated bool , reqURL string ) {
cliOptions := request . options . Options
if cliOptions . Debug || cliOptions . DebugResponse || cliOptions . StoreResponse {
2021-10-30 13:17:47 +03:00
response := string ( redirectedResponse )
2021-11-01 20:45:54 +02:00
var highlightedResult string
if responseContentType == "application/octet-stream" || ( ( responseContentType == "" || responseContentType == "application/x-www-form-urlencoded" ) && responsehighlighter . HasBinaryContent ( response ) ) {
highlightedResult = createResponseHexDump ( event , response , cliOptions . NoColor )
} else {
highlightedResult = responsehighlighter . Highlight ( event . OperatorsResult , response , cliOptions . NoColor , false )
2021-10-30 13:17:47 +03:00
}
2022-03-07 13:32:17 +01:00
msg := "[%s] Dumped HTTP response %s\n\n%s"
if isResponseTruncated {
msg = "[%s] Dumped HTTP response (Truncated) %s\n\n%s"
}
2022-04-01 14:29:02 -05:00
fMsg := fmt . Sprintf ( msg , request . options . TemplateID , formedURL , highlightedResult )
if cliOptions . Debug || cliOptions . DebugResponse {
gologger . Debug ( ) . Msg ( fMsg )
}
if cliOptions . StoreResponse {
request . options . Output . WriteStoreDebugData ( reqURL , request . options . TemplateID , request . Type ( ) . String ( ) , fMsg )
}
2021-11-01 20:45:54 +02:00
}
}
func createResponseHexDump ( event * output . InternalWrappedEvent , response string , noColor bool ) string {
CRLFs := CRLF + CRLF
headerEndIndex := strings . Index ( response , CRLFs ) + len ( CRLFs )
if headerEndIndex > 0 {
headers := response [ 0 : headerEndIndex ]
responseBodyHexDump := hex . Dump ( [ ] byte ( response [ headerEndIndex : ] ) )
highlightedHeaders := responsehighlighter . Highlight ( event . OperatorsResult , headers , noColor , false )
highlightedResponse := responsehighlighter . Highlight ( event . OperatorsResult , responseBodyHexDump , noColor , true )
return fmt . Sprintf ( "%s\n%s" , highlightedHeaders , highlightedResponse )
} else {
return responsehighlighter . Highlight ( event . OperatorsResult , hex . Dump ( [ ] byte ( response ) ) , noColor , true )
2021-10-30 13:17:47 +03:00
}
}
2022-01-09 13:39:50 +01:00
func ( request * Request ) pruneSignatureInternalValues ( maps ... map [ string ] interface { } ) {
var signatureFieldsToSkip map [ string ] interface { }
switch request . Signature . Value {
case AWSSignature :
2022-02-07 16:41:55 +02:00
signatureFieldsToSkip = signer . AwsInternalOnlyVars
2022-01-09 13:39:50 +01:00
default :
return
}
for _ , m := range maps {
for fieldName := range signatureFieldsToSkip {
delete ( m , fieldName )
}
}
}
2022-11-09 14:18:56 +01:00
func ( request * Request ) newContext ( input * contextargs . Context ) context . Context {
if input . MetaInput . CustomIP != "" {
2023-09-04 10:24:34 +02:00
return context . WithValue ( context . Background ( ) , fastdialer . IP , input . MetaInput . CustomIP )
2022-11-09 14:18:56 +01:00
}
return context . Background ( )
}