2021-09-22 22:41:07 +05:30
package ssl
import (
"context"
2022-01-25 13:26:22 +01:00
"crypto/tls"
2022-04-01 14:29:02 -05:00
"fmt"
2021-09-22 22:41:07 +05:30
"net"
"net/url"
"strings"
"time"
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-11-01 18:02:45 +05:30
"github.com/projectdiscovery/cryptoutil"
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"
2021-11-05 03:01:41 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/expressions"
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"
2021-09-22 22:41:07 +05:30
"github.com/projectdiscovery/nuclei/v2/pkg/protocols/network/networkclientpool"
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-01-24 16:15:02 +01:00
ztls "github.com/zmap/zcrypto/tls"
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:"-" `
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-03-07 14:07:30 +05:30
CiperSuites [ ] string ` yaml:"cipher_suites,omitempty" `
cipherSuites [ ] uint16
2021-09-22 22:41:07 +05:30
// cache any variables that may be needed for operation.
dialer * fastdialer . Dialer
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-03-07 14:07:30 +05:30
if request . options . Options . ZTLS {
request . cipherSuites , err = toZTLSCiphers ( request . CiperSuites )
} else {
request . cipherSuites , err = toTLSCiphers ( request . CiperSuites )
}
if err != nil {
return errors . Wrap ( err , "invalid ciphersuites specified" )
}
2021-11-03 02:34:48 +05:30
if len ( request . Matchers ) > 0 || len ( request . Extractors ) > 0 {
compiled := & request . Operators
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.
2021-11-03 02:34:48 +05:30
func ( request * Request ) ExecuteWithResults ( input string , dynamicValues , previous output . InternalEvent , callback protocols . OutputEventCallback ) error {
2021-09-22 22:41:07 +05:30
address , err := getAddress ( input )
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
finalAddress , dataErr := expressions . EvaluateByte ( [ ] byte ( request . Address ) , payloadValues )
if dataErr != nil {
requestOptions . Output . Request ( requestOptions . TemplateID , input , request . Type ( ) . String ( ) , dataErr )
requestOptions . Progress . IncrementFailedRequestsBy ( 1 )
return errors . Wrap ( dataErr , "could not evaluate template expressions" )
}
addressToDial := string ( finalAddress )
2022-01-25 13:26:22 +01:00
var minVersion , maxVersion uint16
2022-01-24 18:20:29 +01:00
if request . MinVersion != "" {
version , err := toVersion ( request . MinVersion )
if err != nil {
return err
}
2022-01-25 13:26:22 +01:00
minVersion = version
2022-01-24 18:20:29 +01:00
}
if request . MaxVersion != "" {
version , err := toVersion ( request . MaxVersion )
if err != nil {
return err
}
2022-01-25 13:26:22 +01:00
maxVersion = version
2022-01-24 18:20:29 +01:00
}
2022-01-25 13:26:22 +01:00
var conn net . Conn
2022-01-25 20:48:21 +01:00
if request . options . Options . ZTLS {
2022-01-25 20:57:54 +01:00
zconfig := & ztls . Config { InsecureSkipVerify : true , ServerName : hostname }
if minVersion > 0 {
zconfig . MinVersion = minVersion
}
if maxVersion > 0 {
zconfig . MaxVersion = maxVersion
}
2022-03-07 14:07:30 +05:30
if len ( request . cipherSuites ) > 0 {
zconfig . CipherSuites = request . cipherSuites
2022-01-25 20:57:54 +01:00
}
2022-01-25 20:48:21 +01:00
conn , err = request . dialer . DialZTLSWithConfig ( context . Background ( ) , "tcp" , addressToDial , zconfig )
2022-01-25 13:26:22 +01:00
} else {
2022-01-25 20:57:54 +01:00
config := & tls . Config { InsecureSkipVerify : true , ServerName : hostname }
if minVersion > 0 {
config . MinVersion = minVersion
}
if maxVersion > 0 {
config . MaxVersion = maxVersion
}
2022-03-07 14:07:30 +05:30
if len ( request . cipherSuites ) > 0 {
config . CipherSuites = request . cipherSuites
2022-01-24 18:20:29 +01:00
}
2022-01-25 13:26:22 +01:00
conn , err = request . dialer . DialTLSWithConfig ( context . Background ( ) , "tcp" , addressToDial , config )
2022-01-24 18:20:29 +01:00
}
2021-09-22 22:41:07 +05:30
if err != nil {
2021-11-05 03:01:41 +05:30
requestOptions . Output . Request ( requestOptions . TemplateID , input , request . Type ( ) . String ( ) , err )
requestOptions . Progress . IncrementFailedRequestsBy ( 1 )
2021-09-22 22:41:07 +05:30
return errors . Wrap ( err , "could not connect to server" )
}
defer conn . Close ( )
2021-11-05 03:01:41 +05:30
_ = conn . SetReadDeadline ( time . Now ( ) . Add ( time . Duration ( requestOptions . Options . Timeout ) * time . Second ) )
2021-09-22 22:41:07 +05:30
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 {
msg := fmt . Sprintf ( "[%s] Dumped SSL request for %s" , requestOptions . TemplateID , input )
if requestOptions . Options . Debug || requestOptions . Options . DebugRequests {
gologger . Debug ( ) . Str ( "address" , input ) . Msg ( msg )
}
if requestOptions . Options . StoreResponse {
request . options . Output . WriteStoreDebugData ( input , request . options . TemplateID , request . Type ( ) . String ( ) , msg )
}
2021-11-01 18:02:45 +05:30
}
2022-01-25 20:48:21 +01:00
var (
tlsData interface { }
certNotAfter int64
)
if request . options . Options . ZTLS {
connTLS , ok := conn . ( * ztls . Conn )
if ! ok {
return nil
}
state := connTLS . ConnectionState ( )
if len ( state . PeerCertificates ) == 0 {
return nil
}
tlsData = cryptoutil . ZTLSGrab ( connTLS )
cert := connTLS . ConnectionState ( ) . PeerCertificates [ 0 ]
certNotAfter = cert . NotAfter . Unix ( )
} else {
connTLS , ok := conn . ( * tls . Conn )
if ! ok {
return nil
}
state := connTLS . ConnectionState ( )
if len ( state . PeerCertificates ) == 0 {
return nil
}
tlsData = cryptoutil . TLSGrab ( & state )
cert := connTLS . ConnectionState ( ) . PeerCertificates [ 0 ]
certNotAfter = cert . NotAfter . Unix ( )
2021-09-22 22:41:07 +05:30
}
2022-01-25 20:48:21 +01:00
jsonData , _ := jsoniter . Marshal ( tlsData )
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-01-25 20:48:21 +01:00
data [ "not_after" ] = float64 ( certNotAfter )
2021-11-03 02:34:48 +05:30
data [ "ip" ] = request . dialer . GetDialedIP ( hostname )
2021-09-22 22:41:07 +05:30
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 {
msg := fmt . Sprintf ( "[%s] Dumped SSL response for %s" , requestOptions . TemplateID , input )
if requestOptions . Options . Debug || requestOptions . Options . DebugResponse {
gologger . Debug ( ) . Msg ( msg )
2021-11-05 03:01:41 +05:30
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 {
request . options . Output . WriteStoreDebugData ( input , request . options . TemplateID , request . Type ( ) . String ( ) , fmt . Sprintf ( "%s\n%s" , msg , jsonDataString ) )
}
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
}