PHP2Shell: Your Code Was Fine, The Engine Was Not
PHP2Shell: 코드가 아니라 PHP 엔진이 문제였습니다
PHP 엔진의 요청 초기화와 정리 과정에서 메모리 오류를 일으키는 취약점 두 건이 공개됐습니다. 특정 구성에서 인증 없이 원격 코드 실행으로 이어질 수 있으며, 글은 패치 버전과 워커 재시작 등 대응 방법을 정리합니다.
- 주제
에디터 노트
이 취약점의 실무상 쟁점은 애플리케이션 입력 검증보다 엔진의 실패 처리와 실제 운영 환경에 있습니다. 글은 요청 초기화 실패 때의 정리 동작과 워커에 남은 포인터라는 두 경로를 구분하고, mod_php와 PHP-FPM의 차이도 제시합니다. 따라서 패치 버전으로 올린 뒤 워커를 재시작하거나 reload해야 한다는 조치는 본문에서 설명한 문제와 직접 연결됩니다. 다만 공개 PoC의 성공 여부는 운영체제와 PHP 버전, 서버 구성에 따라 다르고, 실제 침해 여부도 watchTowr와 Rapid7의 관측이 엇갈립니다. 글은 이 차이를 숨기지 않지만, 취약점 세부와 대응 수치는 PHP Foundation 및 보안업체 분석을 전달한 내용으로 읽어야 합니다. 댓글은 엔진 결함이 애플리케이션의 안전한 코드를 무력화할 수 있다는 점과 설명의 이해하기 쉬운 점에 반응했으며, 기술적 반론이나 독립 검증은 제시하지 않습니다. 본문 끝에는 저자의 LiveReview 홍보가 이어지지만, 앞부분의 PHP 취약점 분석과 패치 안내는 별도로 읽을 수 있습니다.
AI 요약
PHP 애플리케이션 코드가 입력 검증과 prepared statement를 제대로 적용해도 런타임 엔진의 결함은 막지 못합니다. 글은 2026년 10월 21일 공개된 CVE-2026-109618과 CVE-2026-109619를 다룹니다. 두 취약점 모두 요청 처리 중 메모리 상태가 잘못됐을 때 정리 코드가 그 상태를 그대로 신뢰하는 문제입니다. CVSS 4.0 점수는 각각 9.2이며, 웹 워커 권한으로 원격 코드 실행(RCE)이 가능하다고 설명합니다.
요청 초기화 실패와 해제되지 않은 포인터
PHP는 요청마다 상태를 새로 만들고 요청 종료 시 정리합니다. CVE-2026-109618은 요청 초기화 도중 메모리 할당에 실패했을 때 문제가 생깁니다. 상태가 완성되기 전에 요청을 중단해도 종료 코드가 초기화되지 않은 포인터를 따라가거나 할당되지 않은 메모리를 해제할 수 있습니다. Rapid7은 조작한 multipart 요청으로 메모리 부족 상태를 일으킬 수 있다고 분석했습니다. Apache의 mod_php에서는 초기화가 실패해도 정리 절차가 실행되는 경로가 확인됐습니다. PHP-FPM은 초기화 실패 시 워커를 종료하므로 이 취약점의 해당 공격 흐름을 막습니다.
CVE-2026-109619는 요청 사이에 남은 상태를 악용하는 use-after-free 문제입니다. 치명적 오류나 타임아웃으로 정리가 중단되면 메모리는 해제되지만 포인터가 남을 수 있습니다. 같은 워커가 다음 요청을 처리할 때 초기화가 포인터를 지우기 전에 이전 메모리를 읽으면 문제가 됩니다. 글은 PHP 8.5에서 기본 활성화된 fatal error backtrace 기능을 이용하는 mod_php 경로와, 먼저 애플리케이션 동작으로 상태를 남겨야 하는 PHP-FPM의 session upload progress 경로를 소개합니다.
영향 범위와 패치
글은 두 취약점이 모든 PHP 서버에서 똑같이 작동한다는 식의 표현은 과장이라고 선을 긋습니다. Rapid7이 살펴본 공개 PoC는 운영체제와 PHP 버전, 웹 서버 조합에 따라 성공 여부가 달랐고, 공격 복잡도도 높게 평가됐습니다. 반면 공개된 PoC가 불안정하다는 사실이 위험을 없애지는 않습니다. watchTowr는 허니팟에서 실제 악용을 봤다고 밝혔고, Rapid7은 공격 시도는 관측했지만 실제 침해는 확인하지 못했다고 전합니다. Unit 42가 인용한 인터넷 노출 인스턴스 2,200만 건 이상은 업체 텔레메트리 수치라고 덧붙입니다.
수정 버전은 PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35입니다. 8.1 이하 사용자는 배포판이나 LTS 공급업체의 백포트를 확인하거나 업그레이드해야 합니다. 패키지 업데이트만으로 실행 중인 워커가 바뀌지는 않으므로 PHP-FPM을 reload하거나 Apache를 graceful reload해야 합니다. 컨테이너도 새 PHP가 들어간 이미지로 다시 빌드하고 배포해야 합니다.
패치 전 임시 완화책으로 session.upload_progress.enabled=0, PHP 8.5의 fatal_error_backtraces=0, post_max_size=8M와 프런트엔드 multipart 제한이 제시됩니다. memory_limit=-1도 권고에 등장하지만, Rapid7 분석에 따르면 취약한 할당 실패 경로를 완전히 차단하지 않습니다. RLIMIT_AS와 RLIMIT_DATA는 해당 경로를 유발할 수 있고, 컨테이너나 cgroup 메모리 제한도 충분한 해결책이 아니며 워커 풀 전체를 종료시킬 수 있다고 설명합니다.
PHP Foundation의 대응과 AI 관련 주장
글에 따르면 OOM 문제의 징후는 2024년 Quarkslab 외부 감사 과정에서 발견됐지만, 호환성 저하와 서비스 거부 위험, 확장 모듈 영향 등을 둘러싼 패치 논의가 지연됐습니다. PHP Foundation은 이를 보안팀의 의사결정과 자원 부족에 따른 구조적 실패라고 표현했고, 다수결 결정과 보안팀 확충 등 절차 변경을 추진한다고 밝혔습니다. 글은 AI가 취약점 공개 뒤 대상 탐색과 공격 시도에 드는 비용을 낮출 수 있다는 주장을 소개하면서도, 특정 환경에서 안정적으로 메모리 손상을 악용하는 일은 여전히 어렵다고 구분합니다. PHP Foundation은 보안 작업에 AI 보조 스캔과 검증을 활용하되 최종 판단과 패치는 사람이 맡는다고 밝혔습니다.
dev.to 반응
- @ivannovazzi — 잠깐, 코드를 완벽하게 안전하게 써도 엔진 전체가 고장 날 수 있다는 건가요? 정말 놀라운 실패 방식이네요.
- @annavi11arrea1 — 글을 써줘서 고맙습니다! 절반 정도 이해했어요. 그래도 요지는 알겠습니다. 누구나 이해할 수 있게 설명하려고 시간을 들여줘서 고마워요. 어제 이메일이 계속 왔는데 인터넷 연결은 좋지 않았거든요. 궁금했는데 대략적으로나마 이해할 수 있어 좋았습니다. 공유해줘서 고마워요.