Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
spark-history-store-path — Apache Spark 4.2.0 - High - Remote - File write - History appStorePath unsanitized App ID | Kitploit
Tools/GitHubGitHub/abraxas/spark-history-store-path
Static AnalysisVulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationPenetration TestingLabs & Practice
GitHubabraxas/spark-history-store-path

spark-history-store-path

Apache Spark 4.2.0 - High - Remote - File write - History appStorePath unsanitized App ID

View Repository
2 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Abraxas Labs - spark-history-store-path

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  spark-history-store-path

spark-history-store-path

Class: File write (config-gated) Reach: Remote

Apache Spark 4.2.0 (32f72996) - Apache Software Foundation

HistoryServerDiskManager.appStorePath does new File(appStoreDir, appId + extension) with no sanitize. Utils.sanitizeDirName is only for event-log filenames. JSON App ID is asText(). Listing stores event.appId.orNull. Lease.commit uses that string. App ID ../ writes a .rdb outside spark.history.store.path. Default unset store.path is in-memory (no write). Contents are a store file, not RCE.

IDno CVE yet
ClassFile write (config-gated History disk store)
ReachRemote (event-log writer plus History with spark.history.store.path set; UI ACLs default off)
CWECWE-22
CVSSHigh: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N when spark.history.store.path is set
ProductApache Spark History Server
Affected4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8)
Authwriter of spark.history.fs.logDirectory (default file:/tmp/spark-events); History must have spark.history.store.path set
LicenseGNU Affero GPL v3.0
Lab127.0.0.1:18990 only. Gadget of the cited join. Witness .rdb outside store.path.

What an attacker can do

Same victim model as CVE-2025-54920: History Server pointed at a log directory the attacker can write. Jackson no longer constructs HiveConnection-class gadgets. The disk-store path still trusts the event-JSON App ID.

MethodSinkPrivilegeLab vs inferred
Plant an event log whose JSON App ID is ../<name> (or ../../oracle/...). Wait for scan. GET that loads the app UI / getAppUI / loadDiskStore / createDiskStoreHistoryServerDiskManager.appStorePath then Lease.commit renameToIntegrity: create a .rdb (default RocksDB) or .ldb (LevelDB) path outside {store.path}/apps as the History UID. Lab-proven on the gadget: escaped=yes, file /work/oracle/SPARK-HISTORY-STORE-PATH-WITNESS.rdb contains the witness. Contents are a store file, not attacker-chosen host bytecode, not a shell.Lab-proven (gadget write of witness bytes at the escaped path). Full History renameTo of a RocksDB directory is the same join.
Same App ID when the escaped destination already is a directoryLease.commit if (dst.isDirectory()) deleteStore(dst) then Utils.deleteRecursivelyAvailability: delete an existing directory whose name already ends in the backend suffix, then replace it with the leased store.Inferred (pin HistoryServerDiskManager.scala commit). Gadget wrote a file, did not call deleteRecursively.
Default unset spark.history.store.pathLOCAL_STORE_DIR createOptional; diskManager = storePath.mapClosed. In-memory listing. No HistoryServerDiskManager. Lab-proven gated=yes written=no.Lab-proven
Apply Utils.sanitizeDirName to the App ID firstevent-log filename helperStays inside apps/. / and : become -. Lab-proven negative.Lab-proven
Control App ID local-control with store.path setsame joinWrites {store.path}/apps/local-control.rdb. Stays inside apps/.Lab-proven

Adjacent closed: default unset store (in-memory). Jackson sparkEventFromJson still requires SparkListenerEvent (CVE-2025-54920). URL getAppUI fallback eventlog_v2_ + nameForAppAndAttempt is sanitized. Listing KV key is not a filesystem path. HybridStore / kvstore mapper has no Id.CLASS. History HTTP does not accept an upload path. Compaction default off. Cleaner default off. Job submit on an open cluster (SECURITY.md FAQ). ML model load (docs/ml-security.md). This leftover is not host RCE.

How I found it

I pinned Spark 4.2.0, not master, not a 4.3 RC. Twenty-eight hunts. The public History Jackson RCE is closed on this peel. Event-log writers already call Utils.sanitizeDirName so the log file cannot contain /. The disk-cache file name uses the raw App ID.

private[history] def appStorePath(appId: String, attemptId: Option[String]): File = {
  val fileName = appId + attemptId.map("_" + _).getOrElse("") + extension
  new File(appStoreDir, fileName)
}

appStoreDir is {spark.history.store.path}/apps. Unix new File(parent, "../../oracle/name.rdb") resolves outside parent. JsonProtocol.applicationStartFromJson takes json.get("App ID").asText(). AppListingListener.onApplicationStart stores event.appId.orNull. lease.commit(appId, attempt.info.attemptId) feeds that string to appStorePath.

Wrong turns already recorded: treating default unset store.path as a write (it is in-memory); expecting Unix new File(dir, "/abs") to ignore the parent (it does not; leftover is ..); claiming RCE because the suffix is .rdb; booting a full History Server when the join is the oracle; treating sanitizeDirName as covering this path.

Lab

python3 spark-history-store-path-Abraxas-Labs.py

or

cd lab
./run.sh

Gadget of HistoryServerDiskManager.appStorePath on 127.0.0.1:18990. Pin excerpts under lab/pin/. Full History Server is not booted. Bind it to loopback.

SUCCESS SPARK-HISTORY-STORE-PATH how=gadget escaped=yes gated=yes outside=/work/oracle/SPARK-HISTORY-STORE-PATH-WITNESS.rdb SPARK-HISTORY-STORE-PATH-WITNESS

The fix

Run Utils.sanitizeDirName (or reject .. / / / NUL) on appId and attemptId inside appStorePath before new File. After join, require dst.getCanonicalFile() stays under appStoreDir.getCanonicalFile(). Unset spark.history.store.path stays the safe default.

References

  • github.com/apache/spark tag v4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8)
  • HistoryServerDiskManager.scala appStorePath / Lease.commit
  • FsHistoryProvider.scala diskManager / loadDiskStore / onApplicationStart
  • JsonProtocol.scala applicationStartFromJson
  • Utils.scala sanitizeDirName
  • History.scala LOCAL_STORE_DIR (optional)
  • Closed Jackson walk: CVE-2025-54920 / SPARK-52381
  • CWE-22

License

GNU Affero GPL v3.0. See LICENSE. Loopback lab only. No warranty.

Download Tool