안녕하세요. 이 글은 2026년 10월에 공개된 PHP 취약점 2개(PHP2Shell, CVE-2026-109619 / CVE-2026-109618)를 제가 이해한 내용으로 간단히 정리했습니다. 영상으로도 만나볼 수 있습니다 https://youtu.be/lDlTGWcJMZE
취약점 개요
취약점 2개는 모두 공격자가 원하는 코드를 피해자 서버에서 실행하는 RCE(Remote Code Execution, 원격 코드 실행) 취약점입니다. 취약점이 WordPress 같은 애플리케이션 코드가 아니라 PHP 자체에 있어서, 애플리케이션 코드에 문제가 없어도 PHP 버전이 낮으면 영향을 받습니다. 인증 없이 조작한 HTTP 요청만으로 공격할 수 있고, CVSS v4 점수는 둘 다 9.2(Critical)입니다.
- CVE-2026-109618: https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w
- CVE-2026-109619: https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp
영향 대상
PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 미만 버전이 영향 대상입니다. 8.1 이하(PHP 7, PHP 5 포함)는 보안 지원이 끝나 패치가 나오지 않으므로 그대로 영향 대상으로 봐야 합니다.
취약점 원리
취약점 2개는 PHP 메모리 관리 결함을 이용합니다.
CVE-2026-109619
사용자 요청을 처리하는 PHP worker 프로세스는 요청마다 임시 메모리를 만들고 요청이 끝나면 해제합니다. 그런데 PHP worker는 프로세스 1개가 요청 여러 개를 차례로 처리하기 때문에, 요청과 상관없이 프로세스가 살아 있는 동안 유지되는 글로벌 변수도 있습니다. 이 변수에는 임시 메모리 위치(포인터)가 저장됩니다.
정상이라면 요청이 끝날 때 임시 메모리를 해제하면서 글로벌 변수의 포인터도 함께 지웁니다. CVE-2026-109619는 이 사이에 OOM을 일으켜 포인터를 지우는 코드만 건너뛰게 만듭니다. 임시 메모리는 해제되는데 글로벌 변수는 이미 해제된 메모리를 계속 가리키는 상태가 됩니다. 공격자가 같은 worker에 두 번째 요청을 보내면 PHP는 글로벌 변수에 남은 포인터를 믿고 해제된 메모리를 읽습니다. 해제된 메모리를 다시 쓰는 공격이라서 use-after-free 취약점으로 분류됩니다. 확인된 공격 경로는 기본값이 켜져 있는 세션 업로드 진행률(session.upload_progress) 기능이고, PHP 8.5에서는 fatal error backtrace 기능도 공격 경로입니다.


CVE-2026-109618
CVE-2026-109618은 메모리 초기화 과정을 악용합니다.
PHP는 애플리케이션 코드가 실행되기 전에 multipart 요청을 파싱해 $_POST, $_FILES 값을 내부 테이블에 넣습니다. 이때 PHP는 테이블에 인덱스를 늘려 놓고 그다음에 내용을 채우는데, 자리를 늘린 직후 OOM이 발생하면 내용을 채우는 코드를 건너뜁니다. 테이블에는 메모리가 할당된것을 인식되지만 실제 그 메모리 영역에는 쓰레기 값으로 있습니다.
요청이 끝나면 PHP는 이 테이블을 해제하기 위해 하나씩 조회하는 로직이 있습니다. 이 때 메모리의 쓰레기 값을 정상 포인터로 착각해 그 주소의 메모리를 정리하려다 메모리를 손상시킵니다. 공격자는 쓰레기 값이 들어가는 위치에 자기 데이터를 넣어 코드를 실행합니다.


조치 방법
영구 조치는 PHP를 패치된 버전으로 업그레이드하는 방법이고, 공식 공지도 업그레이드만 완전한 조치라고 안내합니다. 바로 업그레이드하기 어려우면 PHP 설정을 바꿔 공격 경로를 줄이는 임시 조치를 적용합니다. 임시 조치는 공격 경로 일부만 줄이므로 패치를 대신하지 못합니다.
임시 조치:
- session.upload_progress.enabled = 0 (세션 업로드 진행률 기능 끄기, CVE-2026-109619 대응)
- memory_limit = -1 (OOM 자체가 발생하지 않게 설정, CVE-2026-109618 대응. 프로그램이 바꿀 수 없도록 관리자 설정으로 지정, 예: PHP-FPM pool의 php_admin_value)
- post_max_size 낮게 설정 (업로드가 필요 없으면 8M, 필요하면 32M 수준), upload_max_filesize는 post_max_size보다 작게 유지
- PHP 8.5 사용 시 fatal_error_backtraces = 0 추가 (CVE-2026-109619 대응)
설정을 바꾼 뒤에는 PHP worker를 모두 재시작해야 적용됩니다.
참고자료
'전공영역 공부 기록' 카테고리의 다른 글
| CloudNet MLOps 스터디 1주차 (2) | 2026.10.25 |
|---|---|
| production LLM에 인터넷 검색을 붙일 때 유료 search provider를 써야 하는 이유 (0) | 2026.10.18 |
| ECS 배포 파이프라인에서 고민한 것 (0) | 2026.10.05 |
| AI Gateway 토큰 집계가 Model provider과 다른 이유 (0) | 2026.09.20 |
| kubernetes sidecar가 왜 initContainers로 구현가능하게 했을까? Kubernetes native sidecar 간단 정리 (0) | 2026.09.13 |