2021-09-22 22:41:07 +05:30
package ssl
import (
2022-04-01 14:29:02 -05:00
"fmt"
2021-09-22 22:41:07 +05:30
"net"
"net/url"
"strings"
"time"
2022-09-01 23:56:55 +05:30
"github.com/fatih/structs"
2021-11-01 18:02:45 +05:30
jsoniter "github.com/json-iterator/go"
2021-09-22 22:41:07 +05:30
"github.com/pkg/errors"
2021-11-25 17:09:20 +02:00
2021-09-22 22:41:07 +05:30
"github.com/projectdiscovery/fastdialer/fastdialer"
2021-09-24 19:35:00 +05:30
"github.com/projectdiscovery/gologger"
2021-09-22 22:41:07 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/operators"
2021-10-29 18:26:06 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/operators/extractors"
"github.com/projectdiscovery/nuclei/v2/pkg/operators/matchers"
2021-09-22 22:41:07 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/output"
"github.com/projectdiscovery/nuclei/v2/pkg/protocols"
2022-10-03 12:12:20 +02:00
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/contextargs"
2021-11-05 03:01:41 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/expressions"
2022-07-22 01:35:21 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/generators"
2021-10-29 18:26:06 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/eventcreator"
2021-11-01 18:02:45 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/helpers/responsehighlighter"
2022-08-25 15:37:03 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/utils/vardump"
2022-07-22 01:35:21 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/dns"
2021-09-22 22:41:07 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/network/networkclientpool"
2022-09-01 23:56:55 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/utils"
2021-11-03 19:53:45 +05:30
templateTypes "github.com/projectdiscovery/nuclei/v2/pkg/templates/types"
2021-09-22 22:41:07 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/types"
2022-09-01 23:56:55 +05:30
"github.com/projectdiscovery/tlsx/pkg/tlsx"
"github.com/projectdiscovery/tlsx/pkg/tlsx/clients"
2021-09-22 22:41:07 +05:30
)
// Request is a request for the SSL protocol
type Request struct {
// Operators for the current request go here.
operators . Operators ` yaml:",inline,omitempty" `
CompiledOperators * operators . Operators ` yaml:"-" `
2022-09-01 23:56:55 +05:30
2021-11-05 03:01:41 +05:30
// description: |
// Address contains address for the request
2022-01-25 13:26:22 +01:00
Address string ` yaml:"address,omitempty" jsonschema:"title=address for the ssl request,description=Address contains address for the request" `
// description: |
// Minimum tls version - auto if not specified.
// values:
// - "sslv3"
// - "tls10"
// - "tls11"
// - "tls12"
// - "tls13"
MinVersion string ` yaml:"min_version,omitempty" jsonschema:"title=TLS version,description=Minimum tls version - automatic if not specified.,enum=sslv3,enum=tls10,enum=tls11,enum=tls12,enum=tls13" `
// description: |
// Max tls version - auto if not specified.
// values:
// - "sslv3"
// - "tls10"
// - "tls11"
// - "tls12"
// - "tls13"
MaxVersion string ` yaml:"max_version,omitempty" jsonschema:"title=TLS version,description=Max tls version - automatic if not specified.,enum=sslv3,enum=tls10,enum=tls11,enum=tls12,enum=tls13" `
// description: |
2022-01-25 20:48:21 +01:00
// Client Cipher Suites - auto if not specified.
2022-09-01 23:56:55 +05:30
CiperSuites [ ] string ` yaml:"cipher_suites,omitempty" `
2021-09-22 22:41:07 +05:30
// cache any variables that may be needed for operation.
dialer * fastdialer . Dialer
2022-09-01 23:56:55 +05:30
tlsx * tlsx . Service
2021-09-22 22:41:07 +05:30
options * protocols . ExecuterOptions
}
// Compile compiles the request generators preparing any requests possible.
2021-11-03 02:34:48 +05:30
func ( request * Request ) Compile ( options * protocols . ExecuterOptions ) error {
request . options = options
2021-09-22 22:41:07 +05:30
client , err := networkclientpool . Get ( options . Options , & networkclientpool . Configuration { } )
if err != nil {
return errors . Wrap ( err , "could not get network client" )
}
2021-11-03 02:34:48 +05:30
request . dialer = client
2021-09-22 22:41:07 +05:30
2022-09-01 23:56:55 +05:30
tlsxOptions := & clients . Options {
AllCiphers : true ,
ScanMode : "auto" ,
Expired : true ,
SelfSigned : true ,
MisMatched : true ,
MinVersion : request . MinVersion ,
MaxVersion : request . MaxVersion ,
Ciphers : request . CiperSuites ,
WildcardCertCheck : true ,
Retries : request . options . Options . Retries ,
Timeout : request . options . Options . Timeout ,
Fastdialer : client ,
}
if options . Options . ZTLS {
tlsxOptions . ScanMode = "ztls"
2022-03-07 14:07:30 +05:30
}
2022-09-01 23:56:55 +05:30
tlsxService , err := tlsx . New ( tlsxOptions )
2022-03-07 14:07:30 +05:30
if err != nil {
2022-09-01 23:56:55 +05:30
return errors . Wrap ( err , "could not create tlsx service" )
2022-03-07 14:07:30 +05:30
}
2022-09-01 23:56:55 +05:30
request . tlsx = tlsxService
2022-03-07 14:07:30 +05:30
2021-11-03 02:34:48 +05:30
if len ( request . Matchers ) > 0 || len ( request . Extractors ) > 0 {
compiled := & request . Operators
2022-06-24 23:09:27 +05:30
compiled . ExcludeMatchers = options . ExcludeMatchers
compiled . TemplateID = options . TemplateID
2021-09-22 22:41:07 +05:30
if err := compiled . Compile ( ) ; err != nil {
return errors . Wrap ( err , "could not compile operators" )
}
2021-11-03 02:34:48 +05:30
request . CompiledOperators = compiled
2021-09-22 22:41:07 +05:30
}
return nil
}
// Requests returns the total number of requests the rule will perform
2021-11-03 02:34:48 +05:30
func ( request * Request ) Requests ( ) int {
2021-09-22 22:41:07 +05:30
return 1
}
// GetID returns the ID for the request if any.
2021-11-03 02:34:48 +05:30
func ( request * Request ) GetID ( ) string {
2021-09-22 22:41:07 +05:30
return ""
}
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
2022-10-03 12:12:20 +02:00
func ( request * Request ) ExecuteWithResults ( input * contextargs . Context , dynamicValues , previous output . InternalEvent , callback protocols . OutputEventCallback ) error {
2022-11-09 14:18:56 +01:00
address , err := getAddress ( input . MetaInput . Input )
2021-09-22 22:41:07 +05:30
if err != nil {
return nil
}
2021-11-05 03:01:41 +05:30
hostname , port , _ := net . SplitHostPort ( address )
2021-09-22 22:41:07 +05:30
2021-11-05 03:01:41 +05:30
requestOptions := request . options
payloadValues := make ( map [ string ] interface { } )
for k , v := range dynamicValues {
payloadValues [ k ] = v
}
payloadValues [ "Hostname" ] = address
payloadValues [ "Host" ] = hostname
payloadValues [ "Port" ] = port
2022-07-22 01:35:21 +05:30
hostnameVariables := dns . GenerateVariables ( hostname )
values := generators . MergeMaps ( payloadValues , hostnameVariables )
variablesMap := request . options . Variables . Evaluate ( values )
payloadValues = generators . MergeMaps ( variablesMap , payloadValues )
2022-11-01 20:28:50 +05:30
if vardump . EnableVarDump {
2022-08-25 15:37:03 +05:30
gologger . Debug ( ) . Msgf ( "Protocol request variables: \n%s\n" , vardump . DumpVariables ( payloadValues ) )
}
2021-11-05 03:01:41 +05:30
finalAddress , dataErr := expressions . EvaluateByte ( [ ] byte ( request . Address ) , payloadValues )
if dataErr != nil {
2022-11-09 14:18:56 +01:00
requestOptions . Output . Request ( requestOptions . TemplateID , input . MetaInput . Input , request . Type ( ) . String ( ) , dataErr )
2021-11-05 03:01:41 +05:30
requestOptions . Progress . IncrementFailedRequestsBy ( 1 )
return errors . Wrap ( dataErr , "could not evaluate template expressions" )
}
addressToDial := string ( finalAddress )
2022-09-01 23:56:55 +05:30
host , port , err := net . SplitHostPort ( addressToDial )
if err != nil {
return errors . Wrap ( err , "could not split input host port" )
2022-01-24 18:20:29 +01:00
}
2022-09-01 23:56:55 +05:30
response , err := request . tlsx . Connect ( host , host , port )
2021-09-22 22:41:07 +05:30
if err != nil {
2022-11-09 14:18:56 +01:00
requestOptions . Output . Request ( requestOptions . TemplateID , input . MetaInput . Input , request . Type ( ) . String ( ) , err )
2021-11-05 03:01:41 +05:30
requestOptions . Progress . IncrementFailedRequestsBy ( 1 )
2021-09-22 22:41:07 +05:30
return errors . Wrap ( err , "could not connect to server" )
}
2021-11-05 03:01:41 +05:30
requestOptions . Output . Request ( requestOptions . TemplateID , address , request . Type ( ) . String ( ) , err )
2021-09-24 19:35:00 +05:30
gologger . Verbose ( ) . Msgf ( "Sent SSL request to %s" , address )
2022-04-01 14:29:02 -05:00
if requestOptions . Options . Debug || requestOptions . Options . DebugRequests || requestOptions . Options . StoreResponse {
2022-11-09 14:18:56 +01:00
msg := fmt . Sprintf ( "[%s] Dumped SSL request for %s" , requestOptions . TemplateID , input . MetaInput . Input )
2022-04-01 14:29:02 -05:00
if requestOptions . Options . Debug || requestOptions . Options . DebugRequests {
2022-11-09 14:18:56 +01:00
gologger . Debug ( ) . Str ( "address" , input . MetaInput . Input ) . Msg ( msg )
2022-04-01 14:29:02 -05:00
}
if requestOptions . Options . StoreResponse {
2022-11-09 14:18:56 +01:00
request . options . Output . WriteStoreDebugData ( input . MetaInput . Input , request . options . TemplateID , request . Type ( ) . String ( ) , msg )
2022-04-01 14:29:02 -05:00
}
2021-11-01 18:02:45 +05:30
}
2022-09-01 23:56:55 +05:30
jsonData , _ := jsoniter . Marshal ( response )
2021-11-01 18:02:45 +05:30
jsonDataString := string ( jsonData )
2021-09-22 22:41:07 +05:30
data := make ( map [ string ] interface { } )
2021-11-01 18:02:45 +05:30
2021-11-22 17:53:25 +05:30
data [ "type" ] = request . Type ( ) . String ( )
2021-11-01 18:02:45 +05:30
data [ "response" ] = jsonDataString
2021-09-22 22:41:07 +05:30
data [ "host" ] = input
2021-11-05 03:01:41 +05:30
data [ "matched" ] = addressToDial
2022-11-09 14:18:56 +01:00
if input . MetaInput . CustomIP != "" {
data [ "ip" ] = input . MetaInput . CustomIP
} else {
data [ "ip" ] = request . dialer . GetDialedIP ( hostname )
}
2022-06-03 03:02:45 -05:00
data [ "template-path" ] = requestOptions . TemplatePath
data [ "template-id" ] = requestOptions . TemplateID
data [ "template-info" ] = requestOptions . TemplateInfo
2022-07-22 01:35:21 +05:30
for k , v := range payloadValues {
data [ k ] = v
}
2022-09-01 23:56:55 +05:30
// Convert response to key value pairs and first cert chain item as well
responseParsed := structs . New ( response )
for _ , f := range responseParsed . Fields ( ) {
tag := utils . CleanStructFieldJSONTag ( f . Tag ( "json" ) )
if tag == "" || f . IsZero ( ) {
continue
}
data [ tag ] = f . Value ( )
}
responseParsed = structs . New ( response . CertificateResponse )
for _ , f := range responseParsed . Fields ( ) {
tag := utils . CleanStructFieldJSONTag ( f . Tag ( "json" ) )
if tag == "" || f . IsZero ( ) {
continue
}
data [ tag ] = f . Value ( )
}
2021-11-05 03:01:41 +05:30
event := eventcreator . CreateEvent ( request , data , requestOptions . Options . Debug || requestOptions . Options . DebugResponse )
2022-04-01 14:29:02 -05:00
if requestOptions . Options . Debug || requestOptions . Options . DebugResponse || requestOptions . Options . StoreResponse {
2022-11-09 14:18:56 +01:00
msg := fmt . Sprintf ( "[%s] Dumped SSL response for %s" , requestOptions . TemplateID , input . MetaInput . Input )
2022-04-01 14:29:02 -05:00
if requestOptions . Options . Debug || requestOptions . Options . DebugResponse {
2022-06-03 03:02:45 -05:00
gologger . Debug ( ) . Msg ( msg )
gologger . Print ( ) . Msgf ( "%s" , responsehighlighter . Highlight ( event . OperatorsResult , jsonDataString , requestOptions . Options . NoColor , false ) )
2022-04-01 14:29:02 -05:00
}
if requestOptions . 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 , jsonDataString ) )
2022-04-01 14:29:02 -05:00
}
2021-11-01 18:02:45 +05:30
}
2021-10-29 18:26:06 +05:30
callback ( event )
2021-09-22 22:41:07 +05:30
return nil
}
2021-11-26 16:23:54 +05:30
// RequestPartDefinitions contains a mapping of request part definitions and their
// description. Multiple definitions are separated by commas.
// Definitions not having a name (generated on runtime) are prefixed & suffixed by <>.
var RequestPartDefinitions = map [ string ] string {
"type" : "Type is the type of request made" ,
"response" : "JSON SSL protocol handshake details" ,
"not_after" : "Timestamp after which the remote cert expires" ,
"host" : "Host is the input to the template" ,
"matched" : "Matched is the input which was matched upon" ,
}
2021-09-22 22:41:07 +05:30
// getAddress returns the address of the host to make request to
func getAddress ( toTest string ) ( string , error ) {
if strings . Contains ( toTest , "://" ) {
parsed , err := url . Parse ( toTest )
if err != nil {
return "" , err
}
_ , port , _ := net . SplitHostPort ( parsed . Host )
2021-11-03 18:58:00 +05:30
if strings . ToLower ( parsed . Scheme ) == "https" && port == "" {
2021-09-22 22:41:07 +05:30
toTest = net . JoinHostPort ( parsed . Host , "443" )
} else {
toTest = parsed . Host
}
2021-09-24 19:35:00 +05:30
return toTest , nil
2021-09-22 22:41:07 +05:30
}
return toTest , nil
}
2021-10-29 18:26:06 +05:30
// Match performs matching operation for a matcher on model and returns:
// true and a list of matched snippets if the matcher type is supports it
// otherwise false and an empty string slice
2021-11-03 02:34:48 +05:30
func ( request * Request ) Match ( data map [ string ] interface { } , matcher * matchers . Matcher ) ( bool , [ ] string ) {
2021-10-29 18:26:06 +05:30
return protocols . MakeDefaultMatchFunc ( data , matcher )
}
// Extract performs extracting operation for an extractor on model and returns true or false.
2021-11-03 02:34:48 +05:30
func ( request * Request ) Extract ( data map [ string ] interface { } , matcher * extractors . Extractor ) map [ string ] struct { } {
2021-10-29 18:26:06 +05:30
return protocols . MakeDefaultExtractFunc ( data , matcher )
}
// MakeResultEvent creates a result event from internal wrapped event
2021-11-03 02:34:48 +05:30
func ( request * Request ) MakeResultEvent ( wrapped * output . InternalWrappedEvent ) [ ] * output . ResultEvent {
return protocols . MakeDefaultResultEvent ( request , wrapped )
2021-10-29 18:26:06 +05:30
}
// GetCompiledOperators returns a list of the compiled operators
2021-11-03 02:34:48 +05:30
func ( request * Request ) GetCompiledOperators ( ) [ ] * operators . Operators {
return [ ] * operators . Operators { request . CompiledOperators }
2021-10-29 18:26:06 +05:30
}
2021-11-03 19:53:45 +05:30
// Type returns the type of the protocol request
func ( request * Request ) Type ( ) templateTypes . ProtocolType {
return templateTypes . SSLProtocol
}
2021-11-03 02:34:48 +05:30
func ( request * Request ) MakeResultEventItem ( wrapped * output . InternalWrappedEvent ) * output . ResultEvent {
2021-09-22 22:41:07 +05:30
data := & output . ResultEvent {
2021-11-03 02:34:48 +05:30
TemplateID : types . ToString ( request . options . TemplateID ) ,
TemplatePath : types . ToString ( request . options . TemplatePath ) ,
Info : request . options . TemplateInfo ,
2021-11-22 17:53:25 +05:30
Type : types . ToString ( wrapped . InternalEvent [ "type" ] ) ,
2021-09-22 22:41:07 +05:30
Host : types . ToString ( wrapped . InternalEvent [ "host" ] ) ,
Matched : types . ToString ( wrapped . InternalEvent [ "host" ] ) ,
Metadata : wrapped . OperatorsResult . PayloadValues ,
ExtractedResults : wrapped . OperatorsResult . OutputExtracts ,
Timestamp : time . Now ( ) ,
2021-11-22 17:53:25 +05:30
MatcherStatus : true ,
2021-09-22 22:41:07 +05:30
IP : types . ToString ( wrapped . InternalEvent [ "ip" ] ) ,
}
return data
}