百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

攻击RDP——如何窃听不安全的RDP连接

lipiwang 2024-10-20 10:11 12 浏览 0 评论

翻译:shan66

稿费:300RMB(不服你也来投稿啊!)

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿

简介


系统管理员每天都会使用远程桌面协议(RDP)登录到远程Windows计算机。最常见的情形是,人们使用它在关键服务器上执行某些管理任务,这些服务器包括具有高度特权帐户的域控制器等,它们的登陆凭据都是通过RDP传输的。因此,确保RDP配置的安全性是至关重要的。

但是根据我们的观察,由于配置错误,Active Directory环境中的系统管理员会定期显示(并忽略)证书警告,如下所示:

图1:SSL证书警告

如果您的环境中经常遇到这样的警告的话,您将无法识别真正的中间人(MitM)攻击。

本文旨在帮您认识到认真对待证书警告以及如何安全地配置Windows环境有多么的重要。目标受众是系统管理员、渗透测试人员和安全爱好者。虽然对下列主题的了解没有硬性要求,但我仍然鼓励您进一步深入学习一下:

公钥密码术以及对称密码术(RSA和RC4)

SSL

x509证书

TCP

Python

十六进制数和二进制代码

我们将演示MitM是如何嗅探您的凭据的,如果您不小心的话是很容易中招的。这些都不是特别新的技术,例如Cain [2]就是用来干这个的。然而,Cain不仅“年代久远”,代码封闭,而且还只适用于Windows系统。我们想要分析RDP的所有细节和内部工作机制,并尽可能地模拟真实的攻击情形。

不用说,本文介绍的技术不得用于在未经授权的情况下访问不属于您的任何系统。它们只能在系统所有者完全同意的情况下用于教育目的。否则,你很可能违反法律,当然,具体还取决于你的管辖权。

对于心急的读者,可以通过参考资料[1]中的链接下载相应的源代码。

协议分析


让我们启动Wireshark,看看当我们通过RDP连接到服务器时会发生什么:

图2:Wireshark中的RDP会话的开头部分内容

正如我们所看到的,客户端首先提出了用于RDP会话的安全协议。这里有三个协议:

标准RDP安全性协议

增强型RDP安全或TLS安全性协议

CredSSP协议

在本例中,客户端能够使用前两个协议。注意,标准的RDP安全性协议总是可用的,不需要由客户端指出。TLS或增强的RDP安全性协议,只是将标准RDP安全性协议封装到加密的TLS隧道内而已。顺便说一下,在本文中术语SSL和TLS可是可以互换的。

CredSSP协议虽然也使用TLS隧道,但它不是在受保护的隧道中传输密码,而是使用NTLM或Kerberos进行身份验证。该协议也称为网络级认证(NLA)。

早期的用户身份验证有一个特点,那就是允许服务器可以在用户提交任何凭据(用户名除外)之前拒绝访问,例如,如果用户没有必要的远程访问权限。

在上面的Wireshark会话中,我们可以看到,SSL握手是在客户端和服务器已同意使用增强的RDP安全性协议之后执行的。为此,我们右键单击磋商数据包后的第一个数据包,并将TCP数据流作为SSL解码:

图3:SSL握手的开头部分内容

因此,如果我们想要对一个RDP连接进行中间人攻击的话,不能直接使用SSL代理,因为代理还需要知道RDP。它需要知道何时启动SSL握手,这类似于SMTP或FTP中的StartTLS。我们选择Python来实现这样的代理。为此,我们只需创建受害者客户端连接到的服务器套接字,以及连接到实际服务器的客户端套接字。我们在这些套接字之间转发数据,并在必要时将它们封装到SSL套接字中。当然,我们将密切关注相关数据并根据需要进行修改。

当然,首先要修改的就是客户端提出的协议。客户端可能想告诉服务器它可以执行CredSSP协议,但我们将在相应数据到达服务器的半路上将协议更改为标准RDP安全性协议。在默认配置中,服务器会很乐意使用这个协议的。

利用Python打造用于RDP的MitM代理


我们的Python脚本的主循环大致如下所示:

函数run()打开套接字,处理协议的协商并启用SSL(如有必要)。

之后,数据就可以在两个套接字之间转发了。如果设置了调试标志,dump_data()函数会将数据作为hexdump打印到屏幕。而parse_rdp()会从数据中提取相应的信息,tamper_data()函数可用于对其进行修改。

密码学基础知识


因为我们需要借助RSA来搞定标准RDP安全协议,所以我们先来了解一下RSA的基础知识。如果您熟悉这方面的知识的话,可以跳过此部分。

在RSA中,加密、解密和签名纯粹就是数学运算,并且都是针对整数的运算。

请记住,所有这些操作都是在有限群上完成的[3]。

当生成RSA密钥对时,您需要找到两个大质数p和q。你得到他们的乘积,n = pq(这称为模数),计算φ(n)=(p - 1)(q - 1)(欧拉常数函数),并选择一个与φ(n) 互质的整数e。然后,您需要找到满足

1
e?d≡1 modφ(n)

的数字d。

数字d就是私钥,而e和n则组成公钥。当然,理论上d可以利用n和e求出,但φ(n)却很难计算,除非你知道p和q。这就是为什么RSA的安全性在很大程度上取决于大数分解的难度。到目前为止,没有人知道如何有效地进行大数分解——除非你有一台可以工作的量子计算机[4,5]。

为了加密消息m,我们只需求其e次幂,然后模n:

1
c≡me mod n

为了对密文c进行解密,我们可以使用私钥指数d进行下列运算:

1
m≡cd mod n

实际上,这是加密运算的逆运算。当然,这里涉及许多数学知识,过于深入的内容我就不介绍了。

签名与解密相同。你只需在消息的哈希值上执行相同的运算即可。

如果m或c大于256位的话,这些运算的开销将非常大,所以通常只使用RSA来加密对称密钥。然后,通过使用刚生成的密钥通过对称密码(通常为AES)算法来加密实际的消息。

攻陷标准RDP安全协议


其实,攻破这个协议难度并不太大,因为它的设计本身就存在很大隐患,下面我会具体加以讲解。

标准RDP安全协议的运行机制是:

客户声明打算使用标准RDP安全协议。

服务器同意并将自己的RSA公钥与“Server Random”一起发送到客户端。公钥以及其他信息(例如主机名等)的集合称为“证书”。

使用终端服务私钥对证书进行签名,以确保真实性。

客户端通过使用终端服务公钥验证证书。如果验证成功,它就使用服务器的公钥来加密“Client Random”,并将其发送给服务器。

服务器使用自己的私钥解密Client Random。

服务器和客户端从Server Random和Client Random中求出会话密钥[6]。这些密钥用于对会话的其余部分进行对称加密。

请注意,所有这些都是以纯文本形式发送的,而不是在SSL隧道内发送的。原则上讲这没有什么问题,微软只是试图实现自己的SSL加密技术。然而,加密技术是可没想象的那么容易[7],按一般规律,你始终应该依靠已建立的解决方案,因为它们都是经过时间考验过得,而不是实现自己的解决方案。因此,微软在这里犯了一个根本性的错误——我实在想不通他们为什么要这样做。

你能发现这里的错误吗?客户端是如何获取终端服务公钥的?答案是:它是预装的。这意味着它在所有系统上使用相同的密钥。这意味着私钥也都是一样的!所以,它可以从任何Windows安装上面提取到。事实上,我们甚至不需要这样做,因为现在微软已经决定正式发布它,这样一来,我们可以直接从microsoft.com网站上找到它们[8]。

在导出会话密钥后,可以使用多个安全级别[9]进行对称加密:无、40位RC4、56位RC4、128位RC4或3DES(称为FIPS)。默认值为128位RC4(“High”)。但是如果我们能够窃听密钥的话,那么加密的强度就无所谓了。

所以我们的计划是很明显的:当发现服务器的公钥时,我们快速生成相同大小的自己的RSA密钥对,并用它覆盖原始密钥。当然,我们需要使用终端服务私钥生成我们的公钥的签名,并用它替换原始签名。然后,在客户端成功验证我们的假公钥之后,我们接收其Client Random。我们使用我们的私钥对它进行解密,将其写下来以便使用服务器的公钥重新加密它。仅此而已!这样一来,我们就可以被动地读取客户端和服务器之间的加密流量了。

唯一的挑战是正确解析RDP数据包。这才是我们感兴趣的:

我高亮显示了代表公钥的相关字节。在其前面的两个字节的内容是以小端字节顺序(0x011c)表示的公钥长度。正如我们之前讨论的那样,公钥由模数和公钥指数组成。有关此数据结构的详细信息,请阅读RDP的相关规范[10]。

让我们来看看我们感兴趣的信息。下面是模数:

这里我们可以看到模数n、公钥指数e和私钥指数d。它们是使用大端字节顺序的十六进制数字表示的。我们实际上需要一个2048位的密钥,而不是512位的,但道理您应该清楚了。

伪造签名很容易。我们取证书的前六个字段的MD5哈希值,根据规范添加一些常量[11],然后用终端服务密钥[8]的私有部分进行加密。具体可以利用下列Python代码来完成:

我们需要拦截的下一个消息是包含加密的Client Random的消息。它看起来如下所示:

我们只需要使用服务器的公钥重新加密它,并在传递它之前进行相应的替换即可。

不幸的是,事情还没有结束。我们现在知道了秘密的Client Random,但不知道什么原因,微软并没有单纯用它作为对称密钥。有一个精心制作程序[6]可以导出客户端的加密密钥、服务器的加密密钥和签名密钥。

在导出会话密钥之后,我们可以初始化RC4流的s-box。由于RDP对于来自服务器的消息使用单独的密钥,而不是来自客户端的消息,因此我们需要两个s-box。s-box是一个256字节的数组,会根据密钥按照某种特定的方式进行重排。然后,s盒就会产生伪随机数流,用来与数据流进行异或处理。这个过程可以用下列Python代码完成:

正如你所看到的那样,协议要求密钥对4096个数据包加密后进行更新。但是这里我不打算实现这一点,因为我只是对凭证安全性的概念证明感兴趣,所以不想弄那么复杂。不过,如果读者学有余力的话,可以自行补上!

现在,我们已经万事俱备,足以读取所有的流量了。我们对含有键盘输入事件(即按键和按键释放)信息的数据包特别感兴趣。我从规范[12]了解到,消息可以包含多个数据包,并有慢路径包(从0x03开始)和快路径包(第一个字节可被四整除)。

键盘输入事件[13]由两个字节组成,例如:

这意味着“S”键(0x1F)已被释放(因为第一个字节是0x01)。

当然,这里的解析工作处理的不是很到位,因为有时鼠标移动事件会被识别为键盘事件。此外,scancode需要转换为虚拟键代码,这取决于键盘类型和键盘布局。这样做好像有点复杂,所以我没有这样做,而是直接采用了参考资料[14]中的方法。对于概念验证来说,这已经足够好了。

让我们试验一下。连接到我们的虚假RDP服务器后,我们收到警告说无法验证服务器的真实性:

图4:无法验证服务器的身份...

注意到了吗?这不是SSL的警告。但是无论如何,我们现在已经可以看到按键了(见图5)。

顺便说一下,这正是Cain所做的事情。

攻陷增强型RDP安全协议


对我来说,降级到标准RDP安全协议是无法令人满意的。如果我是一个攻击者,我会尽量让攻击看起来不那么不显眼。在上面的情形中,受害人会注意到一个与平常不同的警告,并且在连接已经建立之后还必须输入其凭证。

当我使用Cain通过MitM方式攻击RDP连接时,要是没有看到相同的SSL警告的话,我会很不爽的。因为如果这个MitM工具会导致显示完全不同的警告的话,那么我就很难向客户解释为什么必须认真对待SSL警告,特别是当他们使用了未经验证的自签名证书的时候。

图5:以明文显示的键盘输入事件。密码是Secr3t!

因此,让我们尝试将连接降级为增强型RDP安全协议。为此,我们需要自己的自签名SSL证书,不过这可以由openssl生成:

我们需要在正确的时间将我们的Python TCP套接字封装到SSL套接字中,这对于我们来说不成问题。我之前说过,标准的RDP协议使用了SSL隧道,但服务器总是选择“None”作为其加密级别。这简直太好了,因为可以安全地假设SSL封装器能确保数据的真实性和完整性。在SSL之上使用RC4是没有必要的,因为这就是在资源浪费。提取击键的工作方式与前一节完全相同。

唯一多出来的安全功能就是服务器会对原始协议协商请求进行确认。

在建立SSL连接后,服务器会对客户端说:“顺便说一下,你告诉我这些是你能够处理的安全协议。”用二进制表示的话,它看起来像这样:

然后,客户端可以将该值与最初在第一个请求中发送的值进行比较,如果不匹配,则终止连接。显然,这时已经太晚了。我们作为中间人,可以通过用其原始值(在这种情况下为0x03)替换相应字节(在偏移量为0x4C处的高亮显示字节)来隐藏来自客户端的伪协商请求。

之后,我们可以毫无阻碍的侦听一切流量了。好了,继续努力。

如预期的那样,受害者这里看到的是SSL警告。但这事仍然不够圆满。因为在建立RDP连接之前,没有提示我们输入凭据,而是直接显示了Windows登录屏幕。与NLA不同,认证是在会话内部进行的。同样,这仍然有别于典型的管理工作流程,很容易被精明的用户觉察到。

突破CredSSP协议


好吧,我承认:这里我们没有直接攻陷CredSSP协议。但我们会找到一种方法来绕过它。

首先,让我们看看如果我们不降低连接的安全等级的话,会发生什么。这时,发送到服务器的相关消息如下所示:

我高亮显示了客户端质询和NTLM的应答,两者是彼此相邻的。服务器质询位于服务器的上一条消息中。

我们在这里看到的是NTLM身份验证[15]。这是一种质询-应答技术,其中客户端会将服务器质询(类似于Server Random)、客户端质询以及用户密码的哈希值以及一些其他值映射为加密哈希值。这个值称为“NTLM应答”,然后将其传输到服务器。

这个值的计算细节对我们来说并不重要。我们唯一需要知道的是,它不能用于重放攻击或用于哈希值传递攻击。但它可能会遭受密码猜测攻击!

底层的哈希算法是HMAC-MD5,这是一个相当差劲的哈希算法(所以我们可以每秒猜测大量值),但它仍然使用了salt(用来对付彩虹表)。

我们现在可以尝试用Hashcat [17]或者John Ripper [18]来破解它。John的哈希格式为[16]:

虽然不是很理想,但是总比什么都没有强。不过,实际上我们可以做得更好。

我们需要问自己的问题是:服务器是如何验证NTLM应答的?它会咨询域控制器。那么,如果域控制器不可用呢? 它会说“算了,让我们使用增强型RDP安全协议吧,不用NLA了”,客户端也会言听计从。但是有趣的是:由于客户端已经缓存了用户的密码,所以它会直接传输它,而不是将用户引导至Windows登录屏幕! 这正是我们想要的。这样除了SSL警告之外,就没有任何可疑的东西引起受害者的注意了。

所以,我们要做的事情是:当客户端发送它的NTLM应答后,我们将服务器的响应替换为:

我没有找到与此有关的文档,但它的确是无法联系域控制器时的服务器响应。客户端将返回到增强型RDP安全协议,并显示SSL警告,同时将SSL隧道内的密码传输到服务器。

请注意,我们没有收到SSL警告。根据规范[19],客户端将SSL证书的指纹发送到使用由CredSSP协议协商的密钥加密的服务器。

如果它与服务器证书的指纹不匹配,那么会话将会被终止。这就是为什么即使受害者提供不正确的凭据也不要紧的原因—— 我们可以看到(不正确的)密码。

但是,如果密码正确,我们将观察到一个TLS内部错误。

我想出的解决方法是直接篡改NTLM应答。我对Python脚本进行了相应的修改,通过更改NTLM应答让NTLM身份验证总是失败。不过,受害者是不会注意到这一点的,因为正如我们刚才看到的,我们可以将连接降级到TLS,这样会重新传输凭据。

但是,还有一件事需要注意。如果客户端会显示正在尝试连接一台加入域的计算机,那么它就不会使用NTLM,而是使用Kerberos,这意味着它将在建立RDP连接之前与域控制器联系以请求相应的ticket。这是一件好事,因为Kerberos的ticket对攻击者而言,要比没有“盐化”的NTLM应答更加微不足道。但是,如果攻击者处于MitM位置,那么他就可以阻止针对Kerberos服务的所有请求。如果客户端无法联系Kerberos服务,那会发生什么呢?实际上,它会退回到NTLM。

将这种攻击技术武器化

到目前为止,我们一直在实验室环境下进行的。所以,受害者在RDP客户端中输入的不是我们的IP,而是他们自己的服务器的IP或主机名。有多种方法可以让我们成为中间人,但在这里我们将利用ARP欺骗。这种方法并不难,过一会儿就给出一个概念证明式的演示。由于这种攻击是在网络协议的第二层进行的,所以要求我们必须与受害者在同一个子网中。

在我们欺骗ARP回复并启用IPv4流量转发后,受害者和网关之间的所有通信都将通过我们的计算机。由于仍然不知道受害者输入的IP地址,所以仍然无法运行我们的Python脚本。

首先,我们创建一个iptables规则,丢弃受害者用于RDP服务器的SYN数据包:

图6:左侧:受害者看到的域控制器的RDP会话。右侧:攻击者看到的明文密码。

建议


那么,作为系统管理员你可能想知道,应该采取哪些行动来保护自己网络的安全。

首先,如果服务器的身份不能被验证,即如果SSL证书没有被可信证书颁发机构(CA)签名,则拒绝RDP连接是绝对关键的。您必须使用企业CA来签署所有服务器证书。如果无法验证证书,则客户端必须通过GPO [22]配置为禁止连接:

在服务器端是否执行CredSSP(NLA)的问题是非常棘手的。为了便于记录,这也可以作为组策略[20]推出:

我们已经看到,客户端会缓存用户的凭据,以便在NLA不可用的情况下方便地重新传输它们——我们知道,这些凭据就位于内存中。因此,它们可能被具有SYSTEM权限的攻击者读取,例如使用Mimikatz [24]等。这是一个令人难以置信的常见网络攻击情形:攻陷一台机器,利用Mimikatz提取登录用户的明文凭证,并通过横向运动攻击其他帐户,直到找到域管理员密码为止。这就是为什么你只能在域控制器上使用你的个人域管理员帐户,而不应该在其他地方使用的原因。

但是如果使用RDP远程进入域控制器则会在工作站上留下高权限帐户的痕迹,这是一个严重的问题。此外,如果您强制执行NLA,在启用“用户必须在下次登录时更改密码”选项后,那么只能使用终端服务器的用户会被锁定。我们知道,NLA的唯一优点是更轻便,可以减轻拒绝服务攻击的影响,因为它占用较少的资源,并且可以保护RDP免受基于网络的攻击,如MS12-020 [25]。这就是为什么目前我们正在讨论是否建议禁用RDA的NLA的原因。

如果您希望避免使用NLA,请将组策略“要求为远程连接使用特定安全层”设置为SSL [20]。

为了进一步增加RDP连接的安全性,您可以采取的另一项措施是,除了用户凭证之外,为用户验证添加其他验证因子。目前有许多相关的第三方产品,至少在保护关键系统如域控制器的时候,您可以考虑这一措施。

如果你的Linux机器是通过RDP连接到Windows终端服务器的话,我需要提醒的是,流行的RDP客户端rdesktop不支持NLA,并且根本不对SSL证书进行验证。所以我建议使用xfreerdp,至少它会验证SSL证书。

最后,鼓励大家对您的同事和用户不断重申:不要轻视SSL警告,无论是在RDP或HTTPS或其他任何情况下。作为管理员,您有责任确保您的客户端系统在受信任的CA列表中含有您的根CA。这样,这些警告就属于异常,需要马上通知IT部门。

如果您有任何其他问题或意见,请随时与我们联系。

图7:一个关键的GPO设置:为客户端配置服务器验证

参考资料


[1] Vollmer, A., Github.com: Seth (2017), https://github.com/SySS-Research/Seth (Cited onpage 2.)

[2] Montoro M., Cain & Abel (2014), http://www.oxid.it/cain.html (Cited on page 2.)

[3] Wikipedia contributors, Finite group, https://en.wikipedia.org/w/index.php?title=Finite_group&oldid=768290355 (accessed March 8, 2017) (Cited on page 5.)

[4] Wikipedia contributors, Shor’s algorithm (accessed March 8, 2017), https://en.wikipedia.org/w/index.php?title=Shor%27s_algorithm&oldid=767553912 (Cited on page 5.)

[5] Shor, P. W., Polynomial-Time Algorithms for Prime Factorization and Discrete Logarithms on a QuantumComputer (1995), https://arxiv.org/abs/quant-ph/9508027v2 (Cited on page 5.)

[6] Microsoft Developer Network, [MS-RDPBCGR]: Non-FIPS (2017), https://msdn.microsoft.com/en-us/library/cc240785.aspx (Cited on pages 6 and 9.)

[7] Schneier, B., Why Cryptography Is Harder Than It Looks (1997), https://www.schneier.com/essays/archives/1997/01/why_cryptography_is.html (Cited on page 6.)

[8] Microsoft Developer Network, [MS-RDPBCGR]: Terminal Services Signing Key (2017), https://msdn.microsoft.com/en-us/library/cc240776.aspx (Cited on pages 6 and 8.)

[9] Microsoft Developer Network, [MS-RDPBCGR]: Encrypting and Decrypting the I/O Data Stream (2017),https://msdn.microsoft.com/en-us/library/cc240787.aspx (Cited on page 6.)

[10] Microsoft Developer Network, [MS-RDPBCGR]: Server Security Data (TS_UD_SC_SEC1) (2017), https://msdn.microsoft.com/en-us/library/cc240518.aspx (Cited on page 7.)

[11] Microsoft Developer Network, [MS-RDPBCGR]: Signing a Proprietary Certificate (2017), https://msdn.microsoft.com/en-us/library/cc240778.aspx (Cited on page 8.)

[12] Microsoft Developer Network, [MS-RDPBCGR]: Client Input Event PDU Data (TS_INPUT_PDU_DATA)(2017), https://msdn.microsoft.com/en-us/library/cc746160.aspx (Cited on page 10.)

[13] Microsoft Developer Network, [MS-RDPBCGR]: Keyboard Event (TS_KEYBOARD_EVENT) (2017), https://msdn.microsoft.com/en-us/library/cc240584.aspx (Cited on page 11.)

[14] Brouwer, A., Keyboard Scancodes (2009), https://www.win.tue.nl/~aeb/linux/kbd/scancodes-10.html#ss10.6 (Cited on page 11.)

[15] Microsoft Developer Network, Microsoft NTLM (2017), https://msdn.microsoft.com/en-us/library/aa378749%28VS.85%29.aspx (Cited on page 14.)

[16] Weeks, M., Attacking Windows Fallback Authentication (2015), https://www.root9b.com/sites/default/files/whitepapers/R9B_blog_003_whitepaper_01.pdf (Cited on page 14.)

[17] Hashcat, https://hashcat.net/hashcat/ (Cited on page 14.)

[18] John The Ripper, http://www.openwall.com/john/ (Cited on page 14.)

[19] Microsoft Developer Network, [MS-CSSP]: TSRequest (2017), https://msdn.microsoft.com/enus/library/cc226780.aspx (Cited on page 15.)

[20] Microsoft Technet, Security (2017), https://technet.microsoft.com/en-us/library/cc771869(v=ws.10).aspx(Cited on page 18.)

[21] Microsoft Technet, Network Security: Restrict NTLM: NTLM authentication in this domain (2017), https://technet.microsoft.com/en-us/library/jj852241(v=ws.11).aspx (Not cited.)

[22] Microsoft Technet, Remote Desktop Connection Client (2017), https://technet.microsoft.com/en-us/library/cc753945(v=ws.10).aspx (Cited on page 18.)

[23] Vollmer, A., Github.com: clone-cert.sh (2017), https://github.com/SySS-Research/clonecert (Cited on page 16.)

[24] Delpy, B., Github.com: mimikatz (2017), https://github.com/gentilkiwi/mimikatz (Cited onpage 18.)

[25] Microsoft Technet, Security Bulletin MS12-020 (2012), https://technet.microsoft.com/enus/library/security/ms12-020.aspx (Cited on page 18.)

相关推荐

前端入门——css 网格轨道详细介绍

上篇前端入门——cssGrid网格基础知识整体大概介绍了cssgrid的基本概念及使用方法,本文将介绍创建网格容器时会发生什么?以及在网格容器上使用行、列属性如何定位元素。在本文中,将介绍:...

Islands Architecture(孤岛架构)在携程新版首页的实践

一、项目背景2022,携程PC版首页终于迎来了首次改版,完成了用户体验与技术栈的全面升级。作为与用户连接的重要入口,旧版PC首页已经陪伴携程走过了22年,承担着重要使命的同时,也遇到了很多问题:维护/...

HTML中script标签中的那些属性

HTML中的<script>标签详解在HTML中,<script>标签用于包含或引用JavaScript代码,是前端开发中不可或缺的一部分。通过合理使用<scrip...

CSS 中各种居中你真的玩明白了么

页面布局中最常见的需求就是元素或者文字居中了,但是根据场景的不同,居中也有简单到复杂各种不同的实现方式,本篇就带大家一起了解下,各种场景下,该如何使用CSS实现居中前言页面布局中最常见的需求就是元...

CSS样式更改——列表、表格和轮廓

上篇文章主要介绍了CSS样式更改篇中的字体设置Font&边框Border设置,这篇文章分享列表、表格和轮廓,一起来看看吧。1.列表List1).列表的类型<ulstyle='list-...

一文吃透 CSS Flex 布局

原文链接:一文吃透CSSFlex布局教学游戏这里有两个小游戏,可用来练习flex布局。塔防游戏送小青蛙回家Flexbox概述Flexbox布局也叫Flex布局,弹性盒子布局。它决定了...

css实现多行文本的展开收起

背景在我们写需求时可能会遇到类似于这样的多行文本展开与收起的场景:那么,如何通过纯css实现这样的效果呢?实现的难点(1)位于多行文本右下角的展开收起按钮。(2)展开和收起两种状态的切换。(3)文本...

css 垂直居中的几种实现方式

前言设计是带有主观色彩的,同样网页设计中的css一样让人摸不头脑。网上列举的实现方式一大把,或许在这里你都看到过,但既然来到这里我希望这篇能让你看有所收获,毕竟这也是前端面试的基础。实现方式备注:...

WordPress固定链接设置

WordPress设置里的最后一项就是固定链接设置,固定链接设置是决定WordPress文章及静态页面URL的重要步骤,从站点的SEO角度来讲也是。固定链接设置决定网站URL,当页面数少的时候,可以一...

面试发愁!吃透 20 道 CSS 核心题,大厂 Offer 轻松拿

前端小伙伴们,是不是一想到面试里的CSS布局题就发愁?写代码时布局总是对不齐,面试官追问兼容性就卡壳,想跳槽却总被“多列等高”“响应式布局”这些问题难住——别担心!从今天起,咱们每天拆解一...

3种CSS清除浮动的方法

今天这篇文章给大家介绍3种CSS清除浮动的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。首先,这里就不讲为什么我们要清楚浮动,反正不清除浮动事多多。下面我就讲3种常用清除浮动的...

2025 年 CSS 终于要支持强大的自定义函数了?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!1.什么是CSS自定义属性CSS自...

css3属性(transform)的一个css3动画小应用

闲言碎语不多讲,咱们说说css3的transform属性:先上效果:效果说明:当鼠标移到a标签的时候,从右上角滑出二维码。实现方法:HTML代码如下:需要说明的一点是,a链接的跳转需要用javasc...

CSS基础知识(七)CSS背景

一、CSS背景属性1.背景颜色(background-color)属性值:transparent(透明的)或color(颜色)2.背景图片(background-image)属性值:none(没有)...

CSS 水平居中方式二

<divid="parent"><!--定义子级元素--><divid="child">居中布局</div>...

取消回复欢迎 发表评论: