Java Deserialization Vulnerability in ZAP Viewstate Add-on

Posted 509 Words

First of all, the bad news:

Now the good news:

  • Neither the ZAP team or Neo have been able to prove an RCE at this stage
  • It is in an alpha add-on: Viewstate which is not included in any of the ZAP releases by default
  • The add-on is relatively obscure, and does not appear to be widely installed
  • It only impacts the ZAP Desktop, and requires user interaction
  • We patched it in 30 minutes of it being reported

ℹ️ Important

Please update all of your ZAP 2.17 add-ons right now.

We do recommend that you auto-update ZAP add-ons so that you will automatically be protected as soon as we publish fixes.

‼️ Caution

If you are using an older version of ZAP then please update to the latest version (currently 2.17) ASAP - we are sure that older versions will be vulnerable to Remote Code Execution!

The Vulnerability

The vulnerability was triggered by a ZAP user selecting a response containing a malicious javax.faces.ViewState value through the ZAP UI.

The Viewstate add-on parsed javax.faces.ViewState from proxied HTTP traffic and deserialized it when the ViewState panel renders.

The vulnerable code is in JSFViewState.java :

public <T> T decode(String base64) {
    // BASE64Decoder decoder = new BASE64Decoder();
    try {
        // byte[] b = decoder.decodeBuffer(base64);
        byte[] b = Base64.getDecoder().decode(base64);
        ByteArrayInputStream bais = new ByteArrayInputStream(b);
        ObjectInputStream ois = new ObjectInputStream(bais);
        return (T) ois.readObject();
    } catch (IllegalArgumentException | IOException | ClassNotFoundException e) {
        throw new RuntimeException(e);
    }
}

The readObject() call is vulnerable as it will instantiate any class on the classpath.

As a PoC the researcher was able to successfully trigger an outgoing DNS query.

The Fix

The initial fix was very simple - we disabled the option to view JSF ViewStates: https://github.com/zaproxy/zap-extensions/pull/7481/changes

Why no RCE?

The initial report, and the analysis via an LLM, indicated that RCE would be possible. However we tried all of the suggested patterns and were unable to make any of them work.

There are 2 reasons why the potential RCEs failed:

  • All ZAP add-ons run in their own classloaders, this is a key defence
  • We keep our dependencies as up to date as we can

Having said that, someone else may find a way to trigger an RCE from this vulnerability.

Next Steps

The initial fix was the quickest way we could get a fix out to everyone using the Viewstate add-on. We will be investigating what else we can do to fix problems like this, and to add any other measures which will help mitigate any vulnerabilities which slip through.

Also please note, that as per our FAQ What is ZAP’s Assurance case:

  • If the user is testing potentially malicious sites we recommend taking additional precautions (as one would when dealing with malware), such as running ZAP in a container or VM.

Many thanks to the researcher from Neo by ProjectDiscovery for reporting this vulnerability, and for working with us to make sure that ZAP users stay as safe as possible.