Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions pkg/js/generated/go/librdp/rdp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
45 changes: 37 additions & 8 deletions pkg/js/generated/ts/rdp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


/**
* CheckRDPAuth checks if the given host and port are running rdp server
* with authentication and returns their metadata.
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -71,8 +102,6 @@ export interface IsRDPResponse {
OS?: string,
}



/**
* ServiceRDP Interface
*/
Expand Down
16 changes: 16 additions & 0 deletions pkg/js/libs/rdp/memo.rdp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
210 changes: 210 additions & 0 deletions pkg/js/libs/rdp/rdp.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package rdp
import (
"context"
"fmt"
"net"
"strconv"
"time"

"github.com/praetorian-inc/fingerprintx/pkg/plugins"
Expand Down Expand Up @@ -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
}
Loading