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"
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"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
"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
2020-12-29 01:30:07 +05:30
"github.com/projectdiscovery/gologger"
2020-12-28 20:02:26 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/output"
2021-01-01 19:36:21 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
2021-10-07 01:40:49 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/expressions"
2020-12-28 20:02:26 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
2021-10-01 16:52:38 +03:00
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/eventcreator"
2021-10-01 14:24:45 +03:00
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/responsehighlighter"
2021-04-18 16:10:10 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/interactsh"
2021-01-01 15:28:28 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/tostring"
2021-01-16 12:06:27 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/http/httpclientpool"
2021-11-03 19:53:45 +05:30
templateTypes "github.com/projectdiscovery/nuclei/v2/pkg/templates/types"
2020-12-28 20:02:26 +05:30
"github.com/projectdiscovery/rawhttp"
2021-06-17 16:26:23 +02:00
"github.com/projectdiscovery/stringsutil"
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
2021-10-01 14:30:04 +03:00
func ( request * Request ) executeRaceRequest ( reqURL string , previous output . InternalEvent , callback protocols . OutputEventCallback ) error {
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
2021-10-01 14:30:04 +03:00
generator := request . newGenerator ( )
2021-11-04 17:13:47 +05:30
requestForDump , err := generator . Make ( reqURL , 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
}
2021-10-01 14:30:04 +03:00
if request . options . Options . Debug || request . options . Options . DebugRequests {
gologger . Info ( ) . Msgf ( "[%s] Dumped HTTP request for %s\n\n" , request . options . TemplateID , reqURL )
2021-03-01 05:18:31 +01:00
gologger . Print ( ) . Msgf ( "%s" , string ( dumpedRequest ) )
}
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 ++ {
generator := request . newGenerator ( )
2021-11-04 17:13:47 +05:30
generatedRequest , err := generator . Make ( reqURL , 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 ( )
2021-10-01 14:30:04 +03:00
err := request . executeRequest ( reqURL , 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
2021-10-01 14:30:04 +03:00
func ( request * Request ) executeParallelHTTP ( reqURL string , dynamicValues output . InternalEvent , callback protocols . OutputEventCallback ) error {
generator := request . newGenerator ( )
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-04 17:13:47 +05:30
generatedHttpRequest , err := generator . Make ( reqURL , dynamicValues )
2020-12-28 20:02:26 +05:30
if err != nil {
2021-11-10 18:11:42 +01:00
if err == io . EOF {
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
}
2021-11-10 18:11:42 +01:00
if reqURL == "" {
reqURL = generatedHttpRequest . URL ( )
}
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 { } )
2021-10-01 14:30:04 +03:00
err := request . executeRequest ( reqURL , 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
2021-10-01 14:30:04 +03:00
func ( request * Request ) executeTurboHTTP ( reqURL string , dynamicValues , previous output . InternalEvent , callback protocols . OutputEventCallback ) error {
generator := request . newGenerator ( )
2020-12-26 14:55:15 +05:30
// need to extract the target from the url
URL , err := url . Parse ( reqURL )
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-04 17:13:47 +05:30
generatedHttpRequest , err := generator . Make ( reqURL , dynamicValues )
2020-12-28 20:02:26 +05:30
if err != nil {
2021-11-10 18:11:42 +01:00
if err == io . EOF {
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-28 20:02:26 +05:30
}
2021-11-10 18:11:42 +01:00
if reqURL == "" {
reqURL = generatedHttpRequest . URL ( )
}
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
2021-10-01 14:30:04 +03:00
err := request . executeRequest ( reqURL , 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
}
2020-12-29 11:42:46 +05:30
// ExecuteWithResults executes the final request on a URL
2021-10-01 14:30:04 +03:00
func ( request * Request ) ExecuteWithResults ( reqURL string , dynamicValues , previous output . InternalEvent , callback protocols . OutputEventCallback ) error {
2020-12-26 14:55:15 +05:30
// verify if pipeline was requested
2021-10-01 14:30:04 +03:00
if request . Pipeline {
return request . executeTurboHTTP ( reqURL , 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 {
return request . executeRaceRequest ( reqURL , previous , 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 {
return request . executeParallelHTTP ( reqURL , dynamicValues , callback )
2020-12-26 14:55:15 +05:30
}
2021-10-01 14:30:04 +03:00
generator := request . newGenerator ( )
2020-12-26 14:55:15 +05:30
2021-03-08 19:01:40 +05:30
requestCount := 1
2020-12-28 20:02:26 +05:30
var requestErr error
for {
2021-10-01 14:30:04 +03:00
hasInteractMarkers := interactsh . HasMatchers ( request . CompiledOperators )
2021-04-16 16:56:41 +05:30
2021-11-04 17:13:47 +05:30
generatedHttpRequest , err := generator . Make ( reqURL , dynamicValues )
2020-12-28 20:02:26 +05:30
if err != nil {
2021-11-10 18:11:42 +01:00
if err == io . EOF {
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-28 20:02:26 +05:30
}
2021-11-10 18:11:42 +01:00
if reqURL == "" {
reqURL = generatedHttpRequest . URL ( )
}
2021-10-01 14:30:04 +03:00
request . dynamicValues = generatedHttpRequest . dynamicValues
2021-08-17 14:23:42 +05:30
// Check if hosts just keep erroring
2021-10-01 14:30:04 +03:00
if request . options . HostErrorsCache != nil && request . options . HostErrorsCache . Check ( reqURL ) {
2021-08-17 14:23:42 +05:30
break
}
2021-01-01 19:36:21 +05:30
var gotOutput bool
2021-10-01 14:30:04 +03:00
request . options . RateLimiter . Take ( )
err = request . executeRequest ( reqURL , generatedHttpRequest , previous , hasInteractMarkers , func ( event * output . InternalWrappedEvent ) {
2021-01-01 16:52:41 +05:30
// Add the extracts to the dynamic values if any.
2021-01-01 19:36:21 +05:30
if event . OperatorsResult != nil {
gotOutput = true
dynamicValues = generators . MergeMaps ( dynamicValues , event . OperatorsResult . DynamicValues )
2021-01-01 16:52:41 +05:30
}
2021-10-01 14:30:04 +03:00
if hasInteractMarkers && request . options . Interactsh != nil {
2021-11-04 17:13:47 +05:30
request . options . Interactsh . RequestEvent ( generatedHttpRequest . interactshURLs , & interactsh . RequestData {
2021-10-01 14:30:04 +03:00
MakeResultFunc : request . MakeResultEvent ,
2021-04-18 17:53:59 +05:30
Event : event ,
2021-10-01 14:30:04 +03:00
Operators : request . CompiledOperators ,
MatchFunc : request . Match ,
ExtractFunc : request . Extract ,
2021-04-18 17:53:59 +05:30
} )
2021-05-09 01:37:22 +05:30
} else {
callback ( event )
2021-04-16 16:56:41 +05:30
}
2021-03-09 14:45:04 +05:30
} , requestCount )
2021-10-07 04:50:34 +05:30
// If a variable is unresolved, skip all further requests
if err == errStopExecution {
break
}
2021-01-01 19:36:21 +05:30
if err != nil {
2021-10-01 14:30:04 +03:00
if request . options . HostErrorsCache != nil && request . options . HostErrorsCache . CheckError ( err ) {
request . options . HostErrorsCache . MarkFailed ( reqURL )
2021-08-17 14:23:42 +05:30
}
requestErr = err
2020-12-26 14:55:15 +05:30
}
2021-03-08 19:01:40 +05:30
requestCount ++
2021-10-01 14:30:04 +03:00
request . options . Progress . IncrementRequests ( )
2020-12-26 14:55:15 +05:30
2021-10-07 04:50:34 +05:30
// If this was a match and we want to stop at first match, skip all further requests.
2021-10-01 14:30:04 +03:00
if ( generatedHttpRequest . original . options . Options . StopAtFirstMatch || request . StopAtFirstMatch ) && gotOutput {
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
2021-10-01 16:52:38 +03:00
func ( request * Request ) executeRequest ( reqURL string , generatedRequest * generatedRequest , previousEvent output . InternalEvent , hasInteractMarkers 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
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-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 {
2021-10-07 01:40:49 +05:30
var dumpError error
2021-10-11 13:58:20 +03:00
dumpedRequest , dumpError = dump ( generatedRequest , reqURL )
2021-10-07 01:40:49 +05:30
if dumpError != nil {
return dumpError
}
dumpedRequestString := string ( dumpedRequest )
// Check if are there any unresolved variables. If yes, skip unless overriden by user.
2021-10-11 13:58:20 +03:00
if varErr := expressions . ContainsUnresolvedVariables ( dumpedRequestString ) ; varErr != nil && ! request . SkipVariablesCheck {
gologger . Warning ( ) . Msgf ( "[%s] Could not make http request for %s: %v\n" , request . options . TemplateID , reqURL , varErr )
2021-10-07 04:50:34 +05:30
return errStopExecution
2021-10-07 01:40:49 +05:30
}
2021-10-11 13:58:20 +03:00
if request . options . Options . Debug || request . options . Options . DebugRequests {
gologger . Info ( ) . Msgf ( "[%s] Dumped HTTP request for %s\n\n" , request . options . TemplateID , reqURL )
2021-10-07 01:48:48 +05:30
gologger . Print ( ) . Msgf ( "%s" , dumpedRequestString )
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 {
if generatedRequest . rawRequest != nil {
formedURL = generatedRequest . rawRequest . FullURL
2021-06-09 11:15:21 +05:30
if parsed , parseErr := url . Parse ( formedURL ) ; parseErr == nil {
hostname = parsed . Host
}
2021-10-01 14:30:04 +03:00
resp , err = generatedRequest . pipelinedClient . DoRaw ( generatedRequest . rawRequest . Method , reqURL , generatedRequest . rawRequest . Path , generators . ExpandMapValues ( generatedRequest . rawRequest . Headers ) , ioutil . NopCloser ( strings . NewReader ( generatedRequest . rawRequest . Data ) ) )
} 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 {
formedURL = generatedRequest . rawRequest . FullURL
2021-02-26 13:13:11 +05:30
if parsed , parseErr := url . Parse ( formedURL ) ; parseErr == nil {
hostname = parsed . Host
2021-01-16 12:06:27 +05:30
}
2021-10-01 14:30:04 +03:00
options := generatedRequest . original . rawhttpClient . Options
options . FollowRedirects = request . Redirects
options . CustomRawBytes = generatedRequest . rawRequest . UnsafeRawBytes
resp , err = generatedRequest . original . rawhttpClient . DoRawWithOptions ( generatedRequest . rawRequest . Method , reqURL , generatedRequest . rawRequest . Path , generators . ExpandMapValues ( generatedRequest . rawRequest . Headers ) , ioutil . NopCloser ( strings . NewReader ( generatedRequest . rawRequest . Data ) ) , options )
2020-12-26 14:55:15 +05:30
} else {
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-10-01 14:30:04 +03:00
resp , err = request . httpClient . Do ( generatedRequest . request )
2020-12-26 14:55:15 +05:30
}
}
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.
2021-10-01 14:30:04 +03:00
if resp != nil && resp . Body != nil && generatedRequest . rawRequest == nil {
2021-02-07 03:34:07 +05:30
_ , _ = io . CopyN ( ioutil . 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.
2021-08-26 02:43:58 +05:30
if hasInteractMarkers {
2021-10-01 14:30:04 +03:00
outputEvent := request . responseToDSLMap ( & http . Response { } , reqURL , 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 ]
}
outputEvent [ "ip" ] = httpclientpool . Dialer . GetDialedIP ( hostname )
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 {
_ , _ = io . CopyN ( ioutil . Discard , resp . Body , drainReqSize )
}
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
2021-10-25 18:22:33 +05:30
if ! request . Unsafe && resp != nil && generatedRequest . request != nil && resp . Request != nil {
2021-10-15 13:55:50 +05:30
bodyBytes , _ := generatedRequest . request . BodyBytes ( )
resp . Request . Body = ioutil . NopCloser ( bytes . NewReader ( bodyBytes ) )
command , _ := http2curl . GetCurlCommand ( resp . Request )
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-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 ) )
2021-08-30 12:37:36 +05:30
} else {
bodyReader = resp . Body
2021-06-09 11:15:21 +05:30
}
2021-11-09 06:00:30 +05:30
data , err := ioutil . 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" )
}
}
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 {
2021-11-09 06:00:30 +05:30
dumpedResponse = [ ] redirectedResponse { { fullResponse : dumpedResponseHeaders , headers : dumpedResponseHeaders } }
2021-02-07 03:34:07 +05:30
}
2021-11-09 06:00:30 +05:30
for _ , response := range dumpedResponse {
// 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 , response . body ) ; err != nil {
return errors . Wrap ( err , "could not store in project file" )
}
}
2020-12-26 14:55:15 +05:30
2021-11-09 06:00:30 +05:30
matchedURL := reqURL
if generatedRequest . rawRequest != nil && generatedRequest . rawRequest . FullURL != "" {
matchedURL = generatedRequest . rawRequest . FullURL
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
}
2021-11-09 06:00:30 +05:30
finalEvent := make ( output . InternalEvent )
2021-10-16 00:17:33 +02:00
2021-11-09 17:55:42 +05:30
outputEvent := request . responseToDSLMap ( response . resp , reqURL , matchedURL , tostring . UnsafeToString ( dumpedRequest ) , tostring . UnsafeToString ( response . fullResponse ) , tostring . UnsafeToString ( response . body ) , tostring . UnsafeToString ( response . headers ) , duration , generatedRequest . meta )
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
outputEvent [ "ip" ] = httpclientpool . Dialer . GetDialedIP ( hostname )
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
}
// Add to history the current request number metadata if asked by the user.
if request . ReqCondition {
for k , v := range outputEvent {
key := fmt . Sprintf ( "%s_%d" , k , requestCount )
previousEvent [ key ] = v
finalEvent [ key ] = v
}
2021-03-09 14:45:04 +05:30
}
2020-12-29 01:30:07 +05:30
2021-11-09 06:00:30 +05:30
event := eventcreator . CreateEventWithAdditionalOptions ( request , finalEvent , request . options . Options . Debug || request . options . Options . DebugResponse , func ( internalWrappedEvent * output . InternalWrappedEvent ) {
internalWrappedEvent . OperatorsResult . PayloadValues = generatedRequest . meta
} )
2021-10-01 14:24:45 +03:00
2021-11-09 06:00:30 +05:30
responseContentType := resp . Header . Get ( "Content-Type" )
dumpResponse ( event , request . options , response . fullResponse , formedURL , responseContentType )
2021-09-29 19:43:46 +03:00
2021-11-09 06:00:30 +05:30
callback ( event )
}
2021-09-29 19:43:46 +03:00
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"
2021-11-01 20:45:54 +02:00
func dumpResponse ( event * output . InternalWrappedEvent , requestOptions * protocols . ExecuterOptions , redirectedResponse [ ] byte , formedURL string , responseContentType string ) {
cliOptions := requestOptions . Options
if cliOptions . Debug || cliOptions . DebugResponse {
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
}
2021-11-01 20:45:54 +02:00
gologger . Debug ( ) . Msgf ( "[%s] Dumped HTTP response for %s\n\n%s" , requestOptions . TemplateID , formedURL , highlightedResult )
}
}
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
}
}