PyYAML의 CVE-2020-14343에 대한 통제된 취약점 연구 및 재현 랩
이 프로젝트는 통제되고 격리된 Docker 환경에서 CVE-2020-14343을 시연합니다.
이 실습에는 두 가지 환경이 포함되어 있습니다:
yaml.safe_load()를 사용하는 패치된 환경이 실습은 전체 취약점 생명주기를 시연합니다:
이 실습에서 사용되는 재현 페이로드는 의도적으로 무해하며 테스트 마커만 출력합니다.
| 속성 | 세부 정보 |
|---|
| CVE | CVE-2020-14343 |
| 제품 | PyYAML |
| 영향받는 버전 | 5.4 이전 버전 |
| 수정된 버전 | 5.4 |
| 취약점 유형 | 부적절한 입력 검증 (CWE-20) |
| 심각도 | 치명적 |
| 공격 표면 | 취약한 로딩 기능을 사용하여 처리되는 YAML 입력 |
이 취약점은 신뢰할 수 없는 YAML 입력이 취약한 PyYAML 로딩 기능을 사용하여 처리될 때 발생합니다.
5.4 이전의 PyYAML 버전은 다음과 같은 Python 전용 태그를 포함한 특수하게 조작된 YAML 태그가:
!!python/object/new
FullLoader가 사용될 때 객체 생성 기능에 도달하도록 허용했습니다.
따라서 악성 YAML 문서는 의도하지 않은 Python 코드 실행을 유발할 수 있습니다.
이 문제는 이전 PyYAML 취약점에 대한 불완전한 수정과 관련이 있었습니다.
PyYAML 5.4는 임의의 Python 태그를 UnsafeLoader로 이동시켜 CVE-2020-14343을 수정했습니다.
신뢰할 수 없는 YAML을 처리하는 애플리케이션의 경우, 이 실습에서는 안전한 로딩 방식으로 yaml.safe_load()도 사용합니다.
이 실습에는 두 개의 격리된 Docker 서비스가 포함되어 있습니다:
Host Machine
|
+------------+------------+
| |
v v
Vulnerable App Patched App
localhost:5000 localhost:5001
| |
PyYAML 5.3.1 PyYAML 5.4
FullLoader SafeLoader
| |
v v
Payload executes Payload rejected
| 서비스 | 호스트 포트 | 컨테이너 포트 | PyYAML | 목적 |
|---|---|---|---|---|
| vulnerable | 5000 | 5000 | 5.3.1 | 취약한 환경 |
| patched | 5001 | 5000 | 5.4 | 수정된 환경 |
cve-2020-14343-lab/
│
├── vulnerable/
│ ├── app.py
│ ├── Dockerfile
│ └── requirements.txt
│
├── patched/
│ ├── app.py
│ ├── Dockerfile
│ └── requirements.txt
│
├── exploit/
│ └── reproduce.py
│
├── detection/
│ └── detect.py
│
├── docker-compose.yml
└── README.md
| 파일/디렉터리 | 목적 |
|---|---|
vulnerable/app.py | 취약한 YAML 로딩을 사용하는 Flask 애플리케이션 |
vulnerable/Dockerfile | 취약한 Docker 이미지를 빌드합니다 |
vulnerable/requirements.txt | PyYAML을 5.3.1로 고정합니다 |
patched/app.py | 안전한 YAML 로딩을 사용하는 Flask 애플리케이션 |
patched/Dockerfile | 패치된 Docker 이미지를 빌드합니다 |
patched/requirements.txt | PyYAML을 5.4로 고정합니다 |
exploit/reproduce.py | 통제된 페이로드를 사용하여 취약점을 재현합니다 |
detection/detect.py | Docker 컨테이너 내부의 PyYAML 버전을 확인합니다 |
docker-compose.yml | 두 환경을 모두 빌드하고 실행합니다 |
다음 소프트웨어가 필요합니다:
실습을 시작하기 전에 Docker Desktop이 실행 중이어야 합니다.
취약한 애플리케이션은 Docker 포트 매핑을 통해 로컬 머신에서만 의도적으로 노출됩니다.
프로젝트 루트 디렉터리에서 다음을 실행합니다:
docker compose up --build -d
다음을 실행합니다:
docker compose ps
두 서비스 모두 Up으로 표시되어야 합니다.
다음을 엽니다:
http://127.0.0.1:5000
예상 응답:
{
"message": "CVE-2020-14343 vulnerable YAML parser"
}
다음을 엽니다:
http://127.0.0.1:5001
예상 응답:
{
"message": "CVE-2020-14343 patched YAML parser"
}
docker compose up -d
docker compose down
docker compose up --build -d
docker compose ps
docker logs cve-vulnerable
docker logs cve-patched
취약한 애플리케이션은 YAML 입력을 받는 /parse 엔드포인트를 노출합니다.
취약한 환경은 다음을 사용합니다:
yaml.FullLoader재현 스크립트는 다음 위치에 있습니다:
exploit/reproduce.py
취약한 애플리케이션은 다음에서 사용할 수 있습니다:
http://127.0.0.1:5000/parse
다음을 실행합니다:
python exploit/reproduce.py
이 스크립트는 Python 전용 YAML 태그를 포함하는 통제된 YAML 페이로드를 전송합니다.
취약한 환경에서 페이로드가 수락되고 애플리케이션이 HTTP 200 응답을 반환합니다.
통제된 테스트 마커:
CVE-2020-14343-TEST
가 취약한 애플리케이션 컨테이너 내부에서 실행됩니다.
실행은 다음을 사용하여 확인할 수 있습니다:
docker logs cve-vulnerable
예상 로그 출력에는 다음이 포함됩니다:
CVE-2020-14343-TEST
이는 취약한 YAML 로딩 동작을 통한 코드 실행을 보여줍니다.
재현 페이로드는 의도적으로 무해합니다. 테스트 마커만 출력하며 호스트 시스템을 수정하거나, 자격 증명에 접근하거나, 민감한 데이터에 접근하거나, 외부 시스템과 상호 작용하거나, 파괴적인 작업을 수행하지 않습니다.
탐지 스크립트는 다음 위치에 있습니다:
detection/detect.py
이 스크립트는 지정된 Docker 컨테이너 내부에 설치된 PyYAML 버전을 확인합니다.
수정된 버전은 다음과 같습니다:
5.4
다음을 실행합니다:
python detection/detect.py cve-vulnerable
예상 결과:
PyYAML installed version: 5.3.1
Fixed version: 5.4
STATUS: VULNERABLE
Reason: PyYAML version is older than 5.4.
다음을 실행합니다:
python detection/detect.py cve-patched
예상 결과:
PyYAML installed version: 5.4
Fixed version: 5.4
STATUS: PATCHED
Reason: PyYAML version is 5.4 or newer.
Docker Container
|
v
docker exec
|
v
Import PyYAML
|
v
Read yaml.__version__
|
v
Compare with 5.4
|
+----------------------+
| |
< 5.4 >= 5.4
| |
v v
VULNERABLE PATCHED
이 스크립트는 대상 컨테이너에서 설치된 PyYAML 버전을 조회합니다.
취약한 환경은 다음을 사용합니다:
PyYAML==5.3.1
패치된 환경은 다음을 사용합니다:
PyYAML==5.4
패치된 애플리케이션은 또한 다음 대신:
yaml.load(yaml_input, Loader=yaml.FullLoader)
다음을 사용합니다:
yaml.safe_load(yaml_input)
yaml.safe_load()를 사용합니다.동일한 익스플로잇 페이로드가 패치된 애플리케이션에 대해 테스트됩니다.
패치된 애플리케이션은 다음에서 실행됩니다:
http://127.0.0.1:5001/parse
다음을 실행합니다:
python exploit/reproduce.py http://127.0.0.1:5001/parse
이 실습에서 관찰된 결과:
HTTP Status: 400
Server Response:
{"message":"could not determine a constructor for the tag 'tag:yaml.org,2002:python/object/new:tuple' ...","status":"error"}
Python 전용 YAML 태그가 패치된 애플리케이션에 의해 거부됩니다.
통제된 테스트 마커는 패치된 애플리케이션에서 실행되지 않습니다.
| 테스트 | 취약한 환경 | 패치된 환경 |
|---|---|---|
| PyYAML 버전 | 5.3.1 | 5.4 |
| 탐지 | VULNERABLE | PATCHED |
| 익스플로잇 요청 | HTTP 200 | HTTP 400 |
| Python 전용 YAML 태그 | 수락됨 | 거부됨 |
| 테스트 마커 실행 | 컨테이너 로그에서 관찰됨 | 실행되지 않음 |
Docker Desktop이 실행 중인지 확인하고 다음을 검증합니다:
docker version
다음을 확인합니다:
docker ps -a
필요한 경우 이전 컨테이너를 제거합니다:
docker rm cve-vulnerable cve-patched
그런 다음:
docker compose up --build -d
docker compose ps
docker logs cve-vulnerable
docker logs cve-patched
docker compose up --build -d
깨끗한 재빌드를 위해서는:
docker compose build --no-cache
docker compose up -d
이 프로젝트는 통제된 개념 증명 실습이며 프로덕션 배포를 나타내지 않습니다.
제한 사항은 다음과 같습니다:
프로젝트 제출을 위해 다음 증거를 캡처해야 합니다:
STATUS: VULNERABLE을 보여주는 취약한 버전 탐지.CVE-2020-14343-TEST 마커.STATUS: PATCHED를 보여주는 패치된 버전 탐지.스크린샷은 다음 위치에 저장할 수 있습니다:
screenshots/
이 저장소의 모든 재현 코드와 문서는 이 실습을 위해 특별히 개발되었습니다.
이 실습은 식별부터 수정까지 CVE-2020-14343을 시연합니다.
취약한 환경은 PyYAML 5.3.1을 사용하며 조작된 YAML 페이로드를 통한 통제된 코드 실행을 시연합니다.
탐지 스크립트는 취약한 종속성 버전을 식별합니다.
수정된 환경은 PyYAML을 5.4로 업그레이드하고 신뢰할 수 없는 YAML을 처리하기 위해 yaml.safe_load()를 사용합니다.
그런 다음 동일한 재현 페이로드가 패치된 애플리케이션에 의해 거부되어 수정의 전후 증거를 제공합니다.
전체 환경은 Docker Compose를 사용하여 재현할 수 있습니다.