You need to agree to share your Hugging Face information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Log in or Sign Up to review the conditions and access this model content.

YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

Tribuo β€” Remote Code Execution via Unfiltered Java Deserialization on Model Load (LibLinearClassificationModel.deserializeFromProto)

Target: the Tribuo model format (huntr "Tribuo – Oracle") β€” the .tribuo protobuf model loader. Repository: github.com/oracle/tribuo (module tribuo-classification-liblinear). Version / pin: confirmed on org.tribuo:tribuo-classification-liblinear:4.3.2 (latest release, resolved from Maven Central) + tribuo-core/tribuo-json 4.3.2, liblinear-2.44, protobuf-java-3.25.6, temurin JDK 17. The sink is present in every published Tribuo LibLinear release; the fix exists only on unreleased main. Vulnerable file: Classification/LibLinear/src/main/java/org/tribuo/classification/liblinear/LibLinearClassificationModel.java (deserializeFromProto). CWE: CWE-502 (Deserialization of Untrusted Data) β†’ arbitrary code execution. CVSS 3.1: AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H = 8.8 (High) β€” a victim who loads an attacker-supplied .tribuo model gets RCE; 9.8 for a service that auto-loads models from an untrusted source (UI:N). Status: POC_EXECUTED β€” arbitrary code execution confirmed on the released loader (ran id).


1. Summary

A .tribuo model is a protobuf ModelProto. Tribuo migrated model serialization to protobuf specifically to replace its legacy Java-serialization path (see SECURITY.md, which deprecates the old java.io.Serializable model I/O). But the LibLinear model implementation is not protobuf-native: its inner de.bwaldvogel.liblinear.Model is embedded as raw Java-serialized bytes inside the LibLinearProto.models(0) field. When loading, LibLinearClassificationModel.deserializeFromProto unpacks those bytes with a raw new ObjectInputStream(...).readObject() and no ObjectInputFilter/allow-list. So the "safe" recommended protobuf load path (Model.deserializeFromFile / deserializeFromStream / deserialize) still performs unfiltered Java deserialization of attacker-controlled bytes β€” the classic RCE primitive. Merely loading an untrusted .tribuo model triggers code execution β€” exactly the huntr Model File Vulnerabilities threat model.

This is an incomplete-fix: the protobuf migration was the security remediation, yet it left the LibLinear embedded-object path unguarded. main now adds ois.setObjectInputFilter(LIBLINEAR_FILTER) at this exact method β€” an upstream acknowledgment that this is a bug, not by-design β€” but that fix is unreleased, so all Maven Central artifacts are affected.

2. Vulnerable code (LibLinearClassificationModel.java, tribuo-classification-liblinear @ 4.3.2)

deserializeFromProto reads the serialized liblinear model and does (confirmed in the released bytecode β€” see poc/evidence/sink_disassembly.txt, javap -c -p):

// LibLinearModelProto proto -> models(0) is a raw java-serialized de.bwaldvogel.liblinear.Model:
byte[] modelBytes = proto.getModels(0).toByteArray();      // attacker-controlled
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(modelBytes));
de.bwaldvogel.liblinear.Model m = (de.bwaldvogel.liblinear.Model) ois.readObject();  // <-- unfiltered readObject

The javap disassembly shows the opcode order verbatim: new ObjectInputStream β†’ invokevirtual ObjectInputStream.readObject() β†’ then checkcast de/bwaldvogel/liblinear/Model. ObjectInputFilter count in the whole class = 0. The cast happens after readObject() returns, so a gadget fires regardless of the declared type (the cast merely throws ClassCastException afterward β€” too late).

The sibling org.tribuo.regression.liblinear.LibLinearRegressionModel.deserializeFromProto (line 129) is structurally identical (same embedded-model pattern, same unfiltered readObject, shared tribuo-common-liblinear LibLinearModelProto handling) β€” and is independently confirmed exploitable (see Β§3). This is a shared-infrastructure defect, not a single-module bug.

3. Proof of Concept

poc/ contains a minimal, self-checking PoC (all run against the released Maven Central jars β€” see poc/cp.txt, which is 100 % repo1.maven.org):

  • TrainAndPatch.java β€” trains a real minimal LibLinearClassificationModel on toy data, serializes it to a valid .tribuo (orig.tribuo) via the public API, then surgically replaces the embedded models[0] serialized bytes with a serialized attacker object, producing patched.tribuo.
  • Evil.java β€” the attacker's serialized class; its readObject drops /tmp/tribuo_pwned and runs Runtime.exec("id") (stands in for a gadget payload).
  • Load.java / LoadVerbose.java β€” load patched.tribuo the way a victim would, via the documented public org.tribuo.Model.deserializeFromFile.
  • orig.tribuo (benign) and patched.tribuo (malicious) β€” the model files.

Observed result (poc/evidence/rce.txt):

[*] victim: Model.deserializeFromFile(patched.tribuo)
... LibLinearClassificationModel.deserializeFromProto(LibLinearClassificationModel.java:144)
Caused by: java.lang.ClassCastException: class Evil cannot be cast to class de.bwaldvogel.liblinear.Model
marker file /tmp/tribuo_pwned: CREATED
--- Runtime.exec(id) output captured by Evil.readObject: ---
uid=1000(wey) gid=1000(wey) groups=1000(wey),4(adm),20(dialout),24(cdrom),...

readObject() executed attacker code (id) on the standard public load path β€” RCE confirmed. The ClassCastException at deserializeFromProto:144 proves the code ran before the type check.

Weaponization without an attacker class (CONFIRMED, poc/upgrade/): no attacker class is needed. Tribuo's released transitive classpath (poc/cp.txt / poc/upgrade/stockcp.txt) bundles commons-beanutils-1.9.4, commons-collections-3.2.2, commons-collections4-4.4, commons-logging-1.2 β€” the full ysoserial CommonsBeanutils1 gadget chain. poc/upgrade/evil2.tribuo embeds a stock ysoserial CommonsBeanutils1 payload (no bespoke class); loading it via Model.deserializeFromFile with a classpath of only the 32 released Maven jars + a bare loader (verified: 0 Evil* classes, 0 ysoserial jars on the classpath) executed touch+id:

marker /tmp/tribuo_ysoserial_pwned: CREATED
uid=1000(wey) gid=1000(wey) groups=1000(wey),4(adm),...

(poc/evidence/rce_ysoserial_noplant.txt). This closes the "you'd need to plant a gadget class" objection β€” the RCE runs on stock Tribuo dependencies alone.

Honest caveat on the ysoserial variant: CommonsBeanutils1 is TemplatesImpl-based, so on JDK 16+ default modules the reflective defineClass is blocked β€” the no-plant payload above was run on JDK 17 with 5 --add-opens (see poc/evidence/). It is drop-in on JDK 8/11 (no opens needed), or any JVM already launched with those opens; a non-TemplatesImpl chain (plausible given commons-collections 3.2.2 is present) would remove even that condition but was not demonstrated. The core sink RCE (the marker Evil PoC in Β§3 above) executes on any JDK β€” it is a plain readObject on a Serializable class and does not depend on modules/reflection; the ysoserial variant only strengthens the "no attacker class" story.

Sibling confirmed executed: poc/upgrade/evil_reg.tribuo reproduces the identical RCE through LibLinearRegressionModel.deserializeFromProto (marker + id captured, poc/evidence/rce_regression_sibling.txt), proving the defect is shared across tribuo-common-liblinear consumers. Other liblinear-based Tribuo modules (e.g. anomaly detection) are likely-affected siblings by the same pattern.

4. Control test (negative test)

Loading a benign orig.tribuo (whose models[0] is a legitimate serialized de.bwaldvogel.liblinear.Model) loads with no code execution and no marker file β€” the RCE is attributable specifically to deserializing the attacker-controlled embedded bytes, not to normal model loading.

Remediation / Fix recommendation (5)

Install a strict ObjectInputFilter allow-list on the ObjectInputStream in LibLinearClassificationModel.deserializeFromProto (and the LibLinearRegressionModel sibling), permitting only de.bwaldvogel.liblinear.** (and required JDK primitives/arrays) and rejecting everything else β€” which is exactly what unreleased main now does (ois.setObjectInputFilter(LIBLINEAR_FILTER)). Ship it in a release. Better still, migrate the liblinear model to a genuine protobuf representation so no Java ObjectInputStream is on the load path at all.

6. Dedup / upstream status

Checked NVD/CVE, the GitHub Advisory Database (0 advisories for org.tribuo), and huntr (huntr.com/repos/oracle/tribuo β†’ no reports): no prior disclosure of this sink. The bug class (unfiltered readObject) is well-known, but no filed advisory covers Tribuo's LibLinear protobuf loader. The unreleased main fix corroborates that this is a real, acknowledged, still-unpatched-in-release bug. Not a duplicate.

7. Reproduction environment

Linux, JDK 17, internet (coursier/Maven resolves org.tribuo:tribuo-classification-liblinear:4.3.2 + tribuo-json:4.3.2). poc/repro.sh is self-contained. No GPU.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support