주의: 명령줄(php -v, php -i)로 확인하면 안 됩니다
명령줄의 PHP는 웹 서비스와 다른 설정 파일을 읽습니다. 명령줄에서는 조치가 된 것처럼 보여도 실제 웹에는 적용되지 않았을 수 있고, 그러면 조치했다고 착각한 채 계속 취약한 상태로 남게 됩니다.
PHP 엔진 자체의 취약점으로, 인증 없이 서버에서 원격 코드를 실행하거나 프로세스 메모리를 읽어낼 수 있습니다. 애플리케이션 코드와 관계없이 PHP 버전만으로 영향을 받습니다. 공식 공개 전에 PoC 시연 영상과 패치가 적용된 바이너리가 먼저 유출됐고, 이를 바탕으로 한 PoC가 빠르게 퍼지고 있습니다. Xint 팀은 공격 코드를 완성하는 과정에서, 다른 공격자들도 동일한 공격 코드를 만들어 내는 것은 시간 문제라고 판단하고 있으니 지금 바로 패치하셔야 합니다.
항목 | 내용 |
CVE | CVE-2026-109618, CVE-2026-109619 |
심각도 | CVSS v4.0 9.2 (Critical) |
수정 버전 | 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 (2026-10-21 23:00 KST 배포) |
영향 버전 | 수정 버전 이하 및 PHP 8.1 이하 (7.x, 5.x 포함) |
PHP 8.1 이하 | 공식 릴리스 없음. 사용 중인 배포판(Ubuntu, Debian, RHEL 등)이나 LTS 지원 업체의 보안 업데이트를 확인하세요. |
완화책 | PHP 공식 입장은 "완전한 완화책(mitigation) 없으며 업데이트만이 안전한 선택" |
즉시 조치할 수 있는 방안 |
auto_start를 끄면 |
Xint 의견 | 자체 재현 환경에서 실제 익스플로잇이 동작하는 것을 확인했고, 이 문서의 조치 방안을 적용하면 공격이 완화되는 것도 확인했습니다. |
패치하지 않은 PHP는 기본 설정 그대로도 취약하다고 보세요. 설정을 바꿔서 줄일 수 있는 것은 일부 공격 경로뿐이고, 같은 원인의 새로운 공격 경로가 계속 발견될 수 있습니다.
공식 분석과 Xint 팀 재현 결과를 기준으로 정리했습니다. 새로 확인되는 내용은 계속 업데이트합니다. (마지막 업데이트: 2026-10-21 KST)
구성 | 판정 | 조건 |
Apache + mod_php (prefork) | O | 애플리케이션과 관계없이 임의의 PHP 스크립트에서 원격 코드 실행 가능. 세션 경로는 PHP 5.4.0 이상, 요청 변수 처리 경로는 PHP 7.3 이상 |
Apache + PHP-FPM (mod_proxy_fcgi) | O | session_start(), unserialize() 같은 디코딩이 일어나는 애플리케이션에서 원격 코드 실행 가능. WordPress, phpMyAdmin 등 주요 애플리케이션이 해당 |
Nginx + PHP-FPM | O | Apache + PHP-FPM과 같음. client_max_body_size를 늘렸거나 fastcgi_request_buffering off로 설정했다면 공격이 더 쉬워질 수 있음 |
Nginx + mod_php | N/A | Nginx는 mod_php를 지원하지 않음 |
O 취약 · N/A 해당 없음.
- 최초 제보자들이 원격 코드 실행을 확인한 애플리케이션: WordPress, Drupal, Joomla, MediaWiki, Roundcube, Magento, phpMyAdmin, Nextcloud, Laravel, Symfony, WHMCS. 이 목록은 시연 대상일 뿐이고, 취약점은 PHP 자체에 있습니다.
- 추가적으로 원격 코드 실행과 별개로 프로세스 메모리를 읽어내는 취약점도 공개됐습니다.
⚠️
주의: 명령줄(php -v, php -i)로 확인하면 안 됩니다
명령줄의 PHP는 웹 서비스와 다른 설정 파일을 읽습니다. 명령줄에서는 조치가 된 것처럼 보여도 실제 웹에는 적용되지 않았을 수 있고, 그러면 조치했다고 착각한 채 계속 취약한 상태로 남게 됩니다.
다운로드: https://gist.github.com/xint-io/14630b00ed2ff90cdde3bb92162e316f
87fd1076386a8c4fd0b3f9477eaf02debca1c36b54c9913e73b76fd18f562e08 php2shell_check.php
스크립트 안의 $TOKEN 값을 임의의 문자열로 바꿉니다. 바꾸지 않으면 404만 반환합니다.
추측하기 어려운 파일명(예: chk-8f3a2c.php)으로 저장해서 앱 진입점(index.php)과 같은 디렉터리에 올립니다.
브라우저에서 https://<도메인>/<파일명>.php?k=<TOKEN>으로 접속합니다.
확인이 끝나면 바로 삭제합니다.
사이트(vhost, PHP-FPM 풀)가 여러 개면 각각 확인해야 합니다. 결과는 아래처럼 해석할 수 있습니다.
출력 항목 | 다음 조치 |
|---|---|
| 3.1 패치 적용 |
| 패치 전까지 3.2 임시 완화 적용 |
| 3.2 적용 위치 표에서 내 환경 찾기 |
| 3.4 침해 여부 확인에 쓸 경로 |
⚠️
주의: 배포판 패키지(Ubuntu, RHEL 등)는 버전 번호를 그대로 두고 보안 패치만 백포트하기도 합니다. 스크립트가 "미적용"으로 판정해도 배포판 보안 공지를 같이 확인하세요.
패치가 유일한 근본 조치입니다. 공개된 버그 외에도 같은 패턴의 변종이 더 있는 것으로 알려졌고, 공식 패치는 이 원인 자체를 고쳐서 변종 공격까지 함께 막을 수 있습니다. 아래 임시 완화와 WAF는 패치를 적용할 때까지 시간을 버는 수단임을 알고 계셔야 합니다.
PHP 8.2 ~ 8.5는 수정 버전(8.5.12 / 8.4.27 / 8.3.36 / 8.2.35)으로 업데이트합니다. 운영체제·배포판별 업데이트 방법은 PHP 공식 보안 공지(공지 1, 공지 2)에 정리돼 있습니다.
업데이트 후 Apache나 PHP-FPM을 꼭 재시작하세요. 재시작하지 않으면 이전 버전을 계속 사용하게 됩니다.
컨테이너는 베이스 이미지를 다시 빌드해서 배포합니다.
2.2 스크립트로 버전을 다시 확인합니다.
PHP 8.1 이하는 PHP 공식 릴리스가 없습니다. 배포판이나 LTS 지원 업체가 보안 업데이트를 제공하는지 먼저 확인하세요. 제공되지 않는다면 지원 버전으로 올리는 것이 원칙이고, 그 전까지는 3.2 임시 완화를 적용하세요. 8.2도 2026년 말에 보안 지원이 끝나니 8.3 이상으로 올리는 편이 낫습니다.
Calif가 제시한 임시 완화책에 Xint 재현 결과를 더했습니다. 일부 공격 경로만 막고 변종까지 막지는 못합니다. 실행 방식(2.2 스크립트의 SAPI 값)에 따라 다릅니다.
실행 방식 | Calif 권고 | Xint 추가 권고 |
|---|---|---|
mod_php ( |
|
|
PHP-FPM ( |
|
|
왜 auto_start도 꺼야 하나요? Xint 팀 재현 결과, session.auto_start = On인 서버는 session.upload_progress.enabled를 꺼도 취약한 것으로 파악됐습니다. 세션을 자동으로 시작하는 설정이라 공격에 필요한 세션 처리가 모든 요청에서 일어나기 때문입니다.
⚠️
주의: memory_limit = -1은 PHP의 메모리 상한을 없앱니다.
요청 하나가 서버 메모리를 다 써버릴 수 있어서 서비스 장애(DoS)로 이어질 수 있습니다. 반드시 OS 수준의 메모리 상한을 함께 거세요.
컨테이너: 메모리 제한(--memory, Kubernetes resources.limits.memory)
systemd: 서비스 단위 MemoryMax
PHP-FPM: pm.max_children를 서버 메모리에 맞게 조정
Apache: MaxRequestWorkers를 서버 메모리에 맞게 조정
⚠️
주의: auto_start를 끄기 전에 확인하세요.
코드에 session_start() 없이 $_SESSION을 쓰고 있다면, auto_start를 끄는 순간 로그인이나 장바구니 같은 세션 기능이 멈춥니다.
# 세션을 쓰는 파일
grep -rln '\$_SESSION' /var/www
# session_start()를 직접 호출하는 파일
grep -rln 'session_start' /var/www공통 include 파일(config, header 등)에 session_start()가 이미 있으면 auto_start를 바로 꺼도 됩니다. 없으면 공통 include 파일 맨 앞에 session_start();를 넣은 다음 auto_start를 0으로 바꾸세요. 바꾼 뒤에는 로그인과 세션 기능을 꼭 테스트해야 합니다. 원래 꺼져 있는 서버(기본값 Off)라면 따로 할 일은 없습니다. ob_start(); 계열을 사용하는 경우 https://www.php.net/manual/en/function.session-start.php 를 참고해 수정해야 합니다.
session.upload_progress.enabled = 0을 적용하면, 서버에 진행률을 물어서 업로드 진행률을 보여주는 기능은 동작하지 않을 수 있습니다. 브라우저(JavaScript)에서 진행률을 계산하는 방식은 영향이 없습니다.
적용 위치
SAPI | 적용 위치 | 반영 시점 |
|---|---|---|
| Apache 설정(vhost 등)에 | Apache 재시작 |
| 풀 설정에 | PHP-FPM 재시작 |
| php.ini 또는 웹 루트의 |
|
컨테이너 | 이미지의 | 재빌드 후 재배포 |
가능하면 php_admin_value / php_admin_flag로 넣으세요. 애플리케이션이 코드에서 ini_set()으로 memory_limit을 다시 낮추면 완화 효과가 사라지는데, admin 설정은 코드에서 바꿀 수 없습니다.
가상호스팅: 직접 바꿀 수 없거나, 바꿨는데 스크립트 결과가 그대로라면 호스팅사가 설정 변경을 막아둔 경우입니다. 호스팅사에 PHP 업데이트 일정과 임시 완화 적용을 요청하세요.
적용한 뒤에는 2.2 스크립트로 다시 확인하세요.
WAF
사용 중인 WAF나 CDN 업체의 관리형 룰을 먼저 확인하세요. Cloudflare, Akamai, AWS 등 주요 업체가 이번 취약점용 룰을 미리 준비했습니다. 내 계정에 실제로 적용됐는지는 업체 공지나 관리 콘솔에서 확인해야 합니다.
직접 룰을 만든다면 아래 패턴을 차단합니다.
요청 본문에 PHP_SESSION_UPLOAD_PROGRESS 문자열이 들어 있으면 차단합니다. 서버에서 session.upload_progress.name을 바꿨다면 바꾼 이름으로 룰을 만드세요.
대괄호가 깊게 중첩된 필드 이름(a[b][c][d]...)
filename=""인 빈 업로드 파트가 대량으로 들어 있는 multipart 요청
⚠️
주의: charset 변형, 대용량 데이터, RFC 2231/5987 인코딩 같은 방법으로 WAF 탐지를 피하기가 매우 쉽습니다. 룰만으로는 완전히 막을 수 없으니 패치를 적용하기 전까지 쓰는 임시 조치로 보세요.
Apache
Apache는 길이 정보가 없는 chunked HTTP/1.1이나 HTTP/2 multipart 요청을 그대로 PHP에 넘깁니다. PHP의 post_max_size 검사는 선언된 길이만 보기 때문에 이 경로로 우회될 수 있습니다. LimitRequestBody로 요청 본문 크기를 해당 웹 서비스가 필요한 만큼만 허용하세요.
Nginx + PHP-FPM
client_max_body_size는 웹 서비스가 필요한 최소값으로 두고, 큰 업로드가 필요한 경로만 location 단위로 늘립니다.
fastcgi_request_buffering은 기본값(on)을 유지합니다.
패치와 함께 바로 확인하세요. 이 취약점은 애플리케이션 아래 단계에서 동작해서 애플리케이션 로그에는 흔적이 거의 남지 않습니다. PHP와 웹서버 로그를 봐야 합니다. 공격이 실패해도 흔적은 남습니다.
① 비정상 메모리 초과 로그
정상적인 메모리 초과 오류에는 PHP 파일명과 줄 번호가 찍힙니다. in Unknown on line 0으로 찍혔다면 요청을 처리하기 전 단계에서 터진 것이라 공격일 가능성이 높습니다. 주로 mod_php 공격에서 나타납니다.
grep -cE 'Allowed memory size of [0-9]+ bytes exhausted.*in Unknown on line 0' \
/var/log/php_errors.log /var/log/apache2/error.log /var/log/httpd/error_log \
/var/log/php*-fpm.log 2>/dev/nullPHP-FPM 공격은 애플리케이션이 실행되는 도중에 메모리 초과가 나기 때문에 실제 파일명이 찍힙니다. 세션이나 unserialize() 관련 위치에서 메모리 초과가 반복된다면 함께 의심하세요.
② 크래시와 워커 비정상 종료 로그
grep -E "Segmentation fault|signal 11|SIGSEGV" \ /var/log/apache2/error.log /var/log/httpd/error_log /var/log/php*-fpm.log 2>/dev/null
dmesg | grep -i segfault | grep -E "php|httpd|apache2"③ 요청, 응답 이상 징후
비정상적인 multipart 요청 직후에 메모리 초과 에러나 HTTP 500이 반복되는 경우
응답의 PHPSESSID 쿠키가 비정상적으로 길거나 깨진 값인 경우 (메모리 노출 공격의 흔적. WAF나 프록시 로그에서 확인)
원인을 알 수 없는 PHP 워커 재시작이 급증한 경우
④ 비정상적으로 큰 세션 파일
세션 저장 경로는 2.2 스크립트의 session.save_path 값을 쓰세요.
find <세션 저장 경로> -name 'sess_*' -size +1M -exec ls -lh {} \;⚠️
세션 파일은 기본 설정에서 24분(session.gc_maxlifetime = 1440초)이 지나면 정리 대상이 됩니다. 공격 후 시간이 지나면 이 방법으로는 확인하기 어려우니 최대한 빨리 확인하세요.
⑤ 침해 이후 흔적
# 웹서버 계정이 실행 중인 프로세스 (계정명은 환경에 맞게: www-data, apache, nginx 등)
ps -u www-data -o pid,ppid,lstart,cmd
# 최근 3일 안에 바뀐 PHP 파일 (웹쉘, 변조 확인)
find /var/www -name '*.php' -mtime -3 -ls
# 웹서버·PHP 프로세스의 외부 연결
ss -tnp | grep -E "php|httpd|apache2|nginx"흔적이 나오면 그 서버를 외부 인터넷에서 격리하고 로그를 보존하세요. 메모리 노출 가능성이 있으니 DB 비밀번호, API 키, 세션·암호화 키 같은 비밀정보도 교체 필요합니다.
PHP 공식 보안 공지: https://www.php.net/archive/2026.php#2026-10-21-5
PHP Foundation 설명: https://thephp.foundation/blog/2026/10/21/todays-php-security-release-and-how-were-strengthening-php-security/
GitHub 보안 권고 CVE-2026-109618: https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w
GitHub 보안 권고 CVE-2026-109619: https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp
Calif 기술 분석: https://calif.io/php2shell/
Wiz 기술 분석: https://www.wiz.io/blog/php2shell-php-rce
OpenAI 공동 대응 발표: https://openai.com/index/coordinated-disclosure-of-critical-vulnerabilities/
Xint 팀이 분석한 취약점 연구와 최신 보안 인사이트를 이메일로 가장 먼저 받아보세요.
문의: contact@xint.io
웹사이트: xint.io