Skip to content

Commit 32845bc

Browse files
committed
CheckRDPEncryption
1 parent cbf57ef commit 32845bc

File tree

5 files changed

+272
-12
lines changed

5 files changed

+272
-12
lines changed

cmd/nuclei/rdp.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
id: rdp-enc-check
2+
3+
info:
4+
name: RDP Enc - Detection
5+
author: pussycat0x
6+
severity: info
7+
metadata:
8+
verified: true
9+
max-request: 1
10+
shodan-query: port:"3389"
11+
tags: js,network,rdp,info,enum
12+
13+
javascript:
14+
- code: |
15+
let m = require('nuclei/rdp');
16+
let response = m.CheckRDPEncryption(Host,Port);
17+
Export(response);
18+
19+
args:
20+
Host: "{{Host}}"
21+
Port: "3389"
22+
23+
extractors:
24+
- type: dsl
25+
dsl:
26+
- response

pkg/js/generated/go/librdp/rdp.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,16 @@ func init() {
1515
module.Set(
1616
gojs.Objects{
1717
// Functions
18-
"CheckRDPAuth": lib_rdp.CheckRDPAuth,
19-
"IsRDP": lib_rdp.IsRDP,
18+
"CheckRDPAuth": lib_rdp.CheckRDPAuth,
19+
"CheckRDPEncryption": lib_rdp.CheckRDPEncryption,
20+
"IsRDP": lib_rdp.IsRDP,
2021

2122
// Var and consts
2223

2324
// Objects / Classes
24-
"CheckRDPAuthResponse": gojs.GetClassConstructor[lib_rdp.CheckRDPAuthResponse](&lib_rdp.CheckRDPAuthResponse{}),
25-
"IsRDPResponse": gojs.GetClassConstructor[lib_rdp.IsRDPResponse](&lib_rdp.IsRDPResponse{}),
25+
"CheckRDPAuthResponse": gojs.GetClassConstructor[lib_rdp.CheckRDPAuthResponse](&lib_rdp.CheckRDPAuthResponse{}),
26+
"CheckRDPEncryptionResponse": gojs.GetClassConstructor[lib_rdp.RDPEncryptionResponse](&lib_rdp.RDPEncryptionResponse{}),
27+
"IsRDPResponse": gojs.GetClassConstructor[lib_rdp.IsRDPResponse](&lib_rdp.IsRDPResponse{}),
2628
},
2729
).Register()
2830
}

pkg/js/generated/ts/rdp.ts

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
2-
31
/**
42
* CheckRDPAuth checks if the given host and port are running rdp server
53
* with authentication and returns their metadata.
@@ -15,7 +13,19 @@ export function CheckRDPAuth(host: string, port: number): CheckRDPAuthResponse |
1513
return null;
1614
}
1715

18-
16+
/**
17+
* CheckRDPEncryption checks the RDP server's supported security layers and encryption levels.
18+
* It tests different protocols and ciphers to determine what is supported.
19+
* @example
20+
* ```javascript
21+
* const rdp = require('nuclei/rdp');
22+
* const encryption = rdp.CheckRDPEncryption('acme.com', 3389);
23+
* log(toJSON(encryption));
24+
* ```
25+
*/
26+
export function CheckRDPEncryption(host: string, port: number): RDPEncryptionResponse | null {
27+
return null;
28+
}
1929

2030
/**
2131
* 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 {
3343
return null;
3444
}
3545

36-
37-
3846
/**
3947
* CheckRDPAuthResponse is the response from the CheckRDPAuth function.
4048
* this is returned by CheckRDPAuth function.
@@ -52,7 +60,31 @@ export interface CheckRDPAuthResponse {
5260
Auth?: boolean,
5361
}
5462

55-
63+
/**
64+
* RDPEncryptionResponse is the response from the CheckRDPEncryption function.
65+
* This is returned by CheckRDPEncryption function.
66+
* @example
67+
* ```javascript
68+
* const rdp = require('nuclei/rdp');
69+
* const encryption = rdp.CheckRDPEncryption('acme.com', 3389);
70+
* log(toJSON(encryption));
71+
* ```
72+
*/
73+
export interface RDPEncryptionResponse {
74+
SecurityLayer: {
75+
NativeRDP: boolean;
76+
SSL: boolean;
77+
CredSSP: boolean;
78+
RDSTLS: boolean;
79+
CredSSPWithEarlyUserAuth: boolean;
80+
};
81+
EncryptionLevel: {
82+
RC4_40bit: boolean;
83+
RC4_56bit: boolean;
84+
RC4_128bit: boolean;
85+
FIPS140_1: boolean;
86+
};
87+
}
5688

5789
/**
5890
* IsRDPResponse is the response from the IsRDP function.
@@ -71,8 +103,6 @@ export interface IsRDPResponse {
71103
OS?: string,
72104
}
73105

74-
75-
76106
/**
77107
* ServiceRDP Interface
78108
*/

pkg/js/libs/rdp/memo.rdp.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,19 @@ func memoizedcheckRDPAuth(host string, port int) (CheckRDPAuthResponse, error) {
3939

4040
return CheckRDPAuthResponse{}, errors.New("could not convert cached result")
4141
}
42+
43+
func memoizedcheckRDPEncryption(host string, port int) (RDPEncryptionResponse, error) {
44+
hash := "checkRDPEncryption" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
45+
46+
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
47+
return checkRDPEncryption(host, port)
48+
})
49+
if err != nil {
50+
return RDPEncryptionResponse{}, err
51+
}
52+
if value, ok := v.(RDPEncryptionResponse); ok {
53+
return value, nil
54+
}
55+
56+
return RDPEncryptionResponse{}, errors.New("could not convert cached result")
57+
}

pkg/js/libs/rdp/rdp.go

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package rdp
33
import (
44
"context"
55
"fmt"
6+
"net"
67
"time"
78

89
"github.com/praetorian-inc/fingerprintx/pkg/plugins"
@@ -112,3 +113,188 @@ func checkRDPAuth(host string, port int) (CheckRDPAuthResponse, error) {
112113
resp.PluginInfo = pluginInfo
113114
return resp, nil
114115
}
116+
117+
type (
118+
// RDPEncryptionResponse is the response from the CheckRDPEncryption function.
119+
// This is returned by CheckRDPEncryption function.
120+
// @example
121+
// ```javascript
122+
// const rdp = require('nuclei/rdp');
123+
// const encryption = rdp.CheckRDPEncryption('acme.com', 3389);
124+
// log(toJSON(encryption));
125+
// ```
126+
RDPEncryptionResponse struct {
127+
SecurityLayer struct {
128+
NativeRDP bool
129+
SSL bool
130+
CredSSP bool
131+
RDSTLS bool
132+
CredSSPWithEarlyUserAuth bool
133+
}
134+
EncryptionLevel struct {
135+
RC4_40bit bool
136+
RC4_56bit bool
137+
RC4_128bit bool
138+
FIPS140_1 bool
139+
}
140+
}
141+
)
142+
143+
// CheckRDPEncryption checks the RDP server's supported security layers and encryption levels.
144+
// It tests different protocols and ciphers to determine what is supported.
145+
// @example
146+
// ```javascript
147+
// const rdp = require('nuclei/rdp');
148+
// const encryption = rdp.CheckRDPEncryption('acme.com', 3389);
149+
// log(toJSON(encryption));
150+
// ```
151+
func CheckRDPEncryption(host string, port int) (RDPEncryptionResponse, error) {
152+
return memoizedcheckRDPEncryption(host, port)
153+
}
154+
155+
// @memo
156+
func checkRDPEncryption(host string, port int) (RDPEncryptionResponse, error) {
157+
resp := RDPEncryptionResponse{}
158+
timeout := 5 * time.Second
159+
160+
// Test different security protocols
161+
protocols := map[string]int{
162+
"NativeRDP": 0,
163+
"SSL": 1,
164+
"CredSSP": 3,
165+
"RDSTLS": 4,
166+
"CredSSPWithEarlyUserAuth": 8,
167+
}
168+
169+
for name, value := range protocols {
170+
conn, err := protocolstate.Dialer.Dial(context.TODO(), "tcp", fmt.Sprintf("%s:%d", host, port))
171+
if err != nil {
172+
continue
173+
}
174+
defer conn.Close()
175+
176+
// Test protocol
177+
isRDP, err := testRDPProtocol(conn, timeout, value)
178+
if err == nil && isRDP {
179+
switch name {
180+
case "NativeRDP":
181+
resp.SecurityLayer.NativeRDP = true
182+
case "SSL":
183+
resp.SecurityLayer.SSL = true
184+
case "CredSSP":
185+
resp.SecurityLayer.CredSSP = true
186+
case "RDSTLS":
187+
resp.SecurityLayer.RDSTLS = true
188+
case "CredSSPWithEarlyUserAuth":
189+
resp.SecurityLayer.CredSSPWithEarlyUserAuth = true
190+
}
191+
}
192+
}
193+
194+
// Test different encryption levels
195+
ciphers := map[string]int{
196+
"RC4_40bit": 1,
197+
"RC4_56bit": 8,
198+
"RC4_128bit": 2,
199+
"FIPS140_1": 16,
200+
}
201+
202+
for name, value := range ciphers {
203+
conn, err := protocolstate.Dialer.Dial(context.TODO(), "tcp", fmt.Sprintf("%s:%d", host, port))
204+
if err != nil {
205+
continue
206+
}
207+
defer conn.Close()
208+
209+
// Test cipher
210+
isRDP, err := testRDPCipher(conn, timeout, value)
211+
if err == nil && isRDP {
212+
switch name {
213+
case "RC4_40bit":
214+
resp.EncryptionLevel.RC4_40bit = true
215+
case "RC4_56bit":
216+
resp.EncryptionLevel.RC4_56bit = true
217+
case "RC4_128bit":
218+
resp.EncryptionLevel.RC4_128bit = true
219+
case "FIPS140_1":
220+
resp.EncryptionLevel.FIPS140_1 = true
221+
}
222+
}
223+
}
224+
225+
return resp, nil
226+
}
227+
228+
// testRDPProtocol tests RDP with a specific security protocol
229+
func testRDPProtocol(conn net.Conn, timeout time.Duration, protocol int) (bool, error) {
230+
// Set connection timeout
231+
_ = conn.SetDeadline(time.Now().Add(timeout))
232+
defer func() {
233+
_ = conn.SetDeadline(time.Time{})
234+
}()
235+
236+
// Send RDP connection request with specific protocol
237+
// This is a simplified version - in reality you'd need to implement the full RDP protocol
238+
// including the negotiation phase with the specified protocol
239+
_, err := conn.Write([]byte{0x03, 0x00, 0x00, 0x13, 0x0e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, byte(protocol), 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00})
240+
if err != nil {
241+
return false, err
242+
}
243+
244+
// Read response
245+
buf := make([]byte, 1024)
246+
n, err := conn.Read(buf)
247+
if err != nil {
248+
return false, err
249+
}
250+
251+
// Check if response indicates RDP
252+
if n >= 19 && buf[0] == 0x03 && buf[1] == 0x00 && buf[2] == 0x00 {
253+
// For CredSSP and CredSSP with Early User Auth, we need to check for NLA support
254+
if protocol == 3 || protocol == 8 {
255+
// Check for NLA support in the response
256+
if n >= 19 && buf[18]&0x01 != 0 {
257+
return true, nil
258+
}
259+
return false, nil
260+
}
261+
return true, nil
262+
}
263+
264+
return false, nil
265+
}
266+
267+
// testRDPCipher tests RDP with a specific encryption level
268+
func testRDPCipher(conn net.Conn, timeout time.Duration, cipher int) (bool, error) {
269+
// Set connection timeout
270+
_ = conn.SetDeadline(time.Now().Add(timeout))
271+
defer func() {
272+
_ = conn.SetDeadline(time.Time{})
273+
}()
274+
275+
// Send RDP connection request with specific cipher
276+
// This is a simplified version - in reality you'd need to implement the full RDP protocol
277+
// including the negotiation phase with the specified cipher
278+
_, err := conn.Write([]byte{0x03, 0x00, 0x00, 0x13, 0x0e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, byte(cipher), 0x03, 0x00, 0x00, 0x00})
279+
if err != nil {
280+
return false, err
281+
}
282+
283+
// Read response
284+
buf := make([]byte, 1024)
285+
n, err := conn.Read(buf)
286+
if err != nil {
287+
return false, err
288+
}
289+
290+
// Check if response indicates RDP
291+
if n >= 19 && buf[0] == 0x03 && buf[1] == 0x00 && buf[2] == 0x00 {
292+
// Check for encryption level support in the response
293+
if n >= 19 && buf[18]&byte(cipher) != 0 {
294+
return true, nil
295+
}
296+
return false, nil
297+
}
298+
299+
return false, nil
300+
}

0 commit comments

Comments
 (0)