Java JSF ViewState (.faces) Deserialization

Intro

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

Scope

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.

Server-side ViewState

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!

Client-side ViewState

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 dangers of serialized Java objects

In 2015 at the AppSec California conference Gabriel Lawrence and Chris Frohoff held a presentation with the title Marshalling Pickles (how deserializing objects can ruin your day). This presentation shed some light on forgotten problems with Java object serialization and led to the discovery of several severe remote code execution (RCE) vulnerabilities.

Unfortunately, it led some people to believe that the vulnerability could be mitigated by removing/updating certain versions of Apache Commons Collections. An action which can indeed help but does not solve the root cause of the problem: Deserialization of Untrusted Data (CWE 502). In other words:
The use of a ‘vulnerable’ Apache Commons Collections version does not mean that the application is vulnerable, neither does the absence of such a library version mean that the application is not vulnerable.

However, after a malicious hacker shut down and encrypted the systems of the San Francisco Municipal Transportation Agency via a “Mad Gadget”/“Apache Commons Collections Deserialization Vulnerability” Google started Operation Rosehub. The aim of operation Rosehub was to find as many Java open source projects as possible which used an ‘attacker-friendly’ commons collections version as dependency and submit pull requests to the project owners so that those projects would stop using problematic commons collections versions in newer releases.

The attack on the ViewState

Let’s assume we have a web application with a JSF based login page:

JSF based login

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:Unencrypted MyFaces ViewState:

<input type="hidden" name="javax.faces.ViewState" id="j_id__v_0:javax.faces.ViewState:1" value="rO0ABXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAAJwdAAML2xvZ2luLnhodG1s" autocomplete="off" />

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:

calculator started via a JSF ViewState

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 client
  • In case of MyFaces: ViewState configured to reside on the client or the server

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 jsf-impl.jar.

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.

So at least version 2.1.29-08 (released in July 2016) from the 2.1.x Branch and version 2.0.11-04 (also released in July 2016) from the 2.0.x have encryption enabled by default.

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:

  <context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
  </context-param>

  <env­-entry> 
    <env­-entry-­name>com.sun.faces.ClientStateSavingPassword</env­-entry-­name> 
    <env-­entry-­type>java.lang.String</env-­entry-­type> 
    <env-­entry-­value>[some secret password]</env-­entry-value>
  </env­-entry>

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 com.sun.faces.ClientStateSavingPassword.

By default Mojarra nowadays uses AES as encryption algorithm and HMAC-SHA256 to authenticate the ViewState.

Mojarra: ViewState configured to reside on the client

The default 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).

Mojarra: Mitigation

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: com.sun.faces.disableClientStateEncryption

Apache MyFaces

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 org.apache.myfaces.USE_ENCRYPTION to 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 AES and HMAC-SHA256.

MyFaces: ViewState configured to reside on the client

The default javax.faces.STATE_SAVING_METHOD setting of MyFaces is server. 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.)

MyFaces: Mitigation

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.

Custom Encryption

If somehow you manage to steal the password used you can attack the web server encrypting and signing the payload with this script:

#!/usr/bin/python3
import sys
import hmac
from urllib import parse
from base64 import b64encode
from hashlib import sha1
from pyDes import *

YELLOW = "\033[93m"
GREEN = "\033[32m"

def encrypt(payload,key):
	cipher = des(key, ECB, IV=None, pad=None, padmode=PAD_PKCS5)
	enc_payload = cipher.encrypt(payload)
	return enc_payload

def hmac_sig(enc_payload,key):
	hmac_sig = hmac.new(key, enc_payload, sha1)
	hmac_sig = hmac_sig.digest()
	return hmac_sig

key = b'JsF9876-'

if len(sys.argv) != 3 :
	print(YELLOW + "[!] Usage : {} [Payload File] [Output File]".format(sys.argv[0]))
else:
	with open(sys.argv[1], "rb") as f:
		payload = f.read()
		f.close()
	print(YELLOW + "[+] Encrypting payload")
	print(YELLOW + "  [!] Key : JsF9876-\n")
	enc_payload = encrypt(payload,key)
	print(YELLOW + "[+] Creating HMAC signature")
	hmac_sig = hmac_sig(enc_payload,key)
	print(YELLOW + "[+] Appending signature to the encrypted payload\n")
	payload = b64encode(enc_payload + hmac_sig)
	payload = parse.quote_plus(payload)
	print(YELLOW + "[*] Final payload : {}\n".format(payload))
	with open(sys.argv[2], "w") as f:
		f.write(payload)
		f.close()
	print(GREEN + "[*] Saved to : {}".format(sys.argv[2]))

Final thoughts

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.

References