package rdp
import (
"context"
"fmt"
"net"
"strconv"
"time"
"github.com/praetorian-inc/fingerprintx/pkg/plugins"
"github.com/praetorian-inc/fingerprintx/pkg/plugins/services/rdp"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
)
type (
IsRDPResponse struct {
IsRDP bool
OS string
}
)
func IsRDP(ctx context.Context, host string, port int) (IsRDPResponse, error) {
executionId := ctx.Value("executionId").(string)
return memoizedisRDP(executionId, host, port)
}
func isRDP(executionId string, host string, port int) (IsRDPResponse, error) {
resp := IsRDPResponse{}
dialer := protocolstate.GetDialersWithId(executionId)
if dialer == nil {
return IsRDPResponse{}, fmt.Errorf("dialers not initialized for %s", executionId)
}
timeout := 5 * time.Second
conn, err := dialer.Fastdialer.Dial(context.TODO(), "tcp", fmt.Sprintf("%s:%d", host, port))
if err != nil {
return resp, err
}
defer func() {
_ = conn.Close()
}()
server, isRDP, err := rdp.DetectRDP(conn, timeout)
if err != nil {
return resp, err
}
if !isRDP {
return resp, nil
}
resp.IsRDP = true
resp.OS = server
return resp, nil
}
type (
CheckRDPAuthResponse struct {
PluginInfo *plugins.ServiceRDP
Auth bool
}
)
func CheckRDPAuth(ctx context.Context, host string, port int) (CheckRDPAuthResponse, error) {
executionId := ctx.Value("executionId").(string)
return memoizedcheckRDPAuth(executionId, host, port)
}
func checkRDPAuth(executionId string, host string, port int) (CheckRDPAuthResponse, error) {
resp := CheckRDPAuthResponse{}
dialer := protocolstate.GetDialersWithId(executionId)
if dialer == nil {
return CheckRDPAuthResponse{}, fmt.Errorf("dialers not initialized for %s", executionId)
}
timeout := 5 * time.Second
conn, err := dialer.Fastdialer.Dial(context.TODO(), "tcp", fmt.Sprintf("%s:%d", host, port))
if err != nil {
return resp, err
}
defer func() {
_ = conn.Close()
}()
pluginInfo, auth, err := rdp.DetectRDPAuth(conn, timeout)
if err != nil {
return resp, err
}
if !auth {
return resp, nil
}
resp.Auth = true
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 struct {
NativeRDP bool
SSL bool
CredSSP bool
RDSTLS bool
CredSSPWithEarlyUserAuth bool
RC4_40bit bool
RC4_56bit bool
RC4_128bit bool
FIPS140_1 bool
}
)
func CheckRDPEncryption(ctx context.Context, host string, port int) (RDPEncryptionResponse, error) {
executionId := ctx.Value("executionId").(string)
return memoizedcheckRDPEncryption(executionId, host, port)
}
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
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()
}()
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
}
}
}
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()
}()
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
}
func testRDPProtocol(conn net.Conn, protocol int) (bool, error) {
_, 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
}
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
return false, err
}
if n >= 19 && buf[0] == 0x03 && buf[1] == 0x00 && buf[2] == 0x00 {
if protocol == 3 || protocol == 8 {
if n >= 19 && buf[18]&0x01 != 0 {
return true, nil
}
return false, nil
}
return true, nil
}
return false, nil
}
func testRDPCipher(conn net.Conn, cipher int) (bool, error) {
_, 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
}
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
return false, err
}
if n >= 19 && buf[0] == 0x03 && buf[1] == 0x00 && buf[2] == 0x00 {
if n >= 19 && buf[18]&byte(cipher) != 0 {
return true, nil
}
return false, nil
}
return false, nil
}