Java native deserialization of `compiled.bin`/`cache.bin` (object-stream RCE)

Abierto
#4,968 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
18/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
java
Área
security

Línea de trabajo

Start in k-frontend/src/main/java/org/kframework/utils/BinaryLoader.java, where loadImpl calls readObject() on a raw ObjectInputStream with no ObjectInputFilter; the callers are DefinitionLoadingModule.java, KompileFrontEnd.java, DefinitionParsing.java and lsp/TextDocumentSyncHandler.java. Done would mean an allow-list filter guards loadImpl without breaking kompile and LSP cache loading, which needs maintainer agreement on the trust model first.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Summary

ObjectInputStream.readObject() on definition-directory artifacts; the cls.cast guard is after the fact. Reachable from -kompiled resolution, KRUN_KOMPILED_DIR, LSP workspace auto-discovery of cache.bin, and CI caches/tarballs of definition directories. Highest-severity item in the audit.

4 finding(s) (2 HIGH, 1 MEDIUM, 1 LOW) — line-anchor verified at commit 4a46d1231473.

Findings
Severity ID Location Summary
HIGH kfrontend-compile-001 k-frontend/src/main/java/org/kframework/utils/BinaryLoader.java:91 Unsafe Java deserialization in BinaryLoader (compiled.bin / cache.bin)
HIGH kfrontend-compile-002 k-frontend/src/main/java/org/kframework/lsp/TextDocumentSyncHandler.java:73 LSP auto-discovers and deserializes attacker-controlled cache.bin from the opened workspace
MEDIUM kfrontend-parser-001 k-frontend/src/main/java/org/kframework/utils/BinaryLoader.java:94 Java native deserialization of compiled.bin (untrusted object stream)
LOW kfrontend-parser-002 k-frontend/src/main/java/org/kframework/utils/BinaryLoader.java:64 Java native deserialization of parser caches
Details
kfrontend-compile-001 — Unsafe Java deserialization in BinaryLoader (`compiled.bin` / `cache.bin`) (k-frontend/src/main/java/org/kframework/utils/BinaryLoader.java:91)

Trigger (as reviewed): BinaryLoader.loadImpl creates a raw java.io.ObjectInputStream and calls readObject() with no ObjectInputFilter, no class allow-list and no resolveClass override. It is the single serialization helper for the toolchain and is reached from DefinitionLoadingModule.java:31 (loadOrDie(CompiledDefinition.class, resolveKompiled("compiled.bin"))), KompileFrontEnd.java:109 (write), DefinitionParsing.java:207 and TextDocumentSyncHandler.java:73 (parse cache). An attacker who places a crafted compiled.bin or cache.bin in a *-kompiled directory gets gadget execution when a victim runs kompile/kprove/krun/kast against that directory.

Impact (as reviewed): Arbitrary code execution in the K frontend JVM, i.e. full compromise of the user running kompile/kprove/krun or the language server. The object is cast to CompiledDefinition/Map only after readObject() returns, so any classpath gadget chain runs first. Root cause for both the kompile cache path and the LSP auto-load path.

Deep review: reachability=REACHABLE-FROM-UNTRUSTED; exploitability=EXPLOITABLE; red-team=RED-TEAM-SURVIVED.

Strongest counterargument found during red-teaming: No weaponizable gadget chain has been demonstrated for this exact classpath (commons-collections4 4.3 and Guava 32.1.3 defeat the classic chains), so the CRITICAL 'arbitrary code execution' claim rests on custom gadget development rather than a proven exploit; without a chain the confirmed impact is attacker-controlled object-graph allocation (DoS) plus class loading, and the LSP kompile path also requires the victim to run K tooling inside an attacker-supplied directory.

kfrontend-compile-002 — LSP auto-discovers and deserializes attacker-controlled `cache.bin` from the opened workspace (k-frontend/src/main/java/org/kframework/lsp/TextDocumentSyncHandler.java:73)

Trigger (as reviewed): findCacheFile() recursively walks the opened workspace, selects the shallowest file named cache.bin (Kompile.CACHE_FILE_NAME) and passes it to BinaryLoader.loadCache with no provenance or integrity validation. loadCaches() is re-invoked from KWorkspaceService.didChangeWatchedFiles (KWorkspaceService.java:29). Any repository that ships a cache.bin at its root or under *-kompiled/ is deserialized as soon as the K language server initializes.

Impact (as reviewed): Remote code execution on the developer workstation from merely opening a malicious K project in VS Code or another LSP client. This is the most likely exploitation vector for the deserialization primitive in kfrontend-compile-001 because the attack file arrives with the repository and needs no further user action.

Deep review: reachability=REACHABLE-FROM-UNTRUSTED; exploitability=EXPLOITABLE; red-team=RED-TEAM-SURVIVED.

Strongest counterargument found during red-teaming: The RCE impact is not established: the commons-collections4 4.3 unsafe-serialization guard blocks the classic transformer chain unless a system property that appears nowhere in the repo is set, JDK 17+ removes the standard JDK gadgets, and no alternative classpath gadget chain was demonstrated. The confirmed impact from this finding alone is deterministic DoS/crash plus the attacker-controlled Source path read, and the trigger is one diagnostic or definition request rather than plain initialization, with client behavior (workspaceFolders, auto-diagnostics, Workspace Trust) outside the audited checkout.

kfrontend-parser-001 — Java native deserialization of `compiled.bin` (untrusted object stream) (k-frontend/src/main/java/org/kframework/utils/BinaryLoader.java:94)

Trigger (as reviewed): loadOrDie (line 46) and loadCache (line 62) both funnel arbitrary files through loadImpl. DefinitionLoadingModule.koreDefinition loads compiled.bin from the resolved -kompiled directory, which is selected from --definition, KRUN_KOMPILED_DIR, or a directory scan. Any definition directory obtained from a tarball, build artifact, shared filesystem, or CI cache is attacker-influenced data, and ObjectInputStream.readObject() executes readObject/readResolve on every class in the stream before the cls.cast check runs.

Impact (as reviewed): Classic Java deserialization RCE: a crafted compiled.bin yields arbitrary code execution with the privileges of kompile/krun/kprove, or at minimum memory exhaustion via deeply nested object graphs. The cls.cast(obj) guard is after-the-fact and provides no protection.

Deep review: reachability=REACHABLE-FROM-UNTRUSTED; exploitability=EXPLOITABLE; red-team=RED-TEAM-SURVIVED.

Strongest counterargument found during red-teaming: The RCE half of the CRITICAL rating is unproven: commons-collections4 4.3 hardens its transformer chain, and no other confirmed ysoserial-style gadget was identified on the frontend classpath, so in practice the demonstrated impact may be availability-only (StackOverflowError/OutOfMemoryError) rather than code execution. Separately, the primary path requires a victim to load an attacker-controlled definition directory, which is a real but user-mediated trust boundary; however, the LSP workspace scan and the default cwd '*-kompiled' scan make that boundary easy to cross without an explicit flag.

kfrontend-parser-002 — Java native deserialization of parser caches (k-frontend/src/main/java/org/kframework/utils/BinaryLoader.java:64)

Trigger (as reviewed): loadCache is a second independent entry point into loadImpl. It is intentionally tolerant: any IOException/ClassNotFoundException is downgraded to a warning and null is returned, so a malformed or hostile cache file never stops the pipeline. Caches live under the kompiled/temp directory and are frequently persisted across CI runs.

Impact (as reviewed): Same deserialization RCE primitive as kfrontend-parser-001, reachable through cache files that are more likely to be treated as disposable and less carefully controlled. The error handling guarantees a partially-consumed hostile stream never aborts the load.

Deep review: reachability=REACHABLE-FROM-UNTRUSTED; exploitability=EXPLOITABLE; red-team=RED-TEAM-SURVIVED.

Strongest counterargument found during red-teaming: The finding's HIGH severity rests on RCE, but no usable gadget chain was demonstrated for this exact classpath (JDK 17, Guava 32.1.3, commons-collections4 4.3, Scala 2.13); and the LSP vector depends on an external IDE client sending workspaceFolders and auto-issuing diagnostics or a user invoking goto-definition, plus any host workspace-trust gating, none of which is verifiable in this checkout. The 'frequently persisted across CI runs' premise and the 'kompiled/temp' wording are also inaccurate for this repository.

Suggested fix

Add an ObjectInputFilter allow-list (or replace Java serialization with a safe format) in BinaryLoader.loadImpl, and document that a kompiled directory is executable-trusted input.

Related

Intra-repo sibling references for lines in this bundle: kfrontend-parser-001.


From a static audit of this repository at commit 4a46d1231473b599c699160132fd6e76a5c46406 (master). Every location above was verified against the source at that revision; no exploit code was run against this repository or its infrastructure.

Lenguaje dominante
Python
Estrellas
605
Forks
165
Métricas de merge de PR
Sin PR fusionados en 30 d

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de runtimeverification/k

Todos los issues de runtimeverification/k

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.