Daily-It

Java·백엔드 운영 경험으로 AI·개발·자동화와 일상 IT의 실제 판단을 기록하는 기술 블로그입니다.

PHP2Shell 취약점 실제 악용 확인, PHP 수정 버전과 서버 패치 체크리스트

PHP2Shell로 불리는 두 취약점은 인증 없는 원격 코드 실행으로 이어질 수 있다고 PHP 공식 공지가 밝혔다. CVE 번호는 CVE-2026-109618과 CVE-2026-109619다. php.net이 안내한 수정 릴리스는 PHP 8.2.35, 8.3.36, 8.4.27, 8.5.12다. 패키지 설치 여부만 보지 말고 실제 웹 요청을 처리하는 PHP와 남아 있는 워커까지 확인해야 한다. CISA KEV 원문의 등재일은 2026년 10월 26일이다. 카탈로그에 기록된 조치 기한은 2026년 10월 29일이다. 패치와 침해 점검은 별도 작업이다. 업데이트했다고 이미 생긴 웹셸이나 탈취된 인증정보까지 사라지는 것은 아니다.

목차

무슨 일이 확인됐나?

먼저 내 서비스가 실제로 어떤 PHP를 쓰는지 확인하자. 같은 서버에서도 터미널의 PHP, 웹사이트의 PHP-FPM, 예약 작업의 PHP, 컨테이너 안의 PHP가 서로 다를 수 있다. 이번 점검의 기준은 “업데이트 명령이 성공했는가”가 아니라 “외부 요청을 받는 실행 환경이 실제로 교체됐는가”다.

이 글은 공식 공지와 보도 원문을 바탕으로 정리한 방어용 점검 목록이다. 특정 운영 서버에서 명령을 실행하거나 침해 여부를 조사한 결과는 아니다. 아래 명령은 환경에 맞게 검토한 뒤 사용할 예시다.

두 취약점의 위험도와 실제 악용 여부는 PHP 공식 보안 공지와 CISA KEV에서 확인할 수 있다.

PHP 팀은 2026년 10월 21일 공식 공지에서 두 취약점을 수정한 보안 릴리스를 안내했다. CVE-2026-109618은 메모리 부족 처리 과정의 손상, CVE-2026-109619는 해제된 메모리와 연결된 내부 요청 상태가 남는 문제로 설명한다. 두 문제 모두 인증 없는 원격 코드 실행으로 이어질 수 있다는 것이 PHP 측 설명이다.

공식 공지에 따르면 PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, LiteSpeed 등 웹 요청을 처리하는 PHP 환경이 영향을 받는다. 특히 mod_php는 애플리케이션 코드가 실행되기 전 공격이 촉발되는 시연이 있어 위험을 가장 높게 본다고 명시했다. 따라서 “우리 로그인 페이지는 안전하다”거나 “워드프레스 플러그인만 업데이트했다”는 이유로 PHP 런타임 점검을 생략할 수 없다.[1]

PHP가 등록한 CVE 레코드의 CVSS v4.0 평가는 두 취약점 모두 9.2점이다. 공격 복잡도는 높지만, 사전 권한이나 사용자 상호작용은 필요하지 않은 것으로 기재돼 있다. 공격 복잡도가 높다는 것이 패치를 미뤄도 된다는 뜻은 아니다.[3][4]

BleepingComputer는 취약점 공개 전에 여러 보안업체가 공격 시도를 포착했지만 모두 실패로 분류했다고 전했다. 이는 전체 공격의 성공률 통계가 아니다. 같은 기사는 이후 허니팟의 Apache mod_php 환경에서 원격 코드 실행 증거가 관측됐다고 보도했다.[8] 허니팟 원자료는 직접 검증하지 못했으므로 보도 내용으로만 전한다. 공격 재현이나 공격 코드는 이 글에서 다루지 않는다.

공격자 신원, 실제 피해 조직 수, 국내 피해 통계는 확인한 자료만으로 확정할 수 없다. 공격 시도 관측, 실제 코드 실행, 조직의 데이터 유출은 서로 다른 사실이다. TechCrunch는 Calif 연구진이 AI 모델의 도움으로 악용 경로를 찾았다고 보도했다. AI가 혼자 찾아냈다거나 특정 모델의 성능이 입증됐다는 뜻은 아니다.[9]

여러 서버 랙 앞에 방패가 놓이고 서버 한 대에서 붉은 경고 불빛이 켜진 개념 일러스트
개념 일러스트(daily-it.com 자체 제작)

KEV 등재일과 목요일 기한은 어떻게 읽어야 하나?

등재일은 기사 날짜가 아니라 KEV 항목의 dateAdded를 기준으로 읽어야 한다.

CISA의 KEV JSON에는 두 CVE 모두 dateAdded가 2026-10-26, dueDate가 2026-10-29로 적혀 있다. 등재일은 10월 26일이고, 카탈로그에 기록된 조치 기한은 10월 29일 목요일이다. 기사 발행일과 KEV 등재일은 구분해야 한다.

CISA 공식 알림은 BOD 26-04가 미국 연방 민간 행정부 기관인 FCEB에 적용된다고 설명한다. 따라서 이 기한이 한국의 모든 민간 서버 운영자에게 직접 적용되는 법적 의무는 아니다. 국내 운영자는 자산의 외부 노출 여부와 업무 중요도로 대응 순서를 정하되, 실제 악용이 확인된 취약점이라는 점을 패치 일정에 반영해야 한다.

두 KEV 항목에는 forensicTriage가 Yes로 기록돼 있다. CISA의 이행 지침도 패치 전 침해 가능성을 살피는 절차를 다룬다. 운영 관점에서는 업데이트와 함께 증거 보존 및 침해 흔적 점검을 병행해야 한다는 뜻이다.[5][6][10][11]

어떤 버전으로 바꿔야 하나?

수정 버전은 php.net 공식 공지에 나온 브랜치별 버전을 기준으로 삼는다.

아래 표는 php.net의 10월 21일 보안 공지와 ChangeLog를 기준으로 정리했다. 영향 범위는 PHP CNA의 CVE 레코드와도 대조했다. 수정 기준은 두 CVE에 한정되며, 같은 브랜치의 해당 수정 버전 또는 이후 보안 릴리스를 적용한다.[1][2][3][4] 이 버전들이 PHP의 모든 보안 문제를 해결한 최종 버전이라는 뜻은 아니므로, 적용할 때 후속 보안 공지도 함께 확인해야 한다.

모바일에서는 표를 좌우로 밀어 마지막 열까지 확인할 수 있다.

PHP 브랜치 영향 범위(PHP 공식 배포본) 최초 수정 버전
8.2 8.2.35 미만 8.2.35
8.3 8.3.36 미만 8.3.36
8.4 8.4.27 미만 8.4.27
8.5 8.5.12 미만 8.5.12
8.1 이하 지원 종료·벤더 확인 필요 백포트 확인 또는 이전

PHP 공식 공지는 PHP 8.1 이하가 PHP 프로젝트의 일반 업데이트를 더 이상 받지 않는다고 안내한다. 일부 배포판이나 장기지원 업체가 수정을 백포트했을 수 있으므로, 공급업체에 확인하거나 지원 버전으로 옮겨야 한다.

반대로 배포판 패키지는 표시되는 PHP 버전이 낮아도 보안 수정이 백포트됐을 수 있다. 숫자만 보고 안전이나 취약을 단정하지 말고, 설치된 패키지의 전체 릴리스 번호와 해당 공급업체의 두 CVE 대응 공지를 대조해야 한다. 특정 배포판 패키지의 수정 릴리스 번호는 이 글에서 확인하지 않았다.

php -v만 확인하면 끝인가?

아니다. php -v는 현재 셸이 선택한 CLI 실행 파일의 버전만 보여준다.

터미널에서는 우선 다음처럼 실행 파일과 설정 위치를 확인할 수 있다.[12]

command -v php
php -v
php --ini

여기서 확인한 값이 웹사이트의 PHP와 같다는 보장은 없다. 웹서버가 어떤 FPM 소켓이나 포트로 연결되는지, 해당 FPM 서비스가 어떤 바이너리와 설정을 사용하는지 별도로 확인해야 한다. Apache mod_php라면 Apache가 로드하는 PHP 모듈과 관련 패키지를 확인한다.

서버에 사이트가 여러 개라면 가상호스트별 연결 대상도 살핀다. 관리 화면에서 한 사이트의 PHP만 바꾸고 다른 사이트는 예전 풀에 남겨 두는 일이 없도록 한다. 예약 작업과 큐 워커 역시 별도 실행 경로를 사용할 수 있다.

버전 확인을 위해 phpinfo 페이지를 인터넷에 공개하는 방식은 피한다. phpinfo는 버전뿐 아니라 설정과 경로 등 운영 정보를 드러낼 수 있다.[13] 불가피하게 웹 실행 환경을 확인해야 한다면 인증과 접근 제한이 있는 내부 경로에서 필요한 정보만 확인하고, 점검 파일과 접근 경로를 즉시 제거한다. 검색에 잡히지 않는 주소라는 이유만으로 안전하다고 판단하지 않는다.

apt와 dnf에서는 무엇부터 확인하나?

설치된 패키지와 저장소의 후보 버전을 조회한 뒤, 실제 서비스가 쓰는 패키지를 업데이트해야 한다.

Debian·Ubuntu 계열에서는 다음 명령을 조회 예시로 사용할 수 있다.

dpkg -l 'php*'
apt-cache policy 'php*'

설치 목록에서 CLI, FPM, 공통 패키지, 확장 모듈, Apache용 모듈을 구분한다. 배포판 기본 저장소인지 외부 저장소인지도 기록한다. 저장소가 오래됐거나 패키지가 보류 상태라면 업데이트 명령이 성공해도 필요한 수정이 들어오지 않을 수 있다.

다음은 php8.4-cli, php8.4-fpm, php8.4-common이 이미 설치된 환경의 예시다. 다른 브랜치나 mod_php 환경에 그대로 적용하지 않는다.

sudo apt update
sudo apt install --only-upgrade \
  php8.4-cli php8.4-fpm php8.4-common

위 예시의 세 패키지만으로 점검이 끝나는 것은 아니다. 설치된 확장 모듈과 의존성도 업데이트 대상인지 검토한다. 변경 전에는 설정, 애플리케이션, 데이터베이스의 복구 절차와 서비스 중단 가능성을 확인한다.

RHEL·Fedora 계열의 조회 예시는 다음과 같다.

rpm -qa 'php*'
dnf info --installed 'php*'
dnf check-update 'php*'

해당 저장소의 보안 공지와 패키지 릴리스를 확인한 다음 업데이트한다.

sudo dnf upgrade 'php*'

외부 저장소에서 다른 이름으로 제공하는 PHP는 위 패턴에 포함되지 않을 수 있다. 실제 패키지 이름을 기준으로 조회 범위를 조정해야 한다.

패키지 교체 후에는 기존 프로세스가 남아 있는지도 확인한다. 서비스 이름이 실제로 php-fpm인 환경의 예시는 다음과 같다.

systemctl status php-fpm
sudo systemctl restart php-fpm

버전별 서비스 이름을 쓰는 환경에서는 그 이름으로 바꾼다. mod_php라면 PHP-FPM이 아니라 Apache 프로세스 교체가 필요하다. 무중단 요구가 있으면 노드를 순차적으로 교체하고, 오래된 워커가 모두 종료됐는지까지 확인한다.

서버 부품을 새 부품으로 교체하는 손과 컨테이너가 차례로 바뀌는 장면의 개념 일러스트
개념 일러스트(daily-it.com 자체 제작)

공식 PHP 컨테이너 이미지는 어떻게 패치하나?

기존 컨테이너 안을 임시 수정하기보다, 수정된 공식 이미지를 바탕으로 새 컨테이너를 배포해야 한다.

먼저 Dockerfile과 배포 설정의 이미지 태그, 고정된 다이제스트, 실제 실행 중인 이미지 식별자를 확인한다. 공식 이미지라는 이유만으로 이미 실행 중인 컨테이너까지 자동 업데이트되지는 않는다. 수정 릴리스를 포함하는 이미지가 제공되는지도 별도로 확인해야 한다.

Compose에서 이미지를 직접 사용하는 환경은 검증한 이미지 참조로 설정을 갱신한 뒤 다음과 같이 새 이미지를 받아 컨테이너를 재생성할 수 있다.

docker compose pull
docker compose up -d --force-recreate

Dockerfile로 자체 이미지를 만드는 환경은 베이스 이미지 참조를 먼저 점검하고 다시 빌드한다.

docker compose build --pull
docker compose up -d --force-recreate

태그나 다이제스트를 취약한 이미지에 고정해 두었다면 --pull만으로는 해결되지 않는다. app이라는 서비스 이름을 쓰는 환경에서는 다음처럼 새 컨테이너의 CLI 버전을 확인할 수 있다.

docker compose exec app php -v

이 결과도 컨테이너 안의 CLI 버전일 뿐이다. 실제 웹 요청을 처리하는 FPM이나 Apache 실행 환경, 모든 복제본의 이미지, 남아 있는 이전 컨테이너까지 대조한다. 롤백 이미지도 수정된 빌드로 준비해야 장애 대응 중 취약한 이미지로 돌아가는 일을 막을 수 있다.

호스팅을 쓰면 무엇을 물어봐야 하나?

두 CVE의 수정 적용 여부와 실제 웹 실행 환경의 교체 완료를 구체적으로 확인해야 한다.

호스팅 업체에는 “PHP 최신인가요?”라고만 묻지 말고 다음 내용을 확인하자. CVE-2026-109618과 CVE-2026-109619에 대한 수정 적용 여부, 적용한 PHP 또는 백포트 패키지 릴리스, 내 계정에 연결된 실행 방식, 워커 재시작 완료 여부를 요청한다.

관리 화면의 PHP 선택값과 실제 사이트 실행 환경이 일치하는지도 문의한다. 직접 버전을 바꿀 수 없는 상품이라면 적용 예정 시점과 그동안 가능한 접근 제한 조치를 확인한다. 답변은 티켓이나 이메일로 남겨 이후 재점검의 근거로 삼는다.

즉시 업데이트가 어렵다면 안전한가?

아니다. PHP 공식 공지는 완전한 해결책이 업데이트이며, 임시 조치는 특정 공격 경로의 노출을 줄일 뿐이라고 설명한다.

메모리나 요청 크기 제한을 임의로 바꾸는 방식은 가용성에 다른 문제를 만들 수 있다. 공식 공지에 나온 설정이라도 일부만 떼어 적용하지 말고 적용 위치, 애플리케이션의 덮어쓰기 가능성, 자원 제한을 함께 검토해야 한다. WAF가 요청을 차단했다는 사실도 런타임 패치 완료를 대신하지 않는다.

CISA는 즉시 업데이트할 수 없거나 지원 종료 버전을 계속 쓰는 조직에 패치 또는 업그레이드까지 PHP 기반 시스템을 오프라인으로 전환하라고 권고한다. 서비스 영향과 복구 경로를 검토해 노출을 줄이고, 후속 PHP 보안 공지도 함께 확인한다.[7]

웹셸과 로그는 어디를 확인해야 하나?

최근 변경 파일, 요청 기록, 프로세스와 외부 연결을 함께 살펴야 한다.

웹 루트와 업로드·캐시·임시 디렉터리에서 예상하지 못한 PHP 파일, 기존 파일의 변경, 배포 기록과 맞지 않는 생성 시점을 확인한다. 파일 이름이 정상처럼 보이거나 확장자가 PHP가 아니라는 이유만으로 제외하지 않는다. 정상 배포 산출물이나 신뢰할 수 있는 백업과 비교하면 판단 근거를 보강할 수 있다.

웹서버 접근·오류 로그와 PHP-FPM 로그를 함께 본다. 낯선 경로로의 반복 요청, 비정상적인 POST 증가, 평소와 다른 오류나 워커 종료를 확인한다. 특히 PHP 워커가 예상 밖의 셸이나 다운로드 도구를 실행했는지 살핀다. CISA도 이런 활동을 감시하라고 권고한다.[7] 로그의 시간대와 보존 기간도 확인한다. 웹서버 계정으로 도는 의심 프로세스, 예상하지 못한 외부 연결, 예약 작업과 서비스 설정 변경도 함께 대조한다.

다만 이런 항목은 일반적인 침해 점검 기준이지, PHP2Shell만을 식별하는 확정 지표는 아니다. 오류 하나나 낯선 파일 하나만으로 이번 취약점의 피해라고 단정해서는 안 된다. 반대로 웹셸 파일이 없거나 로그에 특이점이 없다는 사실만으로 미침해를 증명할 수도 없다.

의심 흔적이 있다면 파일부터 삭제하거나 서버를 바로 재부팅하지 않는다. 외부 접근을 제한하면서 가능한 범위에서 로그, 의심 파일, 실행 상태를 보존한다. 사고 대응 담당자와 조치 순서도 정한다. 긴급 패치를 진행하면서 격리와 증거 보존도 함께 챙겨야 한다.

침해가 확인되면 패치만으로 종료하지 않는다. 신뢰할 수 있는 이미지로 재구축하고 지속성 설정을 제거한다. 영향받은 비밀정보를 교체하고 다른 시스템으로 확산했는지도 확인한다.

돋보기로 문서와 로그 묶음을 살피고 옆에 증거 상자가 놓인 개념 일러스트
개념 일러스트(daily-it.com 자체 제작)

언제 점검을 끝냈다고 말할 수 있나?

  • 실제 서비스의 수정 적용, 이전 프로세스 제거, 정상 동작과 침해 점검까지 확인했을 때다.
  • 완료 기록에는 서버별 웹 실행 방식, PHP 버전 또는 백포트 근거, 패키지·이미지 식별자, 프로세스 교체 결과를 남긴다. 로그인, 업로드, 세션 유지, 예약 작업 등 서비스에 필요한 정상 기능도 확인하고 오류율을 관찰한다.
  • 침해 흔적 점검 결과는 패치 결과와 분리해 적는다. “패치 완료, 조사 진행 중”이라고 적어도 된다. 확인하지 못한 부분을 모두 안전으로 처리하는 것보다 정확한 기록이다.

정리: 이번 대응에서 기억할 세 가지

이번 대응의 핵심은 세 가지다. php.net에서 수정 기준을 확인하고, 실제 요청을 처리하는 PHP를 교체하고, 패치 이전에 무슨 일이 있었는지 살피는 것이다. 터미널에 찍힌 버전 한 줄이나 업데이트 성공 메시지만으로 서버가 안전하다고 판단해서는 안 된다.

참고 자료