Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing CVE-2021-42279: Chakra Scripting Engine Memory Corruption Vulnerability #6996

Closed
wants to merge 13 commits into from

Conversation

bhmohanr-techie
Copy link

@bhmohanr-techie bhmohanr-techie commented Jul 1, 2024

Changes For

CVE-2021-42279: Chakra Scripting Engine Memory Corruption Vulnerability. CVE-2021-42279 is a high-severity memory corruption vulnerability affecting the Chakra Scripting Engine and ChakraCore.

The vulnerability stems from an out-of-bounds write, which can potentially be exploited to achieve remote code execution (RCE). An attacker could exploit this by convincing a user to perform certain actions, leading to memory corruption and potentially allowing the attacker to execute arbitrary code on the affected system.

Vulnerability Type

Memory Violation, resulting in Remote Code Execution (RCE)

Affected Versions

All versions of ChakraCore up to and including 1.11.24.

Severity

CVSS Score: 7.5 (HIGH)

Root Cause

  • An out-of-bounds array write occurs when a program writes data to a memory location outside the bounds of the allocated memory for an array.

  • In the context of ChakraCore, an out-of-bounds write can lead to memory corruption. When data is written outside the bounds of an array, it can overwrite adjacent memory regions that may be used for other variables, objects, or control structures. This unintended overwrite can corrupt data, causing unpredictable behavior or program crashes.

  • Consider a simplified scenario where ChakraCore mishandles the bounds of an array:
    // JavaScript code that might trigger out-of-bounds write
    let arr = new Array(1);
    arr[100] = 42; // This could lead to out-of-bounds write if not properly handled

  • If ChakraCore does not correctly check the bounds before writing to the ‘arr[100]’ position, it could write the value ‘42’ to a memory location outside the allocated space for ‘arr’. This could overwrite important data or control structures, leading to memory corruption.

Fix
In this PR, I have added two changes:

  1. Merging the changes from December 2020 Security Update #6531 which will bring the fix added for CVE-2020-17131, and was made available as part of December 2020 Windows security update
    - The December 2020 security update from Microsoft has fix to address "CVE-2020-17131 : Out-of-bounds Write in Chakra, Chakra Scripting Engine Memory Corruption Vulnerability"
    - This fix was added as part of this pull request December 2020 Security Update #6531 on December 8, 2020.
    - When I checked to see if this fix is part of the latest Chakracore code base, I found it missing in both 1.12 and master branches.
    - So, here I'm adding changes to merge the fix added in Microsoft as part of their December 2020 security update.

  2. In addition to merging above mentioned PR, a defensive check is also added to handle out-of-bounds array read/write, as described below:
    - To address this issue, fix is added to allow setting elements only if the index falls within the bounds of the array.
    - This fix is added such that, it takes effect only if additional command line switch “--ValidateArrayBounds” is passed to ChakraCore. With this, we ensure that the existing functionalities of ChakraCore continues to work as before.
    - Users of ChakraCore can take advantage of the newly added switch “--ValidateArrayBounds", which helps in making sure, any injection of elements to an array is restricted only within the bounds of the array. With this, we ensure that the out-of-bound write issue is not seen, there by preventing write operation to sensitive memory locations.
    - By default, “--ValidateArrayBounds" will be false.

Unit Testing

  • Ensured that all commit checks are passing
  • All unit test cases are executed and there is no failure due to these new changes. In fact, this fix takes effect only when the switch “--ValidateArrayBounds" is passed to the ChakraCore engine, so there is no impact to existing testcases/functionalities.

Sample Output

  • Consider Script "test.js" below:
    let arr = new Array(2);
    console.log("arr.length is " + arr.length)
    arr[0] = 0;
    console.log("arr[0] is " + arr[0])
    arr[1] = 1;
    console.log("arr[1] is " + arr[1])
    arr[100] = 100;
    console.log("arr[100] is " + arr[100])
    _

  • Output without the newly added switch,
    C:\temp>ch.exe test.js
    arr.length is 2
    arr[0] is 0
    arr[1] is 1
    arr[100] is 100

  • Output with the newly added switch,
    C:\temp>ch.exe --ValidateArrayBounds test.js
    arr.length is 2
    arr[0] is 0
    arr[1] is 1
    RangeError: Memory index is out of range
    at Global code (C:\temp\test.js:11:1)

References

=========
CVE-2021-42279: Chakra Scripting Engine Memory Corruption Vulnerability

CVE-2021-42279 is a high-severity memory corruption vulnerability affecting the Chakra Scripting Engine and ChakraCore.

The vulnerability stems from an out-of-bounds write, which can potentially be exploited to achieve remote code execution (RCE). An attacker could exploit this by convincing a user to perform certain actions, leading to memory corruption and potentially allowing the attacker to execute arbitrary code on the affected system.

Vulnerability Type:
=============
Memory Violation, resulting in Remote Code Execution (RCE)

Affected Versions:
============
All versions of ChakraCore up to and including 1.11.24.

Severity:
======
CVSS Score: 7.5 (HIGH)

Root Cause:
=========
# An out-of-bounds array write occurs when a program writes data to a memory location outside the bounds of the allocated memory for an array.
# In the context of ChakraCore, an out-of-bounds write can lead to memory corruption. When data is written outside the bounds of an array, it can overwrite adjacent memory regions that may be used for other variables, objects, or control structures. This unintended overwrite can corrupt data, causing unpredictable behavior or program crashes.
# Consider a simplified scenario where ChakraCore mishandles the bounds of an array:

// JavaScript code that might trigger out-of-bounds write
let arr = new Array(1);
arr[100] = 42; // This could lead to out-of-bounds write if not properly handled

# If ChakraCore does not correctly check the bounds before writing to the ‘arr[100]’ position, it could write the value ‘42’ to a memory location outside the allocated space for ‘arr’. This could overwrite important data or control structures, leading to memory corruption.

Fix:
===
# To address this issue, fix is added to allow setting elements only if the index falls within the bounds of the array.
# This fix is added such that, it takes effect only if additional command line switch “--ValidateArrayBounds” is passed to ChakraCore. With this, we ensure that the existing functionalities of ChakraCore continues to work as before.
# Users of ChakraCore can take advantage of the newly added switch “--ValidateArrayBounds", which helps in making sure, any injection of elements to an array is restricted only within the bounds of the array. With this, we ensure that the out-of-bound write issue is not seen, there by preventing write operation to sensitive memory locations.
# By default, “--ValidateArrayBounds" will be false.

Unit Testing:
==========
All unit test cases are executed and there is no failure due to these new changes. In fact, this fix takes effect only when the switch “--ValidateArrayBounds" is passed to the ChakraCore engine, so there is no impact to existing testcases/functionalities.

Sample Output:
===========
Consider Script "test.js" below:

let arr = new Array(2);
console.log("arr.length is " + arr.length)
arr[0] = 0;
console.log("arr[0] is " + arr[0])
arr[1] = 1;
console.log("arr[1] is " + arr[1])
arr[100] = 100;
console.log("arr[100] is " + arr[100])

Output without the newly added switch,
C:\temp>ch.exe test.js
arr.length is 2
arr[0] is 0
arr[1] is 1
arr[100] is 100

Output with the newly added switch,
C:\temp>ch.exe --ValidateArrayBounds test.js
arr.length is 2
arr[0] is 0
arr[1] is 1
RangeError: Memory index is out of range
   at Global code (C:\temp\test.js:11:1)

References:
========
https://nvd.nist.gov/vuln/detail/CVE-2021-42279
https://www.cve.org/CVERecord?id=CVE-2021-42279
https://github.com/chakra-core/ChakraCore
Avoid using MSVC-internal _STRINGIZE chakra-core#6970

Below is the comment from
Stephan T. Lavavej in the above mentioned pull request:

"I work on Microsoft's C++ Standard Library implementation, where we recently merged microsoft/STL#4405 to remove our internal _STRINGIZE macro. Our "Real World Code" test suite, which builds popular open-source projects like yours, found that you were using this MSVC-internal macro and therefore our change broke your code.

The C++ Standard's rule is that _Leading_underscore_capital identifiers (including _LEADING_UNDERSCORE_ALL_CAPS) are reserved for the compiler and Standard Library, so other libraries and applications should avoid using such reserved identifiers. This is N4971 5.10 [lex.name]/3:

In addition, some identifiers appearing as a token or preprocessing-token are reserved for use by C++ implementations and shall not be used otherwise; no diagnostic is required.
— Each identifier that contains a double underscore __ or begins with an underscore followed by an uppercase letter is reserved to the implementation for any use.

This PR introduces non-reserved names that will work on all platforms."
…es as defined in tools/StyleChecks/check_copyright.py.
@bhmohanr-techie
Copy link
Author

Hi Petr Penzin (@ppenzin )

I hope this message finds you well. I have submitted this pull request (#6996) for ChakraCore that addresses the vulnerability: "CVE-2021-42279 - Chakra Scripting Engine Memory Corruption Vulnerability".

Could you please take some time to review it when you have a chance? I would appreciate any feedback or suggestions you may have.

Thank you for your time and for maintaining ChakraCore. I look forward to your feedback.

@ppenzin
Copy link
Member

ppenzin commented Jul 3, 2024

JavaScript arrays (unlike C or Java arrays) allow writing to arbitrary indices, which then get allocated. Think of JS array indices as properties (fields in other languages) that have numeric names, with the whole object dynamically resizable.

To check whether or not you are observing a memory corruption tools like valgrind can be handy.

Since this vulnerability is reported against Microsoft's NuGet package and not this repo directly I don't have much insight into reproducing it further, also report seems to be only affecting older versions of Windows, which I suspect would be only triggered by an older version of ChakraCore that was used there (which also had additional proprietary API layer).

@bhmohanr-techie
Copy link
Author

Thank you very much for your thoughts, Petr Penzin (@ppenzin).

Below are my comments, please correct me if I'm wrong anywhere. (apologies for this lengthier comment, but I want to provide complete information based on which I have come-up with this fix)

An out-of-bounds array write occurs when a program writes data to a memory location outside the bounds of the allocated memory for an array.

In the context of ChakraCore, an out-of-bounds write can lead to memory corruption in the following ways:

1. Overwrite Adjacent Memory:
When data is written outside the bounds of an array, it can overwrite adjacent memory regions that may be used for other variables, objects, or control structures. This unintended overwrite can corrupt data, causing unpredictable behavior or program crashes.

2. Corrupt Control Structures:
If the out-of-bounds write affects control structures such as function pointers, return addresses, or vtable pointers, it can alter the program's control flow. This can be exploited to execute arbitrary code, leading to potential security vulnerabilities like remote code execution.

3. Heap Corruption:
Writing beyond the bounds of a heap-allocated array can corrupt the heap metadata. This can cause heap management functions (e.g., ‘malloc’, ‘free’) to behave incorrectly, potentially leading to further memory corruption, crashes, or exploitable conditions.

4. Stack Corruption:
An out-of-bounds write on the stack can overwrite the return address or local variables of a function. This can result in a stack-based buffer overflow, allowing attackers to redirect the execution flow and execute arbitrary code.

In ChakraCore, an out-of-bounds write typically arises from improper handling of JavaScript arrays or typed arrays. Here’s a more detailed look at how this can happen:

1. JavaScript Arrays:
JavaScript arrays in ChakraCore are dynamic and can grow as needed. However, if the engine improperly calculates the bounds of the array and allows an out-of-bounds write, it can overwrite other memory regions.

2. Typed Arrays:
Typed arrays provide a way to handle binary data directly. They have fixed sizes and are backed by raw memory buffers. If ChakraCore allows an out-of-bounds access to these buffers, it can lead to memory corruption.

Example Scenario
Consider a simplified scenario where ChakraCore mishandles the bounds of an array:

// JavaScript code that might trigger out-of-bounds write
let arr = new Array(1);
arr[100] = 42; // This could lead to out-of-bounds write if not properly handled

If ChakraCore does not correctly check the bounds before writing to the ‘arr[100]’ position, it could write the value ‘42’ to a memory location outside the allocated space for ‘arr’. This could overwrite important data or control structures, leading to memory corruption.

About the Fix:

To address this issue, fix is added to allow setting elements only if the index falls within the bounds of the array. And as I mentioned in my PR commit message, we have ensured two things

  1. We are not breaking existing functionality of ChakraCore
  2. We added a command-line switch, which can be used by users of ChakraCore who need this behavior of writing outside the bounds of an array.

So, none of the exiting functionalities of ChakraCore is affected by this change.

Please let me know if you have any thoughts on my analysis and fix above, and also if you can consider merging the change (please note, this change can help users like me who are still using older version of ChakraCore to take advantage of the fix for the vulnerability)

Thank-you again for your thoughts and suggestions !!!

@ppenzin
Copy link
Member

ppenzin commented Jul 3, 2024

Can you please provide a test that exhibits an out-of-bounds read or write that can be detected via valgrind or similar tool?

bhmohanr-techie and others added 4 commits July 7, 2024 21:56
1. I have added two new tests as part of the unit test framework within ChakraCore (one test to verify out-of-bounds access behavior without my  fix, and other test with my fix). In both the cases, I have verified that the tests are PASSED.
2. In addition to my earlier commits for handling out-of-bounds write with javascript arrays, I have added a change here to address out-of-bounds read scenario.
…ests... Added a change here to summarize the unit test results, so that the unit test framewokr can capture and report the test results.
@ShortDevelopment
Copy link
Contributor

Hey @bhmohanr-techie thanks for your work!

What @ppenzin meant is that we don’t need to merge this PR because

  1. The scenario you describe is actually fine by design.
    The array size will automatically be increased if needed.
    if (itemIndex >= this->length)
    {
    if (itemIndex != JavascriptArray::InvalidIndex)
    {
    this->length = itemIndex + 1;
    }
    else
    {
    JavascriptError::ThrowRangeError(this->GetScriptContext(), JSERR_ArrayLengthAssignIncorrect);
    }
    }
  2. AFAIK there is no public information of what actually causes this vulnerability.
    ⇒ Your explicit out-of-bounds check sadly does not fix this vulnerability.

The scenario you provided runs just fine in any JS engine.

let arr = new Array(1);
// arr := [ empty ]
arr[100] = 42;
// arr := [,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,42]

@bhmohanr-techie
Copy link
Author

Hi Petr Penzin (@ppenzin ) and Lukas Kurz (@ShortDevelopment )

Greetings!!!

As mentioned by you, there is no clear information available on what actually causes this vulnerability. However, the fix attempted in my PR is to handle a possible scenario where out-of-bounds access can lead to this issue. My fix can help users, who strictly want to access elements within the defined array boundary. Also, please refer to my analysis below with multiple tools and let me know your valuable suggestions.

Please find below my response for Pete's latest comment on tests/tools to exhibit out-of-bounds array read/write:

Valgrind is a programming tool primarily used for memory debugging, memory leak detection, and profiling for programs written in languages like C and C++. However, it does not support JavaScript directly. For JavaScript, there isn't a direct equivalent of Valgrind that can detect out-of-bounds errors. However, there are other tools and methods specifically designed for JavaScript that can help you detect such issues. Below are my findings on the same:

When it comes to checking for out-of-bounds errors in JavaScript arrays, the ChakraCore engine, like other JavaScript engines, adheres to the ECMAScript standard which specifies how arrays should behave. JavaScript arrays are designed to handle out-of-bounds access gracefully by returning 'undefined' for non-existent elements.

Possible Approaches:

However, to explicitly check for out-of-bounds access in your JavaScript code while using ChakraCore, we can use the following approaches:

1. Manual Check
We can manually check if an index is within the bounds of the array before accessing it. I have followed similar approach in the fix I have shared (by making sure, my change doesn't affect any of the existing functionalities of ChakraCore).

function accessArray(arr, index) {
if (index >= 0 && index < arr.length) {
return arr[index];
} else {
throw new Error("Array index out of bounds");
}
}
let myArray = [1, 2, 3, 4, 5];
console.log(accessArray(myArray, 2)); // Outputs: 3
console.log(accessArray(myArray, 5)); // Throws error: Array index out of bounds

2. Using Proxy
We can use JavaScript 'Proxy' to create an array that automatically checks for out-of-bounds access. This approach is yet another way of checking elements of array by using their index/property.

let handler = {
get: function(target, property) {
if (property in target) {
return target[property];
} else {
throw new Error("Array index out of bounds");
}
}
};
let myArray = [1, 2, 3, 4, 5];
let proxyArray = new Proxy(myArray, handler);
console.log(proxyArray[2]); // Outputs: 3
console.log(proxyArray[5]); // Throws error: Array index out of bounds

3. ChakraCore Debugger
We can also use built-in debugging tools of ChakraCore to set breakpoints and watch expressions to monitor array accesses. This can help to catch out-of-bounds errors during development. When I added this code fix, and tested my code, I was using the local debugger for verifying the code flow.

Static Analysis Tools

We could use tools like ESLint, Flow, or TypeScript to catch potential out-of-bounds errors during development. In my fix here, I have tried using ESLint

  • ESLint (https://eslint.org/): ESLint is a static code analysis tool for identifying problematic patterns found in JavaScript code
  • Flow (https://flow.org/): Flow is a static type checker for your JavaScript code.
  • TypeScript (https://www.typescriptlang.org/): TypeScript is a free and open-source high-level programming language developed by Microsoft that adds static typing with optional type annotations to JavaScript.

I have evaluated the behavior with ESLint and Flow tools. Below are the results.

1. ESLint:
With ESLint, it provides rules and plugins that help to identify problematic pattern in javascript. Please find below the screenshot where I had used ESLint to validate problematic code. It highlights the lines of code where there is a problem, which includes problems like out-of-bounds access and using sparse arrays with empty elements.

image

2. Flow:
With Flow, I was able to test my javascript code, but Flow doesn't highlight out-of-bounds issue. The reason here is, Flow in their documentation clearly had mentioned that out-of-bounds access is unsafe and should be handled by developers. Please refer below the screenshots for the same.

image

image

Unit Testing:
In addition to taking care of validating the behavior with above mentioned tools and approaches, I also have added two new tests as part of the unit test framework within ChakraCore (one test to verify out-of-bounds access behavior without my fix, and other test with my fix). In both the cases, I see the tests PASSED. Please refer to the screenshot below,

image

With all the observations above, I could summarize as below:

  • First and foremost, my code fix here doesn't impact any of the existing behavior of ChakraCore. The fix is disabled by default (enable only when the newly added is flag is passed), and hence existing users will not be affected.
  • It helps users of ChakraCore who are still using older version, to be safe from the reported vulnerability
  • There are adequate tools and approaches that are evaluated above, and the behavior is as per expectation in all cases.

So Pete, requesting you to kindly share your thoughts on above observations, as well as whether we can consider merging this change for the benefit of affected users.

Thank-you once again.

bhmohanr-techie and others added 5 commits July 8, 2024 18:40
…g UnitTestFramework... This will be replaced with new unit test soon, that adheres to ChakraCore's unit test framework.
Merging PR: chakra-core#6531. 
This is the change that was added by Microsoft as part of their December 2020 Security Update, which addresses the CVE-2020-17131. This CVE is to address Out-of-bounds Write in ChakraCore, and there by fixing memory corruption vulnerability. This change is unfortunately missed in the latest ChakraCore code base, and hence adding this for review.
@bhmohanr-techie
Copy link
Author

Hi Petr Penzin (@ppenzin )

Greetings!!! Hope you are doing good.

After our last discussion, I did further analysis to figure out the fix for the vulnerability CVE-2021-42279. As part of my analysis, I went thru the history of Chakracore, and below are my findings:

  • 2018: In December 2018, Microsoft announced that they were moving the Microsoft Edge browser from their proprietary EdgeHTML engine (which included ChakraCore) to the Chromium project, which uses the V8 JavaScript engine.
  • 2019: First Chromium-based Edge Preview
  • January 15, 2020: The official launch of Chromium-based Microsoft Edge. It was available for manual download for Windows 10, Windows 7, Windows 8/8.1, and macOS.
  • June 2020: Microsoft began the automatic rollout of Chromium-based Edge through Windows Update. This update replaced the legacy Edge browser with the new Chromium-based version
  • 2021: Archiving of ChakraCore on GitHub.
  • November 09, 2021: CVE-2021-42279 was published.
  • December 08, 2021: In December 2020 Security Update, Microsoft released fix for memory corruption issue in Chakracore due to out-of-bounds write

I did spend some time, by verifying multiple online resources, and figured out that the December 2020 security update from Microsoft has fix to address "CVE-2020-17131 : Out-of-bounds Write in Chakra, Chakra Scripting Engine Memory Corruption Vulnerability"

This fix was added as part of this pull request #6531 on December 8, 2020.

When I checked to see if this fix is part of the latest Chakracore code base, I found it missing in both 1.12 and master branches.

So, here I'm adding changes to merge the fix added in Microsoft as part of their December 2020 security update.

So, in this PR, I have two changes added:

  1. Merging the changes from December 2020 Security Update #6531 which will bring the fix added for CVE-2020-17131, and was made available as part of December 2020 Windows security update
  2. My earlier changes to restrict read/write on javascript arrays. This is a safe change, as by default, this code path never gets executed (it gets invoked only when the newly added "--ValidateArrayBounds" command line switch is passed as an argument. This fix can be helpful, for anyone with this specific ask. In general, it doesn't impact existing users who don't use this new switch.

Please review this change and let me know if the changes are looking good and can be considered for merging in the master branch.

Thankyou!!!

@bhmohanr-techie
Copy link
Author

bhmohanr-techie commented Aug 26, 2024

Hi Petr Penzin (@ppenzin )

Greetings!!! I hope this message finds you well.

Please let me know if you had chance to review my earlier comment. As mentioned in that comment, after doing some research I figured out that the fix added my Microsoft in Dec '2020 windows patch is not merged in Chakrocore.

I have merged the PR from Microsoft.

So, we have two changes as part of this pull request:

  1. my earlier fix to restrict the array read/write within bounds (and this code fix is enabled only if a commandline parameter is passed)
  2. merging the changes from December 2020 Security Update #6531 which will bring the fix added for GHSA-qwwg-gc55-qqrv,

Also, though my change 1 above is safe (enabled if a CLI parameter is passed), if you prefer only merging the PR from microsoft, we can do that as well.

Kindly let me know your thoughts here.

Thanks.

@ppenzin
Copy link
Member

ppenzin commented Sep 9, 2024

If this wasn't claimed to be a security fix I'd probably let it hang around longer. Specific questions were asked by maintainers and sufficient answers were not received.

I don't see how unrelated 2020 fix applies here or why it needs to be merged into master.

@ppenzin ppenzin closed this Sep 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants