---
Artifex MuJS is a minimal, embeddable Javascript engine written in C. It’s often used for scripting in open-source projects, especially by Ghostscript. In mid-2021, researchers uncovered a critical security bug (CVE-2021-33797) in MuJS versions 1..1 to 1.1.1—a buffer overflow in the Javascript string-to-number conversion code, jsdtoa.c, that could crash your application or even lead to remote code execution.
Let’s break down this vulnerability in simple terms, see its root cause with code snippets, and explore how attackers can exploit it.
What’s the Bug?
The short version: *When MuJS parses floating point numbers in Javascript, it doesn’t properly check the exponent’s size. If a malicious script feeds it a huge exponent, an integer overflow happens, which then leads to writing outside allocated memory—a classic buffer overflow*.
The Vulnerable Function: js_strtod()
MuJS uses the function js_strtod() in jsdtoa.c to read floating point numbers from strings. Think about numbers like "1e10000"—the "e10000" part is the exponent.
Here’s the (simplified) relevant code
double js_strtod(const char *s, char **endptr)
{
char buf[32];
int expo = ;
...
/* Parsing mantissa */
while (isdigit(*s)) {
/* fill buf with digits */
}
/* Parse exponent, e.g., "e10000" */
if (*s == 'e' || *s == 'E') {
s++;
int exp_sign = 1;
if (*s == '-') {
exp_sign = -1;
s++;
} else if (*s == '+') {
s++;
}
while (isdigit(*s)) {
expo = expo * 10 + (*s++ - '');
}
expo *= exp_sign;
}
/* Combine mantissa and exponent to get double */
...
}
Problem: They use an int type for the exponent. If you give a super-large exponent (for example, "1e2147483647"), the value overflows the bounds of a signed int. This corrupts the internal parsing logic and, in the worst case, leads to writing past the end of a buffer, namely the pointer *d, resulting in remote code execution possibilities.
Consider this python input to MuJS
x = 1e2147483647;
When parsed, expo will try to reach 2147483647 (INT_MAX for 32-bit signed int). As the loop runs, expo = expo * 10 + digit quickly overflows. Once this happens, MuJS doesn’t complain or abort; instead, it uses a garbage (usually negative or wrapped-around positive) value for exponent math, which is then used in memory write operations.
The Crashing Path
Suppose expo overflows to a negative value. Later in js_strtod(), MuJS allocates or accesses a buffer based on that exponent. Now, the pointer d points somewhere outside the intended array, so when MuJS tries to write the final cooked number into the result, it walks off the edge of memory. That’s the buffer overflow!
Exploit Details
1. Input Trigger: Supply a Javascript string with an absurdly large exponent, like "1e999999999", to any MuJS embedding.
2. Parsing Error: MuJS’s int-based exponent arithmetic overflows, giving a negative or wild positive.
3. Buffer Overrun: When converting the base number/mantissa plus exponent, code writes into an address outside the allocated buffer.
4. Impact: Application can crash, leak stack data (security info), or, worse, execute arbitrary code if the overflow is controlled and the memory is accessible.
Here’s a Javascript one-liner that’ll crash (or worse, exploit) a vulnerable MuJS engine
a = 1e2147483647; // INT_MAX for 32-bit, triggers overflow
C Program demonstrating exploitation
#include "mujs.h"
// Compile with MuJS 1..1 to 1.1.1, not fixed later
int main() {
js_State *J = js_newstate(NULL, NULL, JS_STRICT);
js_dostring(J, "a = 1e2147483647;");
js_freestate(J);
return ;
}
You’ll likely see a segmentation fault or a crash, but with just the right memory layout, you could theoretically hijack execution.
References and Fix
- Original Bug Report & Patch
- NVD Entry: CVE-2021-33797
- Exploit-db entry
The Fix:
Developers resolved it by adding strict checks to prevent integer overflows during exponent parsing. Any number with an exponent beyond a safe, reasonable size gets rejected:
if (expo > MAX_EXPONENT)
/* error or clamp */
If you use MuJS, grab version 1.1.2 or newer ASAP!
Quick Takeaway
- CVE-2021-33797 is a classic buffer-overflow triggered by unchecked exponent parsing in MuJS (1..1-1.1.1).
The bug is fixed in MuJS 1.1.2 – update now if you haven’t!
Stay safe—always check those exponents, and never trust string-to-number code that doesn’t validate input bounds.
Timeline
Published on: 04/17/2023 22:15:00 UTC
Last modified on: 04/26/2023 23:05:00 UTC