PyYAML의 CVE-2020-14343에 대한 통제된 취약점 연구 및 재현 랩
이 프로젝트는 통제되고 격리된 Docker 환경에서 CVE-2020-14343을 시연합니다.
이 실습에는 두 가지 환경이 포함되어 있습니다:
yaml.safe_load()를 사용하는 패치된 환경이 실습은 전체 취약점 생명주기를 시연합니다:
이 실습에서 사용되는 재현 페이로드는 의도적으로 무해하며 테스트 마커만 출력합니다.
| 속성 | 세부 정보 |
|---|---|
| CVE | CVE-2020-14343 |
| 제품 | PyYAML |
| 영향을 받는 버전 | 5.4 이전 버전 |
| 수정된 버전 | 5.4 |
| 취약점 유형 | 부적절한 입력 검증 (CWE-20) |
| 심각도 | 치명적 |
| 공격 표면 | 취약한 로딩 기능을 사용하여 처리되는 YAML 입력 |
| CVSS | 9.3 (CVSS v4, GitHub Advisory) |
이 취약점은 신뢰할 수 없는 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
├── screenshots/
│ ├── 01-docker-compose-running.png
│ ├── 02-vulnerable-detection.png
│ ├── 03-patched-detection.png
│ ├── 04a-vulnerable-request.png
│ ├── 04b-vulnerable-execution-log.png
│ └── 05-patched-exploit-blocked.png
├── blog/
│ └── CVE-2020-14343-Technical-Blog.pdf
├── docker-compose.yml
├── README.md
└── .gitignore
| 파일/디렉터리 | 용도 |
|---|---|
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 | 두 환경을 모두 빌드하고 실행함 |
screenshots/ | 실습 중 캡처한 증거 자료를 포함함 |
blog/ | 800~1200단어 분량의 기술 블로그 PDF를 포함함 |
다음 소프트웨어가 필요합니다:
실습을 시작하기 전에 Docker Desktop이 실행 중이어야 합니다.
취약한 애플리케이션은 의도적으로 Docker 포트 매핑을 통해 로컬 머신에서만 노출됩니다.
프로젝트 루트 디렉터리에서 다음을 실행하십시오:
docker compose up --build -d
다음을 실행하세요:
docker compose ps
두 서비스 모두 Up으로 표시되어야 합니다.
다음을 엽니다:
http://127.0.0.1:5000
No input content was provided in your message. Please send the Markdown chunk you would like translated into Korean.
{
"message": "CVE-2020-14343 vulnerable YAML parser"
}
열기:
http://127.0.0.1:5001
No input content was provided in your message. Please send the Markdown chunk you would like translated into Korean.
{
"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.safe_load(yaml_input)
대신:
yaml.load(yaml_input, Loader=yaml.FullLoader)
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