신규 고객모든 신규 계정에 DNS와 CDN 무료 요금제를 제공합니다.계정 만들기

PHP2Shell(CVE-2026-109618/109619): 엣지에서 가상 패치로 대응한 방법

치명적인 PHP 보안 권고 두 건으로 OOM bailout과 오래된 전역 상태를 통해 인증 없이 RCE가 가능합니다. 버그의 동작 원리, Yeap WAF가 첫날부터 엣지에서 차단하는 것, 그리고 오리진에서 여전히 수정해야 할 사항을 설명합니다.

2026년 10월 21일, PHP 프로젝트는 Critical 등급의 보안 권고 두 건을 동시에 발표했습니다. GHSA-w8hw-w9fx-j79w(CVE-2026-109618)와 GHSA-6wmf-4rv5-p3rp(CVE-2026-109619)로, 두 건 모두 CVSS v4 점수가 9.2입니다. 각각 인증 없이 원격 코드 실행을 허용합니다. 이 두 취약점은 PHP2Shell이라는 이름으로 알려지게 되었습니다.

유지보수 중인 네 개 브랜치(PHP 8.2, 8.3, 8.4, 8.5)가 모두 영향을 받으며, PHP 팀은 mod_php가 기본 설정 그대로 악용 가능하다는 사실을 확인했습니다. WordPress, Laravel 등 PHP 애플리케이션을 운영하는 사이트라면 익명 POST 요청 한 번으로 서버가 장악될 수 있습니다.

권고가 발표된 당일, 저희는 공개된 공격 경로에 대한 가상 패치를 모든 Yeap CDN 엣지 노드에 배포했습니다. 이 글에서는 취약점의 동작 원리, 저희 규칙이 차단하는 것과 차단할 수 없는 것, 그리고 오리진에서 여전히 해야 할 조치를 설명합니다.

PHP2Shell 공격 체인: 초기화가 끝나지 않은 메모리를 신뢰된 상태로 남기는 OOM bailout, 그리고 요청 간에 use-after-free로 재사용되는 오래된 전역 상태
두 버그 모두 특정 확장 모듈이 아니라 Zend 엔진의 요청 수명 주기에 존재합니다.

취약점 개요

CVE-2026-109618: OOM bailout으로 인한 메모리 손상

PHP는 메모리가 부족해지면 longjmp()를 호출해 현재 요청을 중단합니다. 일부 코드에서는 요청 메모리가 완전히 초기화되기 전에 이미 참조 가능한 상태가 됩니다. 바로 그 순간에 메모리 할당이 실패하면 조기 종료로 인해 잘못된 메모리가 남게 되고, 이후 요청 종료 처리 과정에서 이를 읽어 분기 조건으로 사용합니다.

공격자는 잘못된 형식의 multipart/form-data 요청을 이용해 PHP의 일반적인 제한 범위 안에서도 OOM을 안정적으로 유발할 수 있습니다. 유효한 콘텐츠 길이가 없는 요청, 헤더가 엄청나게 많은 파트, max_file_uploads를 우회하는 빈 파일 필드, 깊게 중첩된 필드 이름 등이 그 예입니다. 공식 수정에서는 OOM 시의 longjmp()를 완전히 제거했습니다. 이제 PHP는 다음 안전 지점에서 정상적으로 종료하며, 메모리 사용량이 더 높은 임계값을 넘으면 즉시 _exit()를 호출합니다.

CVE-2026-109619: 요청 간에 남는 오래된 전역 상태

요청에 속한 전역 상태(오류, 세션, 객체, 확장 모듈 상태)가 해당 요청의 메모리가 해제된 뒤에도 워커 프로세스에 남아 있을 수 있습니다. 일부 코드는 다음 요청이 제대로 초기화되기 전에 이 상태를 읽기 때문에, 해제된 메모리를 가리키는 포인터를 따라가게 됩니다. 이것이 바로 use-after-free입니다.

  • 생산자(오래된 상태를 남기는 요청): 치명적 오류, 실행 시간 초과, require 실패, 메모리 부족, PHP 8.5의 fatal_error_backtraces.
  • 소비자(그 상태를 읽는 요청): 요청 시작 전에 적용되는 INI 진단, 그리고 session.upload_progress 처리(기본적으로 활성화됨).

생산자와 소비자가 같은 워커에 할당되면, 공격자는 힙을 조작해 use-after-free를 코드 실행으로 이어지게 할 수 있습니다. PHP 팀은 업로드 진행률 경로가 mod_php와 PHP-FPM 모두에서 악용 가능함을 검증했습니다.

영향받는 버전

브랜치영향 범위수정 버전
PHP 8.5< 8.5.128.5.12
PHP 8.4< 8.4.278.4.27
PHP 8.3< 8.3.368.3.36
PHP 8.2< 8.2.358.2.35

mod_php는 악용 가능한 것으로 확인되었습니다. 소스 코드 분석에 따르면 FPM, FrankenPHP, LSAPI도 영향을 받을 수 있습니다. 지원이 종료된(EOL) 버전에는 공식 패치가 제공되지 않으므로, 그 앞단에서의 방어가 더욱 중요합니다.

WAF로 차단할 수 있는 것

공개된 익스플로잇은 본질적으로 인증 없는 multipart POST 요청 한 번입니다. 업로드 진행률 핸들러에 도달하기 위한 PHP_SESSION_UPLOAD_PROGRESS 필드와 파일 업로드, 그리고 힙을 스프레이해 해제된 메모리를 다시 차지하기 위한 대량의 폼 필드를 담고 있습니다. 이러한 형태는 HTTP 계층에서 드러나므로, 요청이 PHP에 도달하기 전에 차단할 수 있습니다.

저희는 Yeap WAF의 OWASP CRS 엔진에 독립적인 가상 패치 규칙 세트를 추가했습니다. 이 익스플로잇이 의존하는 '업로드 진행률 트리거 + 힙 스프레이' 조합을 식별하여 PHP가 요청을 파싱하기 전에 403을 반환합니다.

  • 단독으로 차단합니다. 이상 점수에 의존하지 않으므로 사이트의 보호 강도나 점수 임계값의 영향을 받지 않습니다.
  • 고정된 시그니처가 아니라 행위를 봅니다. 필드 이름을 바꾸거나 페이로드 형태를 바꿔도 우회할 수 없습니다.
  • 정상적인 업로드에는 영향을 주지 않습니다. 진행률 표시줄이 있는 폼을 포함해 정상적인 업로드는 그대로 동작합니다.
  • 엣지에서 동작합니다. 오리진에 무언가를 설치할 필요도, 설정을 한 줄 바꿀 필요도 없습니다.
Yeap CDN 엣지 보호 계층: TLS 종료와 오리진 은닉, PHP2Shell 가상 패치, OWASP CRS 관리형 규칙, 속도 제한과 차단 목록
공격 시도는 엣지에서 멈추고, 정상 트래픽은 그대로 오리진으로 전달됩니다.

권고 발표부터 전체 노드 보호까지

타임라인: 10월 21일 권고 발표, 같은 날 23:34 규칙 검증 후 전체 엣지 노드 적용, 10월 22일 OWASP CRS v4.30.0 번들과 함께 배포

배포 전에 저희는 공격 트래픽의 여러 형태와 다양한 종류의 정상 업로드를 대상으로 규칙을 검증했습니다. 모든 공격 형태는 차단되었고 모든 정상 업로드는 통과했습니다. 이후 규칙은 플러그인으로 CRS 번들에 컴파일되어 10월 21일 23:34(UTC+8)에 전체 엣지 노드에 적용되었습니다. 10월 22일 새벽에는 번들 전체를 OWASP CRS v4.30.0으로 업그레이드했으며, 가상 패치도 함께 배포되었습니다.

이러한 속도는 Yeap WAF 업데이트 파이프라인 덕분입니다. 새 번들은 백그라운드에서 컴파일 및 검증된 뒤 카나리 노드로 배포되고, 그곳에서 로드 상태와 차단율을 모니터링합니다. 정상으로 판단된 번들만 전체에 배포됩니다. 엣지 노드는 규칙을 원자적으로 교체합니다. 새 규칙이 준비될 때까지 기존 규칙이 계속 동작하므로 보호 공백이 생기지 않으며, 클릭 한 번으로 직전의 정상 버전으로 롤백할 수도 있습니다.

WAF는 패치가 아닙니다: 여전히 해야 할 조치

분명히 말씀드리면, WAF가 막을 수 있는 것은 공격 중 HTTP 요청 본문에 실려 오는 부분뿐입니다. CVE-2026-109619에는 PHP 8.5의 fatal_error_backtraces나 시작 시 발생하는 INI 경고처럼 multipart와 전혀 관계없는 생산자와 소비자도 있으며, 해제와 사용을 각각 완전히 정상적으로 보이는 두 요청으로 나눌 수도 있습니다. 이런 경우는 PHP 엔진 내부에서만 수정할 수 있습니다. 또한 slow POST와 같은 타이밍 기반 변형은 콘텐츠를 검사하는 WAF로는 탐지할 수 없습니다.

엣지 규칙은 시간을 벌어 주는 1차 방어선으로 여기시고, 가능한 한 빨리 다음 조치를 진행하시기 바랍니다.

  1. PHP를 업그레이드합니다: 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 이상으로 올리십시오. 권고에 따르면 이것이 유일한 완전한 해결책입니다.
  2. 업그레이드 전에 php.ini를 강화합니다. 변경 후에는 모든 PHP 워커를 재시작하십시오.
    ; 업로드 진행률 표시줄을 사용하지 않는다면 비활성화
    ; (권고는 패치 후에도 비활성화 상태를 유지할 것을 권장)
    session.upload_progress.enabled = 0
    
    ; PHP 8.5: 확인된 RCE 생산자 제거
    fatal_error_backtraces = 0
    
    ; 본문 및 입력 제한을 보수적으로 설정
    post_max_size = 8M
    max_input_vars = 1000
    max_input_nesting_level = 16
    
  3. PHP-FPM에서는 pm.max_requests = 1을 고려합니다. 각 워커가 요청 한 번마다 재생성되어 요청 간 재사용이 끊어집니다. 성능 비용이 따르므로 단기적인 긴급 조치로 사용하십시오.
  4. 업로드가 필요 없는 곳에서는 multipart를 거부합니다. Yeap CDN 콘솔의 방화벽 → 사용자 지정 규칙에서 "메서드가 POST", "요청 헤더 Content-Type에 multipart/form-data 포함", 그리고 대상 경로를 조합해 차단 규칙을 만들고, 실제로 업로드를 받는 엔드포인트만 허용하십시오.
  5. POST 요청에 속도 제한을 적용합니다. 스캔과 반복적인 탐색 시도를 늦출 수 있습니다.

환경을 확인하려면 다음을 실행하십시오.

php -v
php -i | grep -E "session.upload_progress.enabled|fatal_error_backtraces"

CLI와 웹 서버는 서로 다른 php.ini 파일을 불러올 수 있습니다. 가장 확실한 방법은 사이트에 임시 phpinfo() 페이지를 만들어 확인하는 것입니다. 확인한 후에는 바로 삭제하십시오.

Yeap CDN과 WAF로 PHP 사이트 보호하기

PHP2Shell이 PHP의 마지막 치명적 버그는 아닐 것입니다. 권고가 공개된 시점부터 모든 서버에 패치가 적용되기까지는 항상 공백 기간이 있으며, 공격자는 대개 몇 시간 안에 스캔을 시작합니다. 사이트를 Yeap CDN 뒤에 두면 그 공백 기간을 저희가 엣지에서 지켜 드립니다.

  • 항상 최신 상태로 유지되는 관리형 규칙: SQL 인젝션, XSS, 원격 명령 실행, PHP 인젝션, 웹셸 등 14개 카테고리에 걸친 OWASP CRS v4 전체 규칙 세트를 제공합니다. 개별 규칙을 조정하는 대신 카테고리 단위로 켜고 끌 수 있으며, 업스트림의 새 릴리스는 자동으로 빌드되고 카나리 검증을 거쳐 배포됩니다.
  • 치명적 버그에 대한 가상 패치: 이번처럼 권고 발표 당일에 표적 규칙이 엣지에 적용되며, 오리진은 전혀 변경할 필요가 없습니다.
  • 오리진 은닉: 공격자가 도달할 수 있는 곳은 엣지뿐입니다. 오리진에 CDN 트래픽만 허용하면 직접 공격받을 수 있는 표면이 거의 사라집니다.
  • 사용자 지정 규칙과 속도 제한: IP, 국가, 경로, 헤더, 메서드를 조합해 트래픽을 차단하거나, 챌린지를 요구하거나, 속도를 제한할 수 있습니다.
  • 무료 HTTPS와 글로벌 가속: 인증서는 자동으로 발급 및 갱신되며, 여러 지역의 엣지 노드가 방문자와 가까운 곳에서 응답합니다.

도입은 몇 분이면 끝납니다. 도메인을 추가하고, 해당 DNS 레코드를 Yeap CDN이 제공하는 CNAME으로 지정한 뒤, 도메인의 WAF 설정에서 보호를 켜기만 하면 됩니다. 단계별 안내는 10분 만에 Yeap CDN과 무료 HTTPS를 웹사이트에 적용하기를, 제품 상세 정보는 Yeap CDN과 보안을 참고하십시오.

OWASP CRS 관리형 규칙 엔진은 도메인별로 순차적으로 활성화하고 있습니다. 이 PHP2Shell 가상 패치를 사이트에 즉시 적용하고 싶으시다면 콘솔에서 지원 티켓을 열어 주십시오. 해당 도메인에 바로 적용해 드립니다.

참고 자료

Yeap Cloud에서 서비스 구축

모든 신규 계정에 DNS와 CDN 무료 요금제를 제공합니다.

무료 회원가입