
Apache Spark 4.2.0 - 높음 - 원격 - 파일 쓰기 - Connect 세션 SET copyFromLocalToFs.allowDestLocal
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-connect-copy-local
분석 문서: https://abraxaslabs.tech/research/spark-connect-copy-local
분류: 파일 쓰기 (SPARK-58531의 잔여물, 4.3.0에서만 static) 도달 범위: 원격
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
spark.sql.artifact.copyFromLocalToFs.allowDestLocal은 이 핀에서 여전히 buildConf(세션에서 설정 가능)입니다. SPARK-58531은 4.3.0에서 이를 StaticSQLConf로 옮겼습니다. Connect 세션은 테스트 전용 로컬 대상 게이트를 SET한 뒤, copyFromLocalToFs / AddArtifacts forward_to_fs/<abs>를 통해 Spark UID 권한으로 드라이버 로컬 경로를 덮어쓸 수 있습니다.
Connect 세션에서 SET한 뒤 forward_to_fs가 드라이버 로컬 파일을 씁니다. 대상은 반드시 LocalFileSystem이어야 합니다. RCE는 아닙니다.
| ID | 아직 CVE 없음 (SPARK-58531의 잔여물) |
| 분류 | 파일 쓰기 (SPARK-58531의 잔여물, 4.3.0에서만 static) |
| 도달 범위 | 원격 (Spark Connect Config + AddArtifacts) |
| CWE | CWE-22, CWE-732 |
| CVSS | 높음: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H |
| 제품 | Apache Spark (ASF) |
| 영향받는 버전 | Apache Spark 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8). 4.3.0은 static입니다. |
| 인증 | ConfigRequest.SET 및 AddArtifacts를 수행할 수 있는 Connect 클라이언트. 기본 Connect에는 PSK가 없습니다. |
| 라이선스 | GNU Affero GPL v3.0 |
| 랩 | 127.0.0.1:18920 전용 |
gRPC에 도달할 수 있는 Spark Connect 클라이언트(기본 바인드 포트 15002, 운영자가 spark.connect.authenticate.token을 설정하지 않는 한 PSK 없음)는 드라이버가 시작될 때 설정된 테스트 전용 게이트를 끄고, Spark UID 권한으로 드라이버의 선택된 로컬 경로를 덮어쓸 수 있습니다.
| 방법 | 싱크 | 권한 |
|---|---|---|
gRPC Config SET spark.sql.artifact.copyFromLocalToFs.allowDestLocal=true (또는 SQL SET) | SparkConnectConfigHandler.handleSet -> RuntimeConfig.set -> SQLConf.setConfString | 무결성 (세션 설정, 랩에서 입증): 세션별 로컬 대상 게이트를 뒤집습니다. 그 자체로는 파일을 쓰지 않습니다. 다른 세션은 변경하지 않습니다. 랩: SET HTTP 200, modifiable=True. |
gRPC AddArtifacts 이름 forward_to_fs + 절대 대상 경로, CRC 정상 | ArtifactManager.uploadArtifactToFs -> LocalFileSystem.copyFromLocalFile(delSrc=false, overwrite=true) | 무결성 (파일 덮어쓰기, 랩에서 입증): artifactRootDirectory/<sessionUUID>/ 밖의 드라이버 로컬 경로를 Spark UID 권한으로 쓰거나 덮어씁니다. 랩: 대상 /oracle/SPARK-CONNECT-COPY-LOCAL-WITNESS, uid 185, 내용 WITNESS. |
| 동일한 AddArtifacts를 HDFS/S3/ABFS 대상으로 수행 | 문서화된 copyFromLocalToFs | 무결성 (클러스터 FS, 설계상): 이 잔여물이 아닙니다. LocalFileSystem이 아닌 대상은 테스트 플래그를 참조하지 않습니다. |
랩에서 입증된 것 vs 추론된 것: buildConf 키의 세션 SET, 그리고 아티팩트 샌드박스 밖의 LocalFileSystem 대상에 uid 185로 쓰는 것은 랩에서 입증되었습니다. 프로세스가 쓸 수 있는 경로(드라이버 작업 디렉터리, 전역 쓰기 가능한 드롭 위치, 운영자가 나중에 실행하는 이미 실행 가능한 스크립트)에 대한 Spark UID 덮어쓰기는 추론된 것입니다. RIP 제어는 없습니다. 파일 쓰기를 RCE로 과장하지 마십시오.
요구 사항: Spark Connect가 수신 대기 중이어야 합니다. 세션 SQL conf를 SET하고 AddArtifacts를 수행할 수 있는 클라이언트가 필요합니다. 대상(dest)은 Hadoop LocalFileSystem이어야 합니다. 기본 Connect에는 PSK가 없으며, 운영자가 토큰을 설정한 경우에도 이 잔여 경로에는 해당 공유 Bearer가 필요합니다. 이는 로컬 대상 제어에 대한 우회이며, PSK 우회가 아닙니다.
| 표면 | 실험실 |
|---|---|
| SET 없이 복사 | 400 _LEGACY_ERROR_TEMP_3161, 대상 없음 |
기존 정적 spark.connect.copyFromLocalToFs.allowDestLocal을 SET | 400 CANNOT_MODIFY_STATIC_CONFIG |
아티팩트 이름 .. / 절대 스테이징(ArtifactUtils.concatenatePaths) | INVALID_ARTIFACT_PATH |
forward_to_fs가 아닌 아티팩트 | artifactRootDirectory/<sessionUUID>/ 아래에 유지됨 |
| 플래그 없는 HDFS/클라우드 대상 | 문서화된 API이며, 이 잔여 경로가 아님 |
개방형 Connect에서의 미인증 SQL / Python UDF / Scala UdfPacket | 벤더 FAQ 작업 제출; DROP |
| SPARK-52122 ML DefaultParamsReader | 이 핀에서 allowlist + safeMLClassLoader |
셸 없음. 힙 덤프 없음. Connect PSK 건너뛰기 없음. 대상은 여전히 LocalFileSystem이어야 합니다. 쓰기는 root가 아닌 Spark UID로 수행됩니다.
uploadArtifactToFs는 테스트 플래그가 켜져 있지 않으면 LocalFileSystem 대상을 거부하며, 그렇지 않으면 사용자가 드라이버의 임의 파일을 덮어쓴다는 명시적 주석이 달려 있습니다:
if (fs.isInstanceOf[LocalFileSystem]) {
val allowDestLocalConf =
session.sessionState.conf.getConf(SQLConf.ARTIFACT_COPY_FROM_LOCAL_TO_FS_ALLOW_DEST_LOCAL)
.getOrElse(
session.conf.get("spark.connect.copyFromLocalToFs.allowDestLocal").contains("true"))
if (!allowDestLocalConf) {
throw new SparkUnsupportedOperationException("_LEGACY_ERROR_TEMP_3161")
}
}
fs.copyFromLocalFile(false, true, new FSPath(localPath.toString), destFSPath)
3.5 키는 정적이며 기본값은 false입니다. 4.0 대체 항목은 선택 사항인 buildConf입니다:
val ARTIFACT_COPY_FROM_LOCAL_TO_FS_ALLOW_DEST_LOCAL =
buildConf("spark.sql.artifact.copyFromLocalToFs.allowDestLocal")
.internal()
...
.booleanConf
.createOptional
buildConf는 registerStaticConfigKey를 호출하지 않습니다. isModifiable은 containsConfigKey && !isStaticConfigKey입니다. RuntimeConfig.set은 정적 키만 거부합니다. 내부 키는 여전히 설정 가능합니다. SET 이후, getConf(ARTIFACT_...)는 Some(true)를 반환하고 정적 이전 conf로의 getOrElse 폴백은 건너뜁니다.
SPARK-58531 / PR 57730 "Convert spark.sql.artifact.copyFromLocalToFs.allowDestLocal to a static conf"는 2026-08-05에 해결되었으며, Fix Version은 4.3.0입니다. JIRA: 해당 conf는 "호출자가 드라이버의 로컬 파일시스템에서 임의의 경로에 쓸 수 있는지 여부를 제어하므로, 드라이버를 시작하는 주체가 설정하도록 의도된 것입니다." 4.2.0이 Affects로 나열되어 있습니다. 이 핀에는 여전히 런타임 conf가 있습니다.
이미 기록된 잘못된 접근: Connect SQL을 발견 사항으로 취급(FAQ); 이전 정적 키의 SET(CANNOT_MODIFY_STATIC_CONFIG); Unix 절대 dest가 부모를 무시한다고 주장(루트 아래에 결합됨); File 쓰기를 RCE로 과장; JIRA가 해결되었다는 이유로 4.3.0 RC를 고정.
cd lab
./run.sh
루프백 127.0.0.1:18920. session-conf 게이트의 가젯 및 LocalFileSystem dest 복사 (pin v4.2.0 32f72996). 서버 UID 185. no-SET 제어: 400 _LEGACY_ERROR_TEMP_3161, dest 없음. 정적 old 키의 SET 제어: CANNOT_MODIFY_STATIC_CONFIG. buildConf 키의 세션 SET 후 복사: dest /oracle/SPARK-CONNECT-COPY-LOCAL-WITNESS는 /opt/spark/work/artifacts/<sessionUUID>/ 외부, uid 185, 내용 WITNESS.
health pin=v4.2.0 commit=32f7299601108917fb01920a54e084595b7b3bf8
server uid=185 artifact_root=/opt/spark/work/artifacts/<sessionUUID>
control-no-set status=400 condition=_LEGACY_ERROR_TEMP_3161
control-no-set dest-absent=yes
control-static-set status=400 error_class=CANNOT_MODIFY_STATIC_CONFIG
session-set status=200 modifiable=True
copy-after-set status=200 dest=/oracle/SPARK-CONNECT-COPY-LOCAL-WITNESS
oracle dest=/oracle/SPARK-CONNECT-COPY-LOCAL-WITNESS uid=185 outside-sandbox=yes content=SPARK-CONNECT-COPY-LOCAL-WITNESS
SUCCESS SPARK-CONNECT-COPY-LOCAL SPARK-CONNECT-COPY-LOCAL-WITNESS
spark.sql.artifact.copyFromLocalToFs.allowDestLocal을 StaticSQLConf로 이동하여 세션에서 SET할 수 없도록 합니다. 이것이 4.3.0의 SPARK-58531 / PR 57730입니다. 운영자가 4.3.0을 실행할 수 있을 때까지는 토큰과 네트워크 ACL 없이 Connect를 노출하지 말고, 드라이버가 해당 플래그를 설정하지 않은 채로 시작되었다는 이유만으로 테스트 플래그가 꺼져 있다고 간주하지 마십시오.