14 Aug 2017 | Peter Stöckli
Misconfigured JSF ViewStates can lead to severe RCE vulnerabilities
tl;dr ViewStates in JSF are serialized Java objects. If the used JSF implementation in a web application is not configured to encrypt the ViewState the web application may have a serious
remote code execution (RCE) vulnerability. So it is important that the ViewState encryption is never disabled!
After we had a look at RCEs through misconfigured JSON libraries we started analyzing the ViewStates of JSF implementations. JavaServer Faces (JSF) is a User Interface (UI) technology for building web UIs with reusable components. JSF is mostly used for enterprise applications and a JSF implementation is typically used by a web application that runs on a Java application server like JBoss EAP or WebLogic Server. There are two well-known implementations of the JSF specification:
- Oracle Mojarra (JSF reference implementation)
- Apache MyFaces
This blog post focuses on the two JSF 2.x implementations: Oracle Mojarra (Reference Implementation) and Apache MyFaces. Older implementations (JSF 1.x) are also likely to be affected by the vulnerabilities described in this post. (JSF 2.0.x was initially released in 2009, the current version is 2.3.x).
The state of the ViewState
A difference between JSF and similar web technologies is that JSF makes use of ViewStates (in addition to sessions) to store the current state of the view (e.g. what parts of the view should currently be displayed). The ViewState can be stored on the
server or the
client. JSF ViewStates are typically automatically embedded into HTML forms as hidden field with the name
javax.faces.ViewState. They are sent back to the server if the form is submitted.
If the JSF ViewState is configured to sit on the
server the hidden
javax.faces.ViewState field contains an id that helps the server to retrieve the correct state. In the case of MyFaces that id is a serialized Java object!
If the JSF ViewState is configured to sit on the
client the hidden
javax.faces.ViewState field contains a serialized Java object that is at least Base64 encoded. You might have realized by now that this is a potential road to disaster! That might be one of the reasons why nowadays JSF ViewStates are encrypted and signed before being sent to the client.
The attack on the ViewState
Let’s assume we have a web application with a JSF based login page:
That login page has a ViewState that is neither encrypted nor signed. So when we look at its HTML source we see a hidden field containing the ViewState:
If you decode the above ViewState using Base64 you will notice that it contains a serialized Java object. This ViewState is sent back to the server via POST when the form is submitted (e.g. click on Login). Now before the ViewState is POSTed back to the server the attacker replaces the ViewState with his own malicious ViewState using a gadget that’s already on the server’s classpath (e.g.
InvokerTransformer from commons-collections-3.2.1.jar) or even a gadget that is not yet known to the public. With said malicious gadget placed in the ViewState the attacker specifies which commands he wants to run on the server. The flexibility of what an attacker can do is limited by the powers of the available gadgets on the classpath of the server. In case of the
InvokerTransformer the attacker can specify which command line commands should be executed on the server. The attacker in our example chose to start a calculator on the UI of our Linux based server.
After the attacker has sent his modified form back to the server the JSF implementation tries to deserialize the provided ViewState. Now even before the deserialization of the ViewState has ended the command is executed and the calculator is started on the server:
Everything happened before the JSF implementation could have a look at the ViewState and decide that it was no good. When the ViewState was found to be invalid typically an error is sent back to the client like “View expired”. But then it’s already too late. The attacker had access to the server and has run commands. (Most real-world attackers don’t start a calculator but they typically deploy a remote shell, which they then use to access the server.)
=> All in all this example demonstrates a very dangerous unauthenticated remote code execution (RCE) vulnerability.
(Almost the same attack scenario against JSF as depicted above was already outlined and demonstrated in the 2015 presentation (pages 65 to 67): Marshalling Pickles held by Frohoff and Lawrence.)
The preconditions for a successful attack
Now, what are the ingredients for a disaster?
- unencrypted ViewState
- Gadget on the classpath of the server
- In case of Mojarra: ViewState configured to reside on the
- In case of MyFaces: ViewState configured to reside on the
Let’s have a look at those points in relation to the two JSF implementations.
Oracle Mojarra (JSF reference implementation)
As said before Oracle Mojarra is the JSF Reference Implementation (RI) but might not be known under that name. It might be known as Sun JSF RI, recognized with the java package name
com.sun.faces or with the ambiguous jar name
Mojarra: unencrypted ViewState
So here’s the thing: Mojarra did not encrypt and sign the client-side ViewState by default in most of the versions of 2.0.x and 2.1.x. It is important to note that a server-side ViewState is the default in both JSF implementations but a developer could easily switch the configuration to use a client-side viewstate by setting the
javax.faces.STATE_SAVING_METHOD param to
client. The param name does in no way give away that changing it to client introduces grave remote code execution vulnerabilities (e.g. a client-side viewstate might be used in clustered web applications).
Whilst client-side ViewState encryption is the default in Mojarra 2.2 and later versions it was not for the 2.0.x and 2.1.x branches. However, in May 2016 the Mojarra developers started backporting default client-side ViewState encryption to 2.0.x and 2.1.x when they realized that unencrypted ViewStates lead to RCE vulnerabilities.
When we analyzed the Mojarra libraries we noticed that Red Hat also releases Mojarra versions for the 2.1.x and 2.0.x branches, the latest being 2.1.29-jbossorg-1 and 2.0.4-b09-jbossorg-4. Since both releases were without default ViewState encryption we contacted Red Hat and they promptly created Bug 1479661 - JSF client side view state saving deserializes data in their bugtracker with following mitigation advice for the 2.1.x branch:
A vulnerable web application needs to have set javax.faces.STATE_SAVING_METHOD to 'client' to enable client-side view state saving. The default value on Enterprise Application Platform (EAP) 6.4.x is 'server'.
If javax.faces.STATE_SAVING_METHOD is set to 'client' a mitigation for this issue is to encrypt the view by setting com.sun.faces.ClientStateSavingPassword in the application web.xml:
Unfortunately, in some even older versions that mitigation approach does not work: according to this great StackOverflow answer in the JSF implementation documentation it was incorrectly documented that the param
com.sun.faces.ClientStateSavingPassword is used to change the Client State Saving Password, while the parameter up until 2.1.18 was accidentally called
ClientStateSavingPassword. So providing a Client State Saving Password as documented didn’t have an effect! In Mojarra 2.1.19 and later versions they changed the parameter name to the documented name
By default Mojarra nowadays uses
AES as encryption algorithm and
HMAC-SHA256 to authenticate the ViewState.
Mojarra: ViewState configured to reside on the client
javax.faces.STATE_SAVING_METHOD setting of Mojarra is
server. A developer needs to manually change it to
client so that Mojarra becomes vulnerable to the above described attack scenario. If a serialized ViewState is sent to the server but Mojarra uses
server side ViewState saving it will not try to deserialize it (However, a
StringIndexOutOfBoundsException may occur).
When using Mojarra with a server-side ViewState nothing has to be done.
When using Mojarra < 2.2 and a client-side ViewState there are following possible mitigations:
- Update Mojarra to 2.0.11-04 respectively 2.1.29-08.
- Use a server-side ViewState instead of a client-side ViewState.
- When using older Versions of Mojarra and an update or switching to a server-side ViewState is not possible: set a ViewState password as temporary solution and make sure it is the right parameter (not necessarily the one in the corresponding documentation)
For later Mojarra versions:
- Check that the ViewState encryptions is not disabled via the param:
Apache MyFaces is the other big and widely used JSF implementation.
MyFaces: unencrypted ViewState
MyFaces does encrypt the ViewState by default, as stated in their Security configuration Wiki page:
Encryption is enabled by default. Note that encription must be used in production environments and disable it could only be valid on testing/development environments.
However, it is possible to disable ViewState encryption by setting the parameter
false. (Also it would be possible to use encryption but manually set an easy guessable password). By default the ViewState encryption secret changes with every server restart.
By default MyFaces uses
DES as encryption algorithm and
HMAC-SHA1 to authenticate the ViewState. It is possible and recommended to configure more recent algorithms like
MyFaces: ViewState configured to reside on the client
javax.faces.STATE_SAVING_METHOD setting of MyFaces is
But: MyFaces does always deserialize the ViewState regardless of that setting. So it is of great importance to not disable encryption when using MyFaces!
(We created an issue in the MyFaces bug tracker: MYFACES-4133 Don’t deserialize the ViewState-ID if the state saving method is server, maybe this time the wish for more secure defaults will catch on.)
When using MyFaces make sure that encryption of the ViewState is not disabled (via
org.apache.myfaces.USE_ENCRYPTION) regardless if the ViewState is stored on the client or the server.
Most facts about JSF ViewStates and their dangers presented in this blog post are not exactly new but it seems they were never presented in such a condensed way. It showed once more that seemingly harmless configuration changes can lead to serious vulnerabilities.
=> One of the problems seems to be that there is not enough knowledge transfer between security researchers and developers who actually use and configure libraries that might be dangerous when configured in certain ways.