Chrome zero-day: “This exploit is in the wild”, so check your version now

Security

Google’s latest Chrome update is out, and this time the company hasn’t minced its words about one of the two security patches it includes:

Google is aware that an exploit for CVE-2023-3079 exists in the wild.

There’s no two-degrees-of-separation verbiage, as we’ve often seen from Google before, to say that the company “is aware of reports” of an exploit.

This time, it’s “we are aware of it all by ourselves”, which translates even more bluntly into “we know that crooks are abusing this as we speak”, given that the bug report came directly from Google’s own Threat Research Group.

As usual, this implies that Google was investigating an active attack (whether against Google itself, or some external organisation, we don’t know) in which Chrome had been pwned by a previously unknown security hole.

The bug is described simply as: Type Confusion in V8. (Understandably, Google’s not saying more than that at this stage.)

As we’ve explained before, a type confusion bug happens when you supply a program with a chunk of data that it’s supposed to parse, validate, process and and act upon in one way…

…but you later manage to trick the program into interpreting the data in a different, unauthorised, unvalidated, and potentially dangerous way.

Type confusion dangers explained

Imagine that you’re writing a program in C. (It doesn’t matter whether you know C or not, you can just follow along anyway.)

In C, you usually declare variables individually, thus not only reserving memory where they can be stored, but also signalling to the program how those variables are supposed to be used.

For example:

  long long int JulianDayNumber;
  signed char*  CustomerName;

The first variable declaration reserves 64 bits for storing a plain old integer value representing the astromonomical day number. (In case you’re wondering, this afternoon is JDN 23157 – Julian Days start at noon, not midnight, because astronomers often work at night, with midnight being the middle of their working day.)

The second reserves 64 bits for storing a memory address where the text string of a customer’s name can be found.

As you can imagine, you’d better not mix up these two values, because a number that makes sense, and is safe, to use as a day number, such as 23157, would almost certainly be unsafe to use as a memory address.

As you can see from this memory dump of a running Windows program, the lowest memory address that’s allocated for use starts at 0x00370000, which is 3,604,480 in decimal, way larger than any sensible day number.

The actual memory addresses used by Windows vary randomly over time, to make your memory layout harder for crooks to guess, so if you were to run the same program, you’d get values, but they’ll nevertheless be similar:

And (although it’s off the bottom of the image above) the memory addresses of the runtime user data section when this program ran from 0x01130000 to 0x01134FFF, representing the unlikely date range of 22 July 44631 to 16 August 44687.

Indeed, if you try to mix those two variables up, the compiler should try to warn you, for example like this:

  JulianDayNumber = CustomerName;
  CustomerName = JulianDayNumber;

  warning: assignment makes integer from pointer without a cast
  warning: assignment makes pointer from integer without a cast

Now, if you’ve ever programmed in C, you’ll know that for convenience, you can declare variables with multiple different interpretations using the union keyword, like this:

  union {
    long long int JulianDayNumer;
    signed char*  CustomerName;
  } data;

You can now reference exactly the same variable in memory in two different ways.

If you write data.JulianDayNumber, you magically interpret the stored data as an integer, but writing data.CustomerName tells the compiler you’re referencing a memory address, even though you’re accessing the same stored data.

What you’re doing, more or less, is admitting to the compiler that you’ll sometimes be treating the data you’ve got as a date, and at other times as a memory address, and that you’re taking responsibility for remembering which interpretation applies at what moment in the code.

You might decide to have a second variable, known as a tag (typically an integer) to go along with your union to keep track of what sort of data you’re working with right now, for example:

  struct {
    int tag;
    union {
      long long int JulianDayNumer;
      signed char*  CustomerName;
    } data;
  } value;

You might decide that when value.tag is set to 0, the data isn’t initialised for use yet, 1 means you’re storing a date, 2 means it’s a memory address, and anything else denotes an error.

Well, you’d better not let anyone else mess with that value.tag setting, or your program could end up misbehaving dramatically.

A more worrying example might be something like this:

  struct {
    int tag;  // 1 = hash, 2 = function pointers
    union {
      unsigned char hash[16];  // either store a random hash
      struct {
        void* openfunc;        // or two carefully-validated
        void* closefunc;       // code pointers to execute later 
      } validate;
    }
  } value;

Now, we’re overloading the same block of memory so we can sometimes use it to store a 16-byte hash, and sometimes to store two 8-byte pointers to functions that our program will call upon later.

Clearly, when value.tag == 1, we’d be happy to let our software store any 16-byte string at all into the memory allocated for the union, because hashes are pseudorandom, so any collection of bytes is equally likely.

But when value.tag == 2, our code would need to be extra-super careful not to allow the user to provide unvalidated, untrusted, unknown function addresses to execute later.

Now imagine that you could submit a value to this code while tag was set to 1, so it didn’t get checked and validated…

…but later, just before the program actually used the stored value, you were able to trick the code into switching the tag to 2.

The code would then accept your unvalidated function addresses as “known and already verified safe” (even though they weren’t), and would trustingly dispatch program execution to a rogue location in memory that you’d sneakily choosen in advance.

And that is what happens in a type confusion bug, albeit using a contrived and simplified example,

Memory that would be safe to consume if if were handled one way is maliciously delivered to the program to process in an alternative, unsafe way.

What to do?

Make sure you have the latest version of Chrome or Chromium.

You want Chrome 114.0.5735.106 or later on Mac and Linux, and 114.0.5735.110 or later on Windows.

Microsoft Edge, which is based on Chromium, is also affected by this bug.

Microsoft has so far [2023-06-06T16:25:00Z] noted that

Microsoft is aware of the recent exploits existing in the wild. We are actively working on releasing a security patch.

Edge is currently at version 114.0.1823.37, so anything numbered later than that should include Microsoft’s CVE-2023-3079 patches.

To check your version and force an update if there is one that you haven’t received yet:

  • Google Chrome. Three-dot menu (⋮) > Help > About Chrome.
  • Microsoft Edge. Settings and more (…) > Help and feedback > About Microsoft Edge.

You’re welcome.


Products You May Like

Leave a Reply

Your email address will not be published. Required fields are marked *