Merge pull request #6204 from projectdiscovery/RDP-Enc-func

CheckRDPEncryption function
This commit is contained in:
Mzack9999 2025-09-26 01:06:50 +02:00 committed by GitHub
commit f8f89bb721
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 269 additions and 12 deletions

View File

@ -15,14 +15,16 @@ func init() {
module.Set( module.Set(
gojs.Objects{ gojs.Objects{
// Functions // Functions
"CheckRDPAuth": lib_rdp.CheckRDPAuth, "CheckRDPAuth": lib_rdp.CheckRDPAuth,
"IsRDP": lib_rdp.IsRDP, "CheckRDPEncryption": lib_rdp.CheckRDPEncryption,
"IsRDP": lib_rdp.IsRDP,
// Var and consts // Var and consts
// Objects / Classes // Objects / Classes
"CheckRDPAuthResponse": gojs.GetClassConstructor[lib_rdp.CheckRDPAuthResponse](&lib_rdp.CheckRDPAuthResponse{}), "CheckRDPAuthResponse": gojs.GetClassConstructor[lib_rdp.CheckRDPAuthResponse](&lib_rdp.CheckRDPAuthResponse{}),
"IsRDPResponse": gojs.GetClassConstructor[lib_rdp.IsRDPResponse](&lib_rdp.IsRDPResponse{}), "CheckRDPEncryptionResponse": gojs.GetClassConstructor[lib_rdp.RDPEncryptionResponse](&lib_rdp.RDPEncryptionResponse{}),
"IsRDPResponse": gojs.GetClassConstructor[lib_rdp.IsRDPResponse](&lib_rdp.IsRDPResponse{}),
}, },
).Register() ).Register()
} }

View File

@ -1,5 +1,3 @@
/** /**
* CheckRDPAuth checks if the given host and port are running rdp server * CheckRDPAuth checks if the given host and port are running rdp server
* with authentication and returns their metadata. * with authentication and returns their metadata.
@ -15,7 +13,19 @@ export function CheckRDPAuth(host: string, port: number): CheckRDPAuthResponse |
return null; 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. * 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; return null;
} }
/** /**
* CheckRDPAuthResponse is the response from the CheckRDPAuth function. * CheckRDPAuthResponse is the response from the CheckRDPAuth function.
* this is returned by CheckRDPAuth function. * this is returned by CheckRDPAuth function.
@ -52,7 +60,30 @@ export interface CheckRDPAuthResponse {
Auth?: boolean, 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. * IsRDPResponse is the response from the IsRDP function.
@ -71,8 +102,6 @@ export interface IsRDPResponse {
OS?: string, OS?: string,
} }
/** /**
* ServiceRDP Interface * ServiceRDP Interface
*/ */

View File

@ -39,3 +39,19 @@ func memoizedcheckRDPAuth(executionId string, host string, port int) (CheckRDPAu
return CheckRDPAuthResponse{}, errors.New("could not convert cached result") 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")
}

View File

@ -3,6 +3,8 @@ package rdp
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"strconv"
"time" "time"
"github.com/praetorian-inc/fingerprintx/pkg/plugins" "github.com/praetorian-inc/fingerprintx/pkg/plugins"
@ -127,3 +129,211 @@ func checkRDPAuth(executionId string, host string, port int) (CheckRDPAuthRespon
resp.PluginInfo = pluginInfo resp.PluginInfo = pluginInfo
return resp, nil 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
}