
Apache Spark 4.2.0 - 높음 - 원격 - 파일 쓰기 - History appStorePath의 App ID 미검증
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-history-store-path
분류: 파일 쓰기 (설정에 따라 제한됨) 도달 범위: 원격
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
HistoryServerDiskManager.appStorePath는 정제 없이 new File(appStoreDir, appId + extension)을 수행합니다. Utils.sanitizeDirName은 이벤트 로그 파일 이름에만 사용됩니다. JSON App ID는 asText()입니다. 리스팅은 event.appId.orNull을 저장합니다. Lease.commit은 그 문자열을 사용합니다. App ID ../는 spark.history.store.path 외부에 .rdb를 씁니다. 기본적으로 설정되지 않은 store.path는 인메모리입니다(쓰기 없음). 내용은 스토어 파일이며 RCE가 아닙니다.
| ID | 아직 CVE 없음 |
| 분류 | 파일 쓰기 (설정에 따라 제한되는 History 디스크 스토어) |
| 도달 범위 | 원격 (이벤트 로그 작성자와 spark.history.store.path가 설정된 History; UI ACL은 기본적으로 꺼짐) |
| CWE | CWE-22 |
| CVSS | 높음: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N (spark.history.store.path가 설정된 경우) |
| 제품 | Apache Spark History Server |
| 영향받는 버전 | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) |
| 인증 | spark.history.fs.logDirectory(기본값 file:/tmp/spark-events)의 작성자; History에 spark.history.store.path가 설정되어 있어야 함 |
| 라이선스 | GNU Affero GPL v3.0 |
| 랩 | 127.0.0.1:18990 전용. 인용된 조인의 가젯. store.path 외부의 증거 .rdb. |
CVE-2025-54920과 동일한 피해자 모델: History Server가 공격자가 쓸 수 있는 로그 디렉터리를 가리킴. Jackson은 더 이상 HiveConnection 클래스 가젯을 생성하지 않습니다. 디스크 스토어 경로는 여전히 이벤트 JSON의 App ID를 신뢰합니다.
| 방법 | 싱크 | 권한 | 랩 vs 추론 |
|---|---|---|---|
JSON App ID가 ../<name>(또는 ../../oracle/...)인 이벤트 로그를 심습니다. 스캔을 기다립니다. 앱 UI를 로드하는 GET / getAppUI / loadDiskStore / createDiskStore | HistoryServerDiskManager.appStorePath 이후 Lease.commit renameTo | 무결성: History UID로서 {store.path}/apps 외부에 .rdb(기본 RocksDB) 또는 .ldb(LevelDB) 경로를 생성합니다. 가젯에서 랩 입증: escaped=yes, 파일 /work/oracle/SPARK-HISTORY-STORE-PATH-WITNESS.rdb에 증거가 포함됨. 내용은 스토어 파일이며, 공격자가 선택한 호스트 바이트코드도, 셸도 아닙니다. | 랩 입증(이스케이프된 경로에 증거 바이트를 가젯이 기록). RocksDB 디렉터리의 전체 History renameTo는 동일한 조인입니다. |
| 이스케이프된 대상이 이미 디렉터리인 경우 동일한 App ID | Lease.commit if (dst.isDirectory()) deleteStore(dst) 이후 Utils.deleteRecursively | 가용성: 이름이 이미 백엔드 접미사로 끝나는 기존 디렉터리를 삭제한 다음, 임대된 스토어로 교체합니다. | 추론됨(HistoryServerDiskManager.scala 커밋 고정). 가젯은 파일을 썼고 deleteRecursively를 호출하지 않았습니다. |
기본적으로 설정되지 않은 spark.history.store.path | LOCAL_STORE_DIR createOptional; diskManager = storePath.map | 닫힘. 인메모리 리스팅. HistoryServerDiskManager 없음. 랩 입증 gated=yes written=no. | 랩 입증 |
App ID에 Utils.sanitizeDirName을 먼저 적용 | 이벤트 로그 파일 이름 헬퍼 | apps/ 내부에 머무름. /와 :는 -가 됨. 랩 입증 부정. | 랩 입증 |
store.path가 설정된 상태에서 App ID local-control 제어 | 동일한 조인 | {store.path}/apps/local-control.rdb를 씁니다. apps/ 내부에 머무름. | 랩 입증 |
인접한 닫힌 항목: 기본적으로 설정되지 않은 스토어(인메모리). Jackson sparkEventFromJson은 여전히 SparkListenerEvent를 요구합니다(CVE-2025-54920). URL getAppUI 폴백 eventlog_v2_ + nameForAppAndAttempt는 정제됩니다. 리스팅 KV 키는 파일 시스템 경로가 아닙니다. HybridStore / kvstore 매퍼에는 Id.CLASS가 없습니다. 컴팩션은 기본적으로 꺼짐. 클리너는 기본적으로 꺼짐. 열린 클러스터에서의 작업 제출(SECURITY.md FAQ). ML 모델 로드(docs/ml-security.md). 이 잔여물은 호스트 RCE가 아닙니다.
저는 master도, 4.3 RC도 아닌 Spark 4.2.0을 고정했습니다. 스물여덟 번의 추적. 공개된 History Jackson RCE는 이 껍질에서는 닫혀 있습니다. 이벤트 로그 작성자는 이미 Utils.sanitizeDirName을 호출하므로 로그 파일에는 /가 포함될 수 없습니다. 디스크 캐시 파일 이름은 원시 App ID를 사용합니다.
private[history] def appStorePath(appId: String, attemptId: Option[String]): File = {
val fileName = appId + attemptId.map("_" + _).getOrElse("") + extension
new File(appStoreDir, fileName)
}
appStoreDir는 {spark.history.store.path}/apps입니다. Unix new File(parent, "../../oracle/name.rdb")는 parent 외부로 해석됩니다. JsonProtocol.applicationStartFromJson은 json.get("App ID").asText()를 취합니다. AppListingListener.onApplicationStart는 event.appId.orNull을 저장합니다. lease.commit(appId, attempt.info.attemptId)가 그 문자열을 appStorePath에 공급합니다.
이미 기록된 잘못된 방향: 기본적으로 설정되지 않은 store.path를 쓰기로 취급(인메모리임); Unix new File(dir, "/abs")가 parent를 무시할 것이라 기대(그렇지 않음; 잔여물은 ..); 접미사가 .rdb이기 때문에 RCE라고 주장; 조인이 오라클일 때 전체 History Server를 부팅; sanitizeDirName이 이 경로를 커버한다고 취급.
python3 spark-history-store-path-Abraxas-Labs.py
또는
cd lab
./run.sh
127.0.0.1:18990에서 HistoryServerDiskManager.appStorePath의 가젯. 고정 발췌는 lab/pin/ 아래에 있습니다. 전체 History Server는 부팅되지 않습니다. 루프백에 바인딩하십시오.
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
new File 이전에 appStorePath 내부에서 appId와 attemptId에 Utils.sanitizeDirName을 실행하십시오(또는 .. / / / NUL을 거부하십시오). 조인 후 dst.getCanonicalFile()이 appStoreDir.getCanonicalFile() 아래에 머무르도록 요구하십시오. 설정되지 않은 spark.history.store.path는 안전한 기본값으로 유지됩니다.
32f7299601108917fb01920a54e084595b7b3bf8)HistoryServerDiskManager.scala appStorePath / Lease.commitFsHistoryProvider.scala diskManager / loadDiskStore / onApplicationStartJsonProtocol.scala applicationStartFromJsonUtils.scala sanitizeDirNameHistory.scala LOCAL_STORE_DIR (선택 사항)GNU Affero GPL v3.0. LICENSE를 참조하십시오. 루프백 랩 전용. 보증 없음.