alphacep / alphacep/vosk-api

Inadequate Input Validation and Bounds Checking in JSON parser

Ouverte
#1,966 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub
Langage dominant
Jupyter Notebook
Étoiles
15.1k
Forks
1.8k
Métriques de merge des PR
Aucune PR mergée en 30 j

Description

Hi,
I was taking a look at the robustness of the used JSON handler, and I found that the JSON parser in `json.h` is susceptible to several security vulnerabilities that can be triggered by malformed or malicious JSON input. These issues all stem from a single root cause: a lack of robust input validation and bounds checking during the parsing process. This can lead to out-of-bounds reads, resulting in crashes and undefined behavior.

## Description:

The JSON parser in json.h is susceptible to several security vulnerabilities that can be triggered by malformed or malicious JSON input. These issues all stem from a single root cause: a lack of robust input validation and bounds checking during the parsing process. This can lead to out-of-bounds reads, resulting in crashes, undefined behavior, or potential information disclosure.

## Root Cause:

The parser does not consistently verify that it is operating within the bounds of the input string, nor does it sufficiently handle unexpected or non-compliant data structures. This permissive parsing can be exploited by an attacker to cause denial of service or other security-related issues.

## Affected Functions and Vulnerabilities:

The provided patch addresses critical vulnerabilities in the following parsing functions:

- parse_object:
- Out-of-Bounds Read: A malformed object, such as one that is abruptly terminated (e.g., `{"key":`), could cause the parser to read past the end of the input string while searching for a colon, value, or closing brace.
- **Incorrect Key Type**: The original implementation did not validate that the key in a key-value pair is a string, as required by the JSON standard. This could lead to undefined behavior if a non-string key is encountered.
- parse_array:
- Out-of-Bounds Read: Similar to parse_object, a truncated or improperly formatted array (e.g., `[1, 2,`) could cause the parser to read beyond the input buffer when looking for a comma or closing bracket.
- parse_string:
- Out-of-Bounds Read: An unterminated string literal (e.g., `"abc`) or a malformed escape sequence at the end of the input (e.g., `"abc\\`) could lead to an out-of-bounds read as the parser continues to search for a closing quote or the character following a backslash.
- parse_number:
- Out-of-Bounds Read: A number followed by an unexpected character could cause the parser to read past the end of the valid number string.
- **Unhandled Exceptions**: The original code did not handle exceptions that can be thrown by `std::stol` and `std::stod` for invalid arguments, potentially leading to a crash if the parsed number string is malformed (e.g., containing non-numeric characters).

All of the above bugs were successfully triggered by my fuzzer.

## Example log:

```
0x7bc956de0073 is located 0 bytes after 35-byte region [0x7bc956de0050,0x7bc956de0073)
allocated by thread T0 here:
# 0 0x55a4ae1ab179(/ home / foo / vosk - api / fuzz / harness + 0x25b179)( \
BuildId : 44f859b11e459f2f02a6984fe9aefbe87662a9dd)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/foo/vosk-api/fuzz/harness+0x844774) (BuildId: 44f859b11e459f2f02a6984fe9aefbe87662a9dd)
Shadow bytes around the buggy address:
0x7bc956ddfd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bc956ddfe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bc956ddfe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bc956ddff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7bc956ddff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7bc956de0000: fa fa fd fd fd fd fd fd fa fa 00 00 00 00[03]fa
0x7bc956de0080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x7bc956de0100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x7bc956de0180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x7bc956de0200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x7bc956de0280: 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
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==1222728==ABORTING
[2025-07-29T10:28:56Z ERROR libafl::executors::hooks::unix::unix_signal_handler] Crashed with SIGABRT
[2025-07-29T10:28:56Z ERROR libafl::executors::hooks::unix::unix_signal_handler] Child crashed!
[2025-07-29T10:28:56Z ERROR libafl::executors::hooks::unix::unix_signal_handler] input: "d0cd3f16254b9e1e"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ CRASH ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Received signal SIGABRT at 0x007f8957aa8f2b, fault address: 0x00000000000000
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ REGISTERS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
r8 : 0x00000000000016, r9 : 0x007f89581068c0, r10: 0x00000000000003, r11: 0x00000000000246,
r12: 0x00000000000000, r13: 0x00000000000001, r14: 0x007bc956de0073, r15: 0x00000000000001,
rdi: 0x0000000012a848, rsi: 0x0000000012a848, rbp: 0x0000000012a848, rbx: 0x00000000000006,
rdx: 0x00000000000006, rax: 0x00000000000000, rcx: 0x007f8957aa8f2b, rsp: 0x007ffcd82aacb0,
rip: 0x007f8957aa8f2b, efl: 0x00000000000246,
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ BACKTRACE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
0: libafl_bolts::minibsod::generate_minibsod
at /home/foo/git/publ/LibAFL/crates/libafl_bolts/src/minibsod.rs:1122:30
1: libafl::executors::hooks::unix::unix_signal_handler::inproc_crash_handler
at /home/foo/git/publ/LibAFL/crates/libafl/src/executors/hooks/unix.rs:243:36
2: libafl::executors::hooks::unix::unix_signal_handler::::handle
3: libafl_bolts::os::unix_signals::handle_signal
at /home/foo/git/publ/LibAFL/crates/libafl_bolts/src/os/unix_signals.rs:446:17
4:
5: pthread_kill
6: gsignal
7: abort
8: _ZN11__sanitizer5AbortEv.cold
9: _ZN11__sanitizer3DieEv
10: _ZN6__asan19ScopedInErrorReportD2Ev
11: _ZN6__asan18ReportGenericErrorEmmmmbmjb.part.0
12: __asan_report_load1
13: _ZN4json12_GLOBAL__N_112parse_stringERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERm
at /home/foo/vosk-api/fuzz/./../src/json.h:522:0
_ZN4json12_GLOBAL__N_110parse_nextERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEERm
at /home/foo/vosk-api/fuzz/./../src/json.h:638:31
14: _ZN4json4JSON4LoadERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
at /home/foo/vosk-api/fuzz/./../src/json.h:652:12
LLVMFuzzerTestOneInput
at /home/foo/vosk-api/fuzz/harness.cc:18:3

```

### Crashing input

```
00000000: 2229 6178 5c72 745c 7276 5c72 5c72 605c ")ax\rt\rv\r\r`\
00000010: 7274 5c72 765c 725c 7274 7479 645c b874 rt\rv\r\rttyd\.t
00000020: 5c72
```

## Additional notes

So far the JSON implementation has only been tested in an isolated context. I will take a closer look at the overall usage, e.g. in `recgonizer.cc` next.

__NOTE__: The JSON parser is mainly used for validating the `grammar` object. Regardless, the current JSON implementation is easiliy crashable which can also be be mitigated with some sanitiy/bounds-check which in turn would make the overall application more robust.

Guide de contribution

Aucun guide de contribution indexé pour ce dépôt

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.