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

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

bhmohanr-techie
Copy link

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

  • 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

=========
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.

…g UnitTestFramework... This will be replaced with new unit test soon, that adheres to ChakraCore's unit test framework.
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.

None yet

3 participants