diff --git a/pkg/js/generated/go/librdp/rdp.go b/pkg/js/generated/go/librdp/rdp.go index aee252c433..ded295cbde 100644 --- a/pkg/js/generated/go/librdp/rdp.go +++ b/pkg/js/generated/go/librdp/rdp.go @@ -15,14 +15,16 @@ func init() { module.Set( gojs.Objects{ // Functions - "CheckRDPAuth": lib_rdp.CheckRDPAuth, - "IsRDP": lib_rdp.IsRDP, + "CheckRDPAuth": lib_rdp.CheckRDPAuth, + "CheckRDPEncryption": lib_rdp.CheckRDPEncryption, + "IsRDP": lib_rdp.IsRDP, // Var and consts // Objects / Classes - "CheckRDPAuthResponse": gojs.GetClassConstructor[lib_rdp.CheckRDPAuthResponse](&lib_rdp.CheckRDPAuthResponse{}), - "IsRDPResponse": gojs.GetClassConstructor[lib_rdp.IsRDPResponse](&lib_rdp.IsRDPResponse{}), + "CheckRDPAuthResponse": gojs.GetClassConstructor[lib_rdp.CheckRDPAuthResponse](&lib_rdp.CheckRDPAuthResponse{}), + "CheckRDPEncryptionResponse": gojs.GetClassConstructor[lib_rdp.RDPEncryptionResponse](&lib_rdp.RDPEncryptionResponse{}), + "IsRDPResponse": gojs.GetClassConstructor[lib_rdp.IsRDPResponse](&lib_rdp.IsRDPResponse{}), }, ).Register() } diff --git a/pkg/js/generated/ts/rdp.ts b/pkg/js/generated/ts/rdp.ts index 7858b78319..28a28468a5 100755 --- a/pkg/js/generated/ts/rdp.ts +++ b/pkg/js/generated/ts/rdp.ts @@ -1,5 +1,3 @@ - - /** * CheckRDPAuth checks if the given host and port are running rdp server * with authentication and returns their metadata. @@ -15,7 +13,19 @@ export function CheckRDPAuth(host: string, port: number): CheckRDPAuthResponse | return null; } - +/** + * CheckRDPEncryption checks the RDP server's supported security layers and encryption levels. + * It tests different protocols and ciphers to determine what is supported. + * @example + * ```javascript + * const rdp = require('nuclei/rdp'); + * const encryption = rdp.CheckRDPEncryption('acme.com', 3389); + * log(toJSON(encryption)); + * ``` + */ +export function CheckRDPEncryption(host: string, port: number): RDPEncryptionResponse | null { + return null; +} /** * IsRDP checks if the given host and port are running rdp server. @@ -33,8 +43,6 @@ export function IsRDP(host: string, port: number): IsRDPResponse | null { return null; } - - /** * CheckRDPAuthResponse is the response from the CheckRDPAuth function. * this is returned by CheckRDPAuth function. @@ -52,7 +60,30 @@ export interface CheckRDPAuthResponse { Auth?: boolean, } - +/** + * RDPEncryptionResponse is the response from the CheckRDPEncryption function. + * This is returned by CheckRDPEncryption function. + * @example + * ```javascript + * const rdp = require('nuclei/rdp'); + * const encryption = rdp.CheckRDPEncryption('acme.com', 3389); + * log(toJSON(encryption)); + * ``` + */ +export interface RDPEncryptionResponse { + // Security Layer Protocols + NativeRDP: boolean; + SSL: boolean; + CredSSP: boolean; + RDSTLS: boolean; + CredSSPWithEarlyUserAuth: boolean; + + // Encryption Levels + RC4_40bit: boolean; + RC4_56bit: boolean; + RC4_128bit: boolean; + FIPS140_1: boolean; +} /** * IsRDPResponse is the response from the IsRDP function. @@ -71,8 +102,6 @@ export interface IsRDPResponse { OS?: string, } - - /** * ServiceRDP Interface */ diff --git a/pkg/js/libs/rdp/memo.rdp.go b/pkg/js/libs/rdp/memo.rdp.go index 0c0b420127..f295e97b7b 100755 --- a/pkg/js/libs/rdp/memo.rdp.go +++ b/pkg/js/libs/rdp/memo.rdp.go @@ -39,3 +39,19 @@ func memoizedcheckRDPAuth(executionId string, host string, port int) (CheckRDPAu return CheckRDPAuthResponse{}, errors.New("could not convert cached result") } + +func memoizedcheckRDPEncryption(executionId string, host string, port int) (RDPEncryptionResponse, error) { + hash := "checkRDPEncryption" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + + v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) { + return checkRDPEncryption(executionId, host, port) + }) + if err != nil { + return RDPEncryptionResponse{}, err + } + if value, ok := v.(RDPEncryptionResponse); ok { + return value, nil + } + + return RDPEncryptionResponse{}, errors.New("could not convert cached result") +} diff --git a/pkg/js/libs/rdp/rdp.go b/pkg/js/libs/rdp/rdp.go index 9ccffb92d9..b53cf17aca 100644 --- a/pkg/js/libs/rdp/rdp.go +++ b/pkg/js/libs/rdp/rdp.go @@ -3,6 +3,8 @@ package rdp import ( "context" "fmt" + "net" + "strconv" "time" "github.com/praetorian-inc/fingerprintx/pkg/plugins" @@ -127,3 +129,211 @@ func checkRDPAuth(executionId string, host string, port int) (CheckRDPAuthRespon resp.PluginInfo = pluginInfo return resp, nil } + +type ( + SecurityLayer string +) + +const ( + SecurityLayerNativeRDP = "NativeRDP" + SecurityLayerSSL = "SSL" + SecurityLayerCredSSP = "CredSSP" + SecurityLayerRDSTLS = "RDSTLS" + SecurityLayerCredSSPWithEarlyUserAuth = "CredSSPWithEarlyUserAuth" +) + +type ( + EncryptionLevel string +) + +const ( + EncryptionLevelRC4_40bit = "RC4_40bit" + EncryptionLevelRC4_56bit = "RC4_56bit" + EncryptionLevelRC4_128bit = "RC4_128bit" + EncryptionLevelFIPS140_1 = "FIPS140_1" +) + +type ( + // RDPEncryptionResponse is the response from the CheckRDPEncryption function. + // This is returned by CheckRDPEncryption function. + // @example + // ```javascript + // const rdp = require('nuclei/rdp'); + // const encryption = rdp.CheckRDPEncryption('acme.com', 3389); + // log(toJSON(encryption)); + // ``` + RDPEncryptionResponse struct { + // Protocols + NativeRDP bool + SSL bool + CredSSP bool + RDSTLS bool + CredSSPWithEarlyUserAuth bool + + // EncryptionLevels + RC4_40bit bool + RC4_56bit bool + RC4_128bit bool + FIPS140_1 bool + } +) + +// CheckRDPEncryption checks the RDP server's supported security layers and encryption levels. +// It tests different protocols and ciphers to determine what is supported. +// @example +// ```javascript +// const rdp = require('nuclei/rdp'); +// const encryption = rdp.CheckRDPEncryption('acme.com', 3389); +// log(toJSON(encryption)); +// ``` +func CheckRDPEncryption(ctx context.Context, host string, port int) (RDPEncryptionResponse, error) { + executionId := ctx.Value("executionId").(string) + return memoizedcheckRDPEncryption(executionId, host, port) +} + +// @memo +func checkRDPEncryption(executionId string, host string, port int) (RDPEncryptionResponse, error) { + dialer := protocolstate.GetDialersWithId(executionId) + if dialer == nil { + return RDPEncryptionResponse{}, fmt.Errorf("dialers not initialized for %s", executionId) + } + resp := RDPEncryptionResponse{} + defaultTimeout := 5 * time.Second + + // Test different security protocols + protocols := map[SecurityLayer]int{ + SecurityLayerNativeRDP: 0, + SecurityLayerSSL: 1, + SecurityLayerCredSSP: 3, + SecurityLayerRDSTLS: 4, + SecurityLayerCredSSPWithEarlyUserAuth: 8, + } + + for name, value := range protocols { + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + conn, err := dialer.Fastdialer.Dial(ctx, "tcp", net.JoinHostPort(host, strconv.Itoa(port))) + if err != nil { + continue + } + defer func() { + _ = conn.Close() + }() + + // Test protocol + isRDP, err := testRDPProtocol(conn, value) + if err == nil && isRDP { + switch SecurityLayer(name) { + case SecurityLayerNativeRDP: + resp.NativeRDP = true + case SecurityLayerSSL: + resp.SSL = true + case SecurityLayerCredSSP: + resp.CredSSP = true + case SecurityLayerRDSTLS: + resp.RDSTLS = true + case SecurityLayerCredSSPWithEarlyUserAuth: + resp.CredSSPWithEarlyUserAuth = true + } + } + } + + // Test different encryption levels + ciphers := map[EncryptionLevel]int{ + EncryptionLevelRC4_40bit: 1, + EncryptionLevelRC4_56bit: 8, + EncryptionLevelRC4_128bit: 2, + EncryptionLevelFIPS140_1: 16, + } + + for encryptionLevel, value := range ciphers { + ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) + defer cancel() + conn, err := dialer.Fastdialer.Dial(ctx, "tcp", net.JoinHostPort(host, strconv.Itoa(port))) + if err != nil { + continue + } + defer func() { + _ = conn.Close() + }() + + // Test cipher + isRDP, err := testRDPCipher(conn, value) + if err == nil && isRDP { + switch encryptionLevel { + case EncryptionLevelRC4_40bit: + resp.RC4_40bit = true + case EncryptionLevelRC4_56bit: + resp.RC4_56bit = true + case EncryptionLevelRC4_128bit: + resp.RC4_128bit = true + case EncryptionLevelFIPS140_1: + resp.FIPS140_1 = true + } + } + } + + return resp, nil +} + +// testRDPProtocol tests RDP with a specific security protocol +func testRDPProtocol(conn net.Conn, protocol int) (bool, error) { + // Send RDP connection request with specific protocol + // This is a simplified version - in reality you'd need to implement the full RDP protocol + // including the negotiation phase with the specified protocol + _, err := conn.Write([]byte{0x03, 0x00, 0x00, 0x13, 0x0e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, byte(protocol), 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00}) + if err != nil { + return false, err + } + + // Read response + buf := make([]byte, 1024) + n, err := conn.Read(buf) + if err != nil { + return false, err + } + + // Check if response indicates RDP + if n >= 19 && buf[0] == 0x03 && buf[1] == 0x00 && buf[2] == 0x00 { + // For CredSSP and CredSSP with Early User Auth, we need to check for NLA support + if protocol == 3 || protocol == 8 { + // Check for NLA support in the response + if n >= 19 && buf[18]&0x01 != 0 { + return true, nil + } + return false, nil + } + return true, nil + } + + return false, nil +} + +// testRDPCipher tests RDP with a specific encryption level +func testRDPCipher(conn net.Conn, cipher int) (bool, error) { + // Send RDP connection request with specific cipher + // This is a simplified version - in reality you'd need to implement the full RDP protocol + // including the negotiation phase with the specified cipher + _, err := conn.Write([]byte{0x03, 0x00, 0x00, 0x13, 0x0e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, byte(cipher), 0x03, 0x00, 0x00, 0x00}) + if err != nil { + return false, err + } + + // Read response + buf := make([]byte, 1024) + n, err := conn.Read(buf) + if err != nil { + return false, err + } + + // Check if response indicates RDP + if n >= 19 && buf[0] == 0x03 && buf[1] == 0x00 && buf[2] == 0x00 { + // Check for encryption level support in the response + if n >= 19 && buf[18]&byte(cipher) != 0 { + return true, nil + } + return false, nil + } + + return false, nil +}