PHP2Shell은 2026년 10월 21일 PHP 개발팀이 긴급 패치한 두 개의 PHP 원격 코드 실행 취약점(CVE-2026-109618, CVE-2026-109619)을 부르는 별칭이다. 로그인 없이 보낸 HTTP 요청만으로 서버에서 코드를 실행할 수 있고, 심각도는 둘 다 CVSS v4.0 기준 9.2점이다.
문제는 워드프레스 플러그인이나 게시판 코드가 아니라 PHP 엔진 자체에 있다. 그래서 어떤 프로그램을 돌리든, PHP로 웹 요청을 받는 서버라면 일단 점검 대상이다. 어제 밤 패치가 나왔고, 나도 오늘 아침 내 서버부터 확인했다. 그 과정과 함께 지금 해야 할 일을 순서대로 정리한다.
- 영향 범위는 PHP 5.4부터 8.5.11까지다. 고친 버전은 8.5.12, 8.4.27, 8.3.36, 8.2.35이고, 8.1 이하는 공식 패치가 없다.
- PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, LiteSpeed 등 웹 요청을 처리하는 모든 방식이 대상이다. 가장 위험한 건 mod_php다.
- 업데이트만이 완전한 해결책이다. 리눅스 배포판 패키지는 버전 숫자가 그대로여도 패치가 들어가 있을 수 있으니 버전이 아니라 패키지 변경 기록으로 확인해야 한다.
PART 01무슨 일이 있었나: 나흘간의 타임라인
이번 사건은 진행 순서가 조금 꼬였다. 원래 PHP 개발팀은 패치를 준비한 뒤 정해진 날에 한꺼번에 공개하려 했다. 그런데 공개 전에 CVE 번호와 일부 정보가 먼저 퍼졌고, 결국 일정을 하루 앞당겼다. 아래는 PHP 공식 공지와 Tenable, Patchstack, 국내 보도를 맞춰 본 흐름이다.
session.upload_progress.enabled를 끄는 완화 작업을 했다. KISA도 업데이트를 권고했다.보도마다 "이미 뚫린 곳이 많다"와 "아직 확인된 침해는 없다"가 엇갈린다. 내 판단은 단순하다. 둘 중 어느 쪽이 맞든 할 일은 똑같다. 패치하고, 패치 전에 이상한 흔적이 없었는지 본다.
PART 02두 취약점은 어떻게 동작하나
세부 공격 코드는 다루지 않는다. 다만 원리를 알아야 임시 완화책이 왜 그런 설정인지 이해된다. PHP 공식 보안 권고(GHSA)에 적힌 설명을 쉽게 풀면 이렇다.
CVE-2026-109618: 메모리 부족 순간을 노린다
PHP는 요청을 받으면 내부 데이터를 하나씩 준비한다. 그런데 준비가 반쯤 끝난 순간 메모리가 바닥나면 처리가 중간에 끊긴다. 이후 정리 단계에서 PHP가 이 덜 만들어진 데이터를 정상 데이터로 착각하는 게 문제다. 공격자는 일부러 큰 요청을 보내 메모리 한도를 넘기는 식으로 이 상황을 만든다. 그래서 임시 완화책에 memory_limit과 post_max_size가 등장한다.
CVE-2026-109619: 앞 요청의 흔적이 다음 요청에 남는다
PHP-FPM 같은 방식은 작업 프로세스 하나가 여러 요청을 차례로 처리한다. 이 취약점은 앞 요청이 쓰던 내부 상태가 메모리가 해제된 뒤에도 남아 있다가, 같은 프로세스가 받은 다음 요청에서 다시 쓰이는 문제다. 흔히 말하는 use-after-free다. 코드 실행뿐 아니라 메모리 내용이 새어 나가는 것도 확인됐다고 PHP는 밝혔다. 메모리에는 DB 비밀번호나 API 키가 있을 수 있으니, 코드 실행까지 안 가더라도 가볍게 볼 일이 아니다.
두 문제 모두 애플리케이션 코드가 실행되기 전, PHP가 요청을 받아 해석하는 단계에서 터진다. 특히 Apache mod_php 구성에서는 애플리케이션 코드가 돌기도 전에 인증 없는 요청 하나로 공격이 시연됐다. 보안 연구팀 Calif는 워드프레스, 드루팔, 줌라, 마젠토의 특정 버전에서 로그인 없는 원격 코드 실행을 확인했다고 밝혔다. 플러그인을 아무리 최신으로 유지해도 이번엔 소용없다는 뜻이다.
PART 03내 서버는 위험한가: 영향 버전 한눈에 보기
PHP 공식 공지는 "웹 요청을 처리하는 모든 PHP 설치"가 위험하다고 적었다. PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, LiteSpeed가 모두 들어간다. 명령줄에서만 돌리는 스크립트는 공지에서 따로 언급되지 않았지만, 같은 서버에서 웹도 돌린다면 결국 업데이트 대상이다.
PART 04오늘 아침 내 서버 점검기
기사를 보자마자 서버에 접속해 php -v부터 쳤다. 결과는 PHP 8.5.4. 안전한 버전은 8.5.12라고 했으니 숫자만 보면 한참 뒤처진 상태다. 순간 등이 서늘했다. 이 서버엔 PHP-FPM 풀이 여러 개 떠 있고, 모두 외부 요청을 받는다.
그런데 바로 업데이트를 돌리기 전에 패키지 변경 기록을 열어 봤다. 우분투 패키지 버전은 8.5.4-0ubuntu1.5였고, 변경 기록 맨 위에 "SECURITY UPDATE: Remote RCE via state corruption issue"와 함께 CVE-2026-109618, 109619 패치 파일들이 올라와 있었다. 배포판이 PHP 버전 숫자는 그대로 두고 수정 코드만 가져다 붙이는 방식(백포트)이다.
dpkg 로그를 보니 이 패키지는 오늘 새벽 6시 23분에 자동 보안 업데이트(unattended-upgrades)로 설치됐다. PHP-FPM 프로세스 시작 시각도 6시 23분 30초. 패치 설치와 재시작이 같이 끝나 있었다. 결과적으로 내가 한 일은 확인뿐이었다.
교훈은 두 가지다. 첫째, 배포판 패키지는 php -v 숫자만 보고 판단하면 안 된다. 둘째, 반대로 숫자가 높아 보여도 직접 빌드한 PHP나 오래된 도커 이미지는 패치가 안 됐을 수 있다. 결국 "어디서 온 PHP인지"를 먼저 알아야 한다.
PART 055분 점검 명령어 모음
아래는 내가 실제로 쓴 순서다. 우분투·데비안 기준이고, 레드햇 계열은 rpm -q --changelog로 바꿔 보면 된다.
1) 어떤 PHP가 돌고 있는지부터
# 명령줄 PHP 버전 (참고용일 뿐, 웹용 PHP와 다를 수 있다)
php -v
# 웹 요청을 받는 PHP 프로세스 확인
systemctl list-units --type=service | grep -Ei "php|apache|httpd|lsws|frankenphp"
ps -eo pid,lstart,cmd | grep -E "php-fpm|httpd|apache2" | grep -v grep
# 설치된 PHP 패키지 전부
dpkg -l | grep -E "^ii\s+(php|libapache2-mod-php)"
2) 버전 숫자 말고 패치 여부를 확인
# 배포판 패키지 변경 기록에 CVE가 들어 있는지
zcat /usr/share/doc/php8.5-fpm/changelog.Debian.gz | grep -m3 -E "CVE-2026-10961[89]"
# 패키지가 언제 설치됐는지
zgrep -h "php8.5-fpm" /var/log/dpkg.log* | sort | tail -3
php8.5-fpm 부분은 내 서버의 패키지 이름으로 바꾸면 된다. CVE 번호가 검색되면 패치가 들어간 패키지다. 아무것도 안 나오면 아직 이전 패키지다.
3) 패치 후 재시작까지 됐는지
# FPM 마스터 프로세스 시작 시각 — 패키지 설치 시각보다 뒤여야 한다
ps -o lstart= -p "$(cat /run/php/php8.5-fpm.pid)"
# 재시작이 안 됐다면
sudo systemctl restart php8.5-fpm
패키지를 깔아도 이미 떠 있는 작업 프로세스는 옛날 코드를 메모리에 들고 있다. 특히 109619는 작업 프로세스가 요청을 이어 받는 구조를 노리므로 재시작이 빠지면 패치 효과가 없다.
4) 잊기 쉬운 곳
- 도커 이미지:
docker images로 php 베이스 이미지를 찾고, 새로 받은 뒤 다시 빌드·배포했는지 - 스테이징·개발 서버, 오래된 관리자 페이지 전용 서버
- 설치형 솔루션에 묶여 들어온 PHP (NAS 관리 화면, 사내 그룹웨어 등)
- Windows에서 직접 받은 PHP(IIS·XAMPP 등) — php.net 윈도우 빌드로 교체
- 공유 호스팅 — 호스팅사 공지와 적용 일정 확인
PART 06당장 업데이트가 안 된다면: 임시 완화책
PHP 공식 공지는 분명히 적었다. 업데이트만이 완전한 해결책이고, 아래 설정은 설치를 안전하게 만들지 않는다. 그래도 패치까지 몇 시간이라도 벌어야 한다면 다음 설정을 쓴다. 반드시 요청 해석 전에 적용되는 위치(php.ini, FPM 풀 설정, 웹서버 설정)에 넣어야 한다.
; php.ini — 요청 해석 전에 적용되는 위치에 넣는다
memory_limit = -1
post_max_size = 8M ; 파일 업로드가 필요하면 32M
upload_max_filesize = 2M ; 항상 post_max_size보다 작게
session.upload_progress.enabled = 0
fatal_error_backtraces = 0 ; PHP 8.5만 해당
; FPM 풀 설정(/etc/php/8.5/fpm/pool.d/www.conf 등) — 앱이 덮어쓰지 못하게 admin 값으로
php_admin_value[memory_limit] = -1
php_admin_value[session.upload_progress.enabled] = 0
적용한 뒤에는 모든 작업 프로세스를 재시작한다. 몇 가지 함정도 같이 적어 둔다.
웹방화벽도 시간을 버는 수단이다. Cloudflare는 무료 요금제를 포함한 모든 고객에게 알려진 공격 패턴 차단 규칙을 배포했고, 구글 클라우드는 Cloud Armor 규칙과 함께 검사 한도를 넘는 큰 요청 본문을 막으라고 권했다. 다만 이것도 Cloudflare를 거치는 트래픽만 막는다. 서버 IP로 직접 들어오는 요청은 그대로다.
PART 07이미 당했는지 확인하는 법
Amazon은 최소한 10월 17일부터의 기록을 살펴보라고 권했다. 이번 취약점은 애플리케이션 아래 단계에서 터지기 때문에 앱 로그에는 흔적이 거의 안 남을 수 있다. Wiz가 제시한 신호를 기준으로 웹서버·PHP 로그를 보자.
# 10월 17일 이후 500 응답이 몰린 IP 상위
awk '$9==500 {print $1}' /var/log/nginx/access.log* | sort | uniq -c | sort -rn | head
# FPM 작업 프로세스 비정상 종료 흔적
journalctl -u php8.5-fpm --since "2026-10-17" | grep -Ei "exited|signal|memory" | head
의심 흔적이 나오면 그 서버는 침해됐다고 보고 움직이는 게 맞다. 서버가 들고 있던 DB 비밀번호, API 키, 세션 비밀키를 바꾸고, 가능하면 깨끗한 이미지에서 다시 올린다. 워드프레스라면 Patchstack이 경고한 대로 UpdraftCentral 연결 키도 지우고 새로 발급하자. 출처가 불분명한 "긴급 보안 패치 플러그인"이나 퍼온 코드 조각은 설치하지 않는다.
- PHP2Shell은 PHP 엔진 자체 문제다. 앱과 플러그인 업데이트로는 해결되지 않는다.
- 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 이상으로 올리고, 모든 PHP 작업 프로세스를 재시작한다.
- 배포판 패키지는 버전 숫자 대신 변경 기록의 CVE 번호로 패치 여부를 확인한다.
- 8.1 이하는 공식 패치가 없다. 업그레이드 계획을 지금 잡는다.
- 10월 17일 이후 로그에서 500 오류 폭증, 이상한 세션 쿠키, 프로세스 재시작을 찾아본다.
자주 묻는 질문
PHP2Shell은 정확히 무엇인가요?
PHP2Shell은 2026년 10월 21일 패치된 PHP 엔진의 원격 코드 실행 취약점 두 건(CVE-2026-109618, CVE-2026-109619)을 부르는 별칭입니다. 인증 없이 보낸 웹 요청만으로 서버에서 코드를 실행할 수 있고, 심각도는 CVSS v4.0 기준 9.2점입니다.
어떤 PHP 버전이 안전한가요?
PHP 8.5.12, 8.4.27, 8.3.36, 8.2.35 이상이 공식 패치 버전입니다. 우분투·데비안 같은 배포판 패키지는 버전 숫자가 낮아 보여도 보안 패치가 백포트되어 있을 수 있으니 패키지 변경 기록에서 CVE 번호를 확인하세요. PHP 8.1 이하는 공식 릴리스가 없습니다.
워드프레스 플러그인만 업데이트하면 되나요?
아닙니다. 이번 취약점은 워드프레스가 아니라 그 아래에서 도는 PHP 엔진에 있습니다. 워드프레스, 그누보드, 라이믹스 등 어떤 PHP 프로그램이든 PHP 자체를 패치된 버전으로 올려야 합니다.
PHP를 업데이트한 뒤 따로 해야 할 일이 있나요?
PHP-FPM이나 Apache 같은 PHP 작업 프로세스를 반드시 재시작해야 합니다. 이미 실행 중인 프로세스는 이전 코드를 메모리에 들고 있기 때문입니다. FPM 마스터 프로세스의 시작 시각이 패키지 설치 시각보다 뒤인지 확인하면 됩니다.
session.upload_progress.enabled를 끄면 안전해지나요?
완전히 안전해지지는 않습니다. PHP 공식 공지에 따르면 이 설정은 CVE-2026-109619의 공격 경로 일부를 줄이는 임시 조치일 뿐이며, 업데이트만이 완전한 해결책입니다. 또 이 설정을 끄면 파일 업로드 진행률 표시 기능이 동작하지 않습니다.
공유 호스팅을 쓰는데 제가 할 일이 있나요?
호스팅사가 PHP를 관리하므로 직접 패치할 수는 없습니다. 호스팅사 공지에서 패치 적용 일정을 확인하고, 사이트가 PHP 8.1 이하 버전을 선택해 쓰고 있다면 관리 화면에서 8.2 이상으로 바꾸는 것을 검토하세요.
참고 자료
- PHP 공식 — Critical remote code execution vulnerabilities, update now (2026-10-21)
- PHP 보안 권고 — CVE-2026-109618 (GHSA-w8hw-w9fx-j79w)
- PHP 보안 권고 — CVE-2026-109619 (GHSA-6wmf-4rv5-p3rp)
- Tenable — php2shell FAQ
- Google Cloud — Responding to core PHP vulnerabilities
- Patchstack — PHP major vulnerability alert
- AhnLab ASEC — PHP2Shell 긴급공지
- 카페24 — PHP 긴급 보안 취약점 조치 작업 공지
- 보안뉴스 — PHP2Shell 파장
- ZDNet Korea — PHP 보안 결함 비상

댓글 0