
CVE-2026-40176에 대한 차등 개념 증명으로, 악성 저장소 URL을 통해 Composer의 Perforce 드라이버에서 OS 명령 삽입을 보여주며, 영향을 받는 버전과 패치된 버전에 대한 자동 A/B 테스트를 포함합니다.
자체 포함된 OOP 스타일 PHP 개념 증명으로, Composer의 Perforce 저장소 드라이버에서 명령 주입 취약점을 시연하고 차등 검증합니다.
PoC는 동일한 악성 composer.json을 두 개의 Composer 바이너리(영향을 받는 **릴리스(2.9.5)**와 수정된 릴리스(2.9.6))에 대해 실행하여, 영향을 받는 버전에서만 실행되고 수정된 버전에서는 실행되지 않는 부작용(주입된 셸 명령으로 작성된 마커 파일)을 관찰함으로써 버그를 증명합니다.
⚠️ 인가된 보안 연구 및 방어 테스트 전용입니다. 책임 있는 사용을 참조하세요.
| CVE | CVE-2026-40176 |
| 구성 요소 | Composer — Perforce (perforce) 저장소/VCS 드라이버 |
| 클래스 | 공격자가 제어하는 저장소 URL을 통한 OS 명령 주입 |
| 공격 표면 | type: perforce의 조작된 repositories 항목을 포함한 composer.json |
| 영향 받는 버전 | Composer 2.9.5 |
| 수정된 버전 | Composer 2.9.6 |
| 트리거 | 악성 매니페스트에 대한 의존성 해결/업데이트 (composer update) |
| 영향 | Composer를 실행하는 머신에서 임의 명령 실행 |
| PoC 언어 | PHP (단일 파일, 외부 종속성 없음) |
Composer는 여러 버전 관리 시스템에서 패키지를 해결할 수 있습니다. Perforce의 경우 호스트, 포트 및 사용자/스트림을 인코딩하는 p4:// URL로 저장소가 식별됩니다. Composer의 Perforce 드라이버가 기본 p4 명령줄을 구축할 때, 공격자가 제어하는 URL에서 가져온 필드가 셸에 전달되기 전에 충분히 삭제되지 않습니다.
매니페스트 작성자는 저장소 URL을 완전히 제어할 수 있으므로, 피해자가 악성 composer.json에 대해 composer update/composer install을 실행하도록 할 수 있는 공격자(예: 감염된 의존성, 적대적인 저장소, 신뢰할 수 없는 프로젝트 파일을 처리하는 CI 작업)는 의도된 p4 호출을 벗어나 Composer 프로세스의 권한으로 임의의 OS 명령을 실행할 수 있습니다.
이는 URL/브랜치/스트림 값이 이스케이프 없이 셸 명령으로 전달되는 기존 Composer VCS 드라이버 인수 주입 문제와 같은 계열에 속합니다. Composer 2.9.6은 Perforce 드라이버를 강화하여 주입된 페이로드가 더 이상 실행되지 않도록 합니다.
여기서 시연된 동작에 대한 권위 있는 설명은 PoC 소스 자체(
CVE202640176Test.php)입니다. 업스트림 수정 사항에 대한 자세한 내용은 공식 권고 및 Comporter 변경 로그를 참조하십시오.
PoC는 단일 클래스 CVE202640176Test로, 제어된 A/B(차등) 실험을 수행합니다:
2.9.5) 및 수정된 (2.9.6) Composer 바이너리 모두에서 --version을 조회하고, 둘 중 하나라도 호출할 수 없으면 조기 종료합니다.repositories 섹션에 주입된 셸 페이로드를 포함하는 악성 p4:// URL이 있는 perforce 항목이 포함된 composer.json을 작성합니다.composer update를 실행합니다.finally 블록은 항상 프로젝트 디렉터리의 원래 composer.json을 복원합니다.PASS를 출력합니다.각 실행에 대해 validateRun()은 세 가지를 확인합니다:
| 검사 항목 | 증명하는 내용 |
|---|---|
| 마커 파일이 존재하고 실행 ID를 포함함 | 주입된 touch/echo 페이로드가 실제로 실행되었음 — 즉 명령 주입이 성공했음. |
Composer 출력에 p4가 언급됨 | Perforce 드라이버 코드 경로에 도달했음(페이로드가 관련 없는 단계가 아닌 올바른 구성 요소에 의해 처리됨). |
| 파싱된 Composer 버전이 예상과 일치함 | 올바른 바이너리(2.9.5 대 2.9.6)가 실행되었음. |
세 가지 모두 통과해야 실행이 "OK"입니다. 전체 테스트는 영향을 받는 실행이 OK이고 수정된 실행이 아닌 경우에만 통과합니다. 이는 실제 취약점이 나중에 패치되었음을 나타내는 정확한 신호입니다.
악의적인 저장소 URL은 writeComposerJson()에서 생성됩니다:
p4://127.0.0.1:1666:attacker_user;touch <marker> && echo '<runId>' > <marker>:client_test
분석:
p4://127.0.0.1:1666:attacker_user — 올바른 형식의 Perforce URL (호스트, 포트 1666, 사용자).;touch <marker> && echo '<runId>' > <marker> — 주입된 셸 명령입니다. 선행 ;는 의도된 p4 명령을 종료합니다. touch는 마커 파일을 생성하고 echo '<runId>' > <marker>는 고유한 실행 ID를 파일에 기록하여 PoC가 페이로드(관련 없는 프로세스가 아닌)가 파일을 생성했음을 확인할 수 있도록 합니다.:client_test — URL 파싱이 그럴듯하게 유지되도록 하는 후행 텍스트입니다.영향을 받는 드라이버에서는 셸 메타문자가 처리되어 마커 파일이 생성됩니다. 수정된 드라이버에서는 값이 적절하게 이스케이프/인용되므로 동일한 문자열이 비활성 데이터로 처리되어 마커가 나타나지 않습니다.
참고: PoC는 고유한 타임스탬프가 있는 실행 ID를 사용하고 격리된 임시 디렉터리 내부에 마커를 작성하므로 페이로드는 무해하고 자체 정리되며 파괴적이지 않습니다.
2.9.5 (영향 받는 버전)2.9.6 (수정된 버전)exec()는 cd … && php …를 실행합니다). Linux/macOS용으로 설계되었습니다.composer.json (시작 시 읽히고 각 임시 실행에 복사된 후 복원됨).일반적으로 라이브 Perforce 서버가 필요하지 않습니다. 취약점은 Composer가
p4명령줄을 구축하는 방식에 있으며, 주입된 페이로드는 실제p4연결 전/주변에서 실행됩니다. Composer는 Perforce 연결 오류를 기록할 수 있습니다. 이는 예상된 것이며 마커 파일 증명에 영향을 미치지 않습니다.
PoC를 작업 디렉터리에 복제/배치합니다.
PoC와 동일한 디렉터리에 composer.json을 제공합니다. 최소한의 것으로 충분합니다:
{
"name": "research/cve-2026-40176-poc",
"description": "CVE-2026-40176 차등 PoC용 기본 매니페스트",
"require": {}
}
두 개의 Composer 바이너리를 얻고 PoC가 예상하는 위치에 배치합니다(기본값은 다음과 같습니다):
/usr/local/bin/composer-2.9.5.phar # 영향을 받는 버전
/usr/local/bin/composer-2.9.6.phar # 수정된 버전
공식 아카이브에서 특정 Composer 릴리스를 다운로드할 수 있습니다. 예:
curl -Lo /usr/local/bin/composer-2.9.5.phar https://getcomposer.org/download/2.9.5/composer.phar
curl -Lo /usr/local/bin/composer-2.9.6.phar https://getcomposer.org/download/2.9.6/composer.phar
경로가 다른 경우
CVE202640176Test.php하단의 두 생성자 인수를 편집하십시오.
php CVE202640176Test.php
하네스는 두 Composer 버전을 차례로 실행하고 최종 판정을 출력합니다. 실행이 실패하더라도 원래 composer.json은 자동으로 복원됩니다(작업은 휘발성 임시 디렉터리에서 수행됨).
저장소는 정확한 환경을 복제하는 컨테이너화된 랩을 제공합니다: PHP CLI 런타임과 PoC가 예상하는 경로에 있는 두 개의 고정 Composer 릴리스, 런타임에 완전히 네트워크 격리됨.
docker compose run --rm poc
이 명령은 cve-2026-40176-lab:latest를 빌드하고(빌드 중 Composer 2.9.5 및 2.9.6을 다운로드하고 각 --version을 확인) 권한이 없고 외부 송신이 없는 컨테이너 내에서 차등 테스트를 실행합니다.
랩이 보장하는 사항:
poc 서비스는 internal 브리지 네트워크(호스트/인터넷 송신 없음)에서 실행되며, cap_drop: ALL 및 no-new-privileges가 설정됩니다. 주입 페이로드는 격리된 상태로 유지됩니다.빌드 인수를 통해 버전을 다시 고정할 수 있습니다(PoC의 두 생성자 경로와 동기화되어야 함):
docker compose build --build-arg COMPOSER_AFFECTED_VERSION=2.9.5 --build-arg COMPOSER_FIXED_VERSION=2.9.6
선택 사항 — 라이브 Perforce 서버. full-lab 프로필에서 p4d 서비스를 사용할 수 있습니다(docker compose --profile full-lab up). 마커 기반 증명에는 필요하지 않습니다. 라이브 p4:// 엔드포인트를 원하는 연구자를 위해 존재합니다. PoC 페이로드는 127.0.0.1:1666을 대상으로 하므로 별도의 p4d 컨테이너를 통한 라우팅은 PoC URL을 호스트 p4d로 지정해야 합니다.
정직한 결과: 실제 게시된 Composer
2.9.5및2.9.6에 대해 PoC는 현재 실행되지 않으며, 랩은INCONCLUSIVE / FAIL을 보고합니다.
영향을 받는 Composer (2.9.5)를 PoC의 악성 매니페스트에 대해 실행하면 Composer 내부에서 p4/셸 명령이 구축되기 전에 다음 오류가 발생합니다:
In PerforceDriver.php line 40:
[ErrorException]
Undefined array key "depot"
PerforceDriver::initialize()는 가장 먼저 $this->repoConfig['depot']를 읽지만, PoC의 저장소 항목은 type과 url만 제공합니다("depot" 키 없음). 드라이버가 이 시점에서 중단되므로 URL의 주입된 ;touch <marker> 페이로드에 도달하지 못하고 마커가 생성되지 않습니다. 네트워크 격리가 원인이 아닙니다 — 완전한 송신이 있는 경우에도 동일한 오류가 발생합니다.
이것이 의미하는 바:
INCONCLUSIVE 결과는 PoC 페이로드의 속성이며 환경의 속성이 아닙니다.depot 키(그리고 현실적으로 full-lab 프로필 아래의 라이브 p4d 엔드포인트)가 필요합니다. 페이로드를 해당 시점까지 정제하는 것은 "랩을 설정하는 것"을 넘어서는 익스플로잇 개발이며 의도적으로 여기서는 범위를 벗어납니다.아래 "예상 출력"은 PoC의 의도된/이상적인 결과이며 참조용으로 유지됩니다. 현재 페이로드가 실제 드라이버에 대해 생성하는 결과가 아닙니다.
성공적인 시연은 대략 다음과 같습니다(경로 및 ID는 다를 수 있음):
=== CVE-2026-40176 PoC started ===
- Composer 2.9.5 version: 2.9.5
- Composer 2.9.6 version: 2.9.6
Prepared temp dir: /tmp/cve20264176_5_20260610_142233
Written malicious composer.json to /tmp/cve20264176_5_20260610_142233
Running Composer in /tmp/cve20264176_5_20260610_142233…
- Parsed Composer version: 2.9.5
- Marker /tmp/cve20264176_5_.../poc_marker_5.txt created with expected ID.
- Output shows Perforce driver activity.
- Affected run exit code: 1
Prepared temp dir: /tmp/cve20264176_6_20260610_142233
Written malicious composer.json to /tmp/cve20264176_6_20260610_142233
Running Composer in /tmp/cve20264176_6_20260610_142233…
- Parsed Composer version: 2.9.6
✘ Marker file /tmp/cve20264176_6_.../poc_marker_6.txt not found.
- Output shows Perforce driver activity.
- Fixed run exit code: 1
=== CVE-2026-40176 PoC finished ===
=== TEST RESULT: PASS (affected succeeded, fixed failed) ===
0이 아닌 Composer 종료 코드는 정상입니다. composer update는 결국 (가짜) 패키지를 가져오지 못합니다. 증명은 마커 파일이지 Composer의 종료 상태가 아닙니다.
| 결과 | 의미 |
|---|---|
| PASS (영향 받는 버전 성공, 수정된 버전 실패) | 확인됨: 2.9.5는 주입된 명령을 실행했고, 2.9.6은 실행하지 않았습니다. 취약점과 수정 사항이 모두 재현되었습니다. |
| INCONCLUSIVE / FAIL | 하나 이상의 검사가 일치하지 않았습니다. 실행별 ✓/✗ 줄을 검사하십시오: 잘못된 바이너리 경로, 버전 불일치, 영향 받는 마커 누락(환경/이스케이프 차이), 또는 수정된 실행이 예기치 않게 마커를 생성함. |
결론이 나지 않은 결과의 일반적인 원인:
PerforceDriver.php:40에서 Undefined array key "depot"로 중단됨 — 저장소 구성에 depot 키가 없어 Composer가 p4 명령 구축 경로에 도달하지 않음. 이것이 현재 페이로드가 실제 2.9.5/2.9.6에 대해 수행하는 동작입니다(재현 상태 참조).2.9.5/2.9.6이 아닙니다.exec()가 샌드박싱/비활성화되었습니다.p4가 포함되지 않음(드라이버 경로에 도달하지 않음).CVE2026-40176/
├── CVE202640176Test.php # PoC: CVE202640176Test 클래스 + 진입점
├── composer.json # PoC가 런타임에 읽고 복원하는 기본 매니페스트
├── Dockerfile # 랩 이미지: PHP CLI + 고정 Composer 2.9.5 및 2.9.6
├── docker-compose.yml # `poc` 실행기 (+ full-lab 프로필 아래 선택적 `p4d`)
├── .dockerignore # 빌드 컨텍스트 정리
├── README.md # 이 파일
└── .gitignore # 로컬 에이전트/도구 상태 제외
전체 PoC는 하나의 파일입니다:
__construct() — 두 바이너리 경로를 저장하고, 타임스탬프가 있는 실행 ID를 생성하며, 원래 composer.json의 스냅샷을 만듭니다.run() — 사전 점검, 영향 받는 실행, 수정된 실행, 복원 및 판정을 조정합니다.prepareTempDir() — 실행당 격리된 작업 디렉터리를 만듭니다.writeComposerJson() — 주입된 p4:// URL로 악성 매니페스트를 만듭니다.runComposer() — composer update를 실행하고(인수는 escapeshellarg()로 이스케이프) 출력 및 종료 코드를 캡처합니다.validateRun() — 버전, 마커 파일 및 Perforce 드라이버 활동을 확인합니다.preflightVersion() — 주어진 바이너리에서 --version을 읽습니다.composer.json은 결과에 관계없이 finally 블록에서 복원됩니다.escapeshellarg()로 이스케이프합니다(PoC가 실수로 자체 exec() 호출에 주입되지 않도록). 취약점은 한 단계 더 깊은 곳, 즉 Composer 자체가 p4 명령을 구축하는 방식에 있으며, 이것이 바로 페이로드가 대상으로 삼는 부분입니다.touch/echo하므로 PoC를 반복적으로 실행해도 호스트에 부작용이 없습니다.exec("cd … && php …") 패턴과 ;/&& 페이로드는 Unix 계열 셸을 가정합니다. Windows는 현재 지원되지 않습니다.mkdir 경쟁 조건 / 권한. 임시 디렉터리는 모드 0777로 생성됩니다. 공유 환경에서 실행하는 경우 강화하십시오.Composer X.Y.Z를 구문 분석합니다. 특이한 Composer 배너는 일치를 방해할 수 있습니다.이 저장소는 CVE-2026-40176을 이해하고 방어하기 위해 존재합니다.
composer.json 파일(예: 타사 프로젝트 소스를 처리하는 CI 파이프라인)에 대해 composer install/update를 실행하지 마십시오.2.9.5 / 2.9.6 PHAR 가져오기)2.9.6 변경 로그(배포판의 보안 추적기 / GitHub Advisory Database에서 권위 있는 수정 세부 정보 확인)저자: Ikarolaborda · PoC 작성일: 2026-06-10.