CVE

CVE-2026-109618, CVE-2026-109619 (PHP2Shell)

메모리 부족으로 멈춘 PHP의 ‘뒷정리 실수’를 노려, 인증 없이 웹 서버에서 코드를 실행하는 RCE 취약점

2026.10.22 | 조회 37 |
0
|

“요청 하나로 PHP 서버가 뚫린다고요?” — PHP2Shell 쉽게 풀어보기

안녕하세요 구독자님! 이번 호는 급하게 들고 왔어요 🚨

웹사이트의 75~80%가 쓴다는 PHP에서, 로그인 없이 요청만으로 서버에서 코드를 실행할 수 있는 취약점 2건 PHP2Shell이 10월 21일 긴급 패치됐어요. 이미 공격 시도가 관측되고 있고 KISA도 보안 업데이트를 권고했어요. 커피 한 잔 하시면서 훑어보세요 ☕️

첨부 이미지

 

1. PHP가 뭐예요? 🐘

PHP는 웹사이트를 만드는 데 쓰는 서버 쪽 프로그래밍 언어예요. 우리가 브라우저로 게시판이나 쇼핑몰 페이지를 열면, 웹 서버가 PHP에게 “이 페이지 만들어 줘”라고 부탁하고, PHP가 로그인 확인, DB 조회 같은 일을 처리해서 화면(HTML)을 돌려주는 구조예요.

워드프레스, 그누보드, 라라벨 같은 유명한 웹 서비스 및 프레임워크가 모두 PHP 기반이고, 전 세계 웹사이트의 75~80%가 PHP를 쓴다고 해요. 그만큼 PHP 자체에 구멍이 나면 영향 범위가 엄청나게 넓어져요.

이번 취약점을 이해하려면 두 가지만 기억해 주세요.

  • 웹 서버와 PHP를 연결하는 방식이 여러 가지예요. Apache 안에 PHP를 넣어 돌리는 mod_php, PHP를 별도 프로세스 묶음으로 돌리는 PHP-FPM 등이 있고, 방식에 따라 이번 취약점의 위험도가 달라져요.
  • PHP는 앱 코드를 돌리기 전에 요청을 먼저 준비해요. 들어온 요청 데이터를 읽고 메모리를 잡는 준비가 끝남 뒤에야 워드프레스 같은 앱 코드가 실행돼요. PHP2Shell은 바로 이 ‘준비 단계’에서 터져요.

 

2. PHP2Shell 사건 살펴보기 🪪

이번 취약점은 공개 과정부터 예사롭지 않았어요. 원래는 보안 업체들에만 미리 공유된 상태였는데, 10월 20일 온라인에 “PHP RCE 2건이 공개될 것”이라는 글이 올라오면서 존재가 먼저 유출됐어요. PHP 보안팀은 바로 사전 공지를 내고, 다음 날인 10월 21일 14시(UTC)에 긴급 패치를 배포했어요.

더 놀라운 건, 아마존 허니팟이 공개 4일 전인 10월 17일에 이미 공격 시도를 기록했다는 거예요. 패치 직후에는 Cloudflare와 watchTowr도 실제 공격을 확인했고, 안랩 TIP 추산으로는 취약한 PHP 버전을 쓰는 것으로 보이는 국내 호스트가 약 11만 2천 개예요. 😨



출처: php.net 보안 권고, Help Net Security, BleepingComputer (2026.10.21)
출처: php.net 보안 권고, Help Net Security, BleepingComputer (2026.10.21)

 

3. PHP2Shell 간단 개요 ☁️

PHP2Shell은 두 CVE를 묶어 부르는 이름이에요. 둘 다 CVSS v4.0 9.2점이고, 웹 요청을 처리하는 모든 PHP(PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, LiteSpeed)가 대상이에요. Calif, Wiz, OpenAI 연구진이 찾았어요.

  • CVE-2026-109618 (OOM 손상) : 메모리 부족(OOM)으로 요청 처리가 중간에 끊기면, 반만 준비된 데이터를 정리 단계가 정상 데이터로 처리해요.
  • CVE-2026-109619 (남아 있는 전역 상태) : 요청이 쓰던 내부 상태가 메모리 해제 후에도 남아, 같은 워커의 다음 요청이 그걸 쓰면서 Use-After-Free가 발생해요.
출처 : https://www.ahnlab.com/ko/contents/content-center/36340
출처 : https://www.ahnlab.com/ko/contents/content-center/36340

CVSS 벡터는 AV:N/AC:H/AT:P/PR:N/UI:N 예요. 네트워크로, 권한·사용자 조작 없이 공격할 수 있지만 공격 난도(AC:H)가 높고 특정 조건(AT:P)이 필요하다는 뜻이에요.

 

4. PHP2Shell은 왜 위험할까요?💻

    php.net 보안 권고(GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp), Imperva    
    php.net 보안 권고(GHSA-w8hw-w9fx-j79w, GHSA-6wmf-4rv5-p3rp), Imperva    

CVE-2026-109618 취약점의 핵심은 ‘갑자기 멈춘 뒤의 뒷정리’ 예요. PHP는 메모리 한도(memory_limit)를 넘으면 longjmp()로 현재 작업을 즉시 빠져나가요. 그런데 요청 메모리가 반만 초기화된 순간에 빠져나가면, 요청 종료(shutdown) 단계가 그 어중간한 값을 정상으로 믿고 처리해버려요(CVE-2026-109618). 이해를 돕기 위해 단순화하면 이런 흐름이에요.

// ⚠️ 개념 예시 (실제 PHP 소스 코드가 아닙니다)
void request_startup(req) {
    req->buf = alloc(...);   // ① 포인터는 생겼는데
    req->len = ...;          // ② 여기서 메모리 초과 → longjmp로 즉시 탈출
}                            //    (나머지 초기화·정리 기록은 건너뜀)

void request_shutdown(req) {
    cleanup(req->buf, req->len);  // ③ 반만 준비된 값을 정상으로 믿고 정리
}

이 문제는 앱 코드가 실행되기도 전, PHP가 요청을 받아 준비하는 단계에서 터져요. 특히 Apache mod_php는 준비가 실패해도 평소처럼 '정리' 단계를 실행하는데, 이때 반쯤 만들어진 데이터를 정상으로 착각하고 건드리면서 공격으로 이어져요. 반면 PHP-FPM은 준비가 실패하면 그 프로세스를 아예 끝내버려서 이 경로의 위험이 상대적으로 낮아요.

그래서 PHP 보안팀은 mod_php의 위험도를 가장 높게 봤어요. 그리고 워드프레스든 라라벨이든 앱의 로그인 확인은 그다음 단계라, 이 공격을 막을 기회조차 없어요.

 

CVE-2026-109619는 비슷한 문제가 요청과 요청 사이에서 일어나요. PHP 워커는 여러 요청을 차례로 처리하는데, 치명적 오류나 실행 시간 초과로 리셋이 빠지면 이전 요청의 메모리를 가리키는 전역 상태가 남아요. 권고문이 확인한 경로는 두 가지예요.

  • 세션 업로드 진행률 (session.upload_progress) : 기본값이 켜져 있고, 정상적인 세션 초기화보다 먼저 세션 상태를 만지는 기능이에요.
  • 치명적 오류 백트레이스 (fatal_error_backtraces, PHP 8.5) : 종료 처리에서 리셋이 항상 빠지는 상태예요.

한마디로, “비정상 종료 + 불완전한 뒷정리 + 워커 재사용” 이 합쳐지면서 원격 코드 실행까지 가능해진 거예요.

 

4-1. 버린 메모리를 다시 쓰면 무슨 일이 생길까요? 🧩

PHP는 데이터를 ‘메모리 주소’로 기억해요. 메모리는 이미 반납했는데 주소만 남아 있다면, 다음 요청에서 그 주소를 믿고 쓰는 순간 세 가지 일이 생길 수 있어요.

  • 서버가 멈춤 : 반납된 주소에는 쓸 수 없거나 의미 없는 값이 있어요. 그걸 읽거나 쓰려는 순간 운영체제가 PHP 프로세스를 강제 종료해요.
  • 메모리 노출 : 반납된 메모리는 곧 다른 작업이 다시 써요. PHP가 옛 주소로 읽은 그 데이터를 응답에 담으면, 보여주면 안 되는 서버 내부 데이터가 공격자에게 넘어가요.
  • 해커 코드 실행 : 공격자가 그 자리에 자기 데이터가 들어가도록 유도하고, 안에 ‘다음에 실행할 위치’ 같은 정보를 심어두면 PHP가 그걸 정상 값으로 믿고 따라가요. 정확한 자리에 정확한 데이터를 넣어야 해서 난도는 높지만(AC:H), 연구진이 실제로 성공시켰어요.

한마디로, 빈 곳이면 멈추고, 남의 데이터가 있으면 새어 나가고, 해커가 넣은 데이터가 있으면 실행돼요.

 

4-2. GitHub에 퍼진 PoC, 믿어도 될까요? 🧪

  출처: 안랩 ASEC ‘GitHub php2shell 저장소 분석’ (2026.10.22)  
  출처: 안랩 ASEC ‘GitHub php2shell 저장소 분석’ (2026.10.22)  

안랩 ASEC이 공개 직후 GitHub의 ‘php2shell’ 저장소 100개를 분석했더니, 96개가 10월 21일 하루에 만들어졌어요. 수로는 방어 도구(46개)가 더 많았지만, 관심(스타)의 약 85%는 공격 저장소에 몰렸고요.

문제는 그 PoC들을 믿기 어렵다는 거예요. RCE가 된다고 주장하는 저장소가 있는 반면, "내부 상태 손상만 재현됐다"거나 "재현에 실패했다"는 저장소도 있었어요. 인기 PoC의 설명을 그대로 복제한 저장소도 확인됐고요. 인기 CVE를 미끼로 가짜 PoC에 악성코드를 넣는 수법은 이미 알려져 있어서, 출처가 불분명한 코드는 꼭 격리된 환경에서만 돌려야 해요.

방어 도구도 안심은 금물이에요. 명령어 한 줄로 서버 전체 PHP 설정을 바꾼다는 완화 스크립트 중에는 root 권한으로 실행되는 것도 있고, AI로 만들어 동작을 보증하지 않는다고 스스로 밝힌 도구도 있었어요. 탐지는 Corelight의 Zeek·Suricata 규칙이나 NCSC-NL의 공개 자료처럼 믿을 수 있는 출처를 먼저 참고하는 게 좋아요.

 

5. 그럼, 본 취약점을 어떻게 막을 수 있을까요? 🔧

10월 21일 보안 패치가 공개됐어요. 아래 버전으로 바로 업데이트해 주세요. 업데이트가 유일한 완전한 해결책이에요.

  • 수정 버전 : 8.5.12 · 8.4.27 · 8.3.36 · 8.2.35 (8.2는 2026년 12월 31일 지원 종료)
  • 8.1 이하 : PHP 공식 지원이 끝난 버전이라 리눅스 배포판, LTS 벤더의 백포트 패키지를 확인해야 해요. 7.4·8.0·8.1용 최소 소스 백포트가 나왔지만, 근본적으로는 지원 버전으로 옮겨야 해요.

먼저 내 서버의 PHP 버전과 관련 설정을 확인해 보세요.

php -v                                             # PHP 버전 확인
php -i | grep -E "Server API|memory_limit|session.upload_progress.enabled|post_max_size"

패치는 문제의 뿌리인 longjmp() 기반 OOM 처리를 없앴어요. 권고문에 따르면 메모리가 부족해도 그 자리에서 튀어나가지 않고, 다음 안전한 지점에서 멈추는 ‘지연된 soft OOM’과, 더 높은 한도에서 프로세스를 즉시 끝내는 ‘hard OOM(_exit())’으로 바꿨어요. 개념적으로는 이런 차이예요.

// ⚠️ 개념 예시 (실제 패치 코드가 아닙니다)
// 패치 전: 한도 초과 즉시 탈출 → 반쯤 만든 상태가 남음
if (over_limit) longjmp(bailout);

// 패치 후: 지금 작업은 끝내고, 안전한 지점에서 멈춤
if (over_limit) oom_pending = true;          // soft OOM: 다음 safe point에서 오류 처리
if (over_hard_limit) _exit(1);               // hard OOM: 워커 프로세스를 즉시 종료

 

5-1. 만약 패치가 당장 어렵다면? ⏳

PHP 보안 권고문에 임시 완화 설정이 나와 있어요. 다만 어디까지나 위험을 줄이는 임시 처방이고, PHP 코어 개발자도 “공격을 완전히 막는 완화책은 없다”고 강조했어요.

; CVE-2026-109618 완화
; FPM 풀 또는 Apache 설정에서 php_admin_value로 지정
memory_limit = -1
post_max_size = 8M              ; 파일 업로드가 필요하면 32M
; upload_max_filesize는 post_max_size보다 작게

; CVE-2026-109619 완화 (설정 후 모든 워커 재시작)
session.upload_progress.enabled = 0
fatal_error_backtraces = 0      ; PHP 8.5만 해당
  • memory_limit를 풀었다면 컨테이너, cgroup 메모리 제한을 걸어 해당 워커만 종료되게 해주세요.
  • 웹 서버·프록시에서 요청 크기, 필드 수, multipart 파트 수, 헤더 수를 제한해주세요.
  • ini_set()이나 .user.ini로 바꾸면 효과가 없고, 에러 메시지를 숨기는 것도 도움이 안 돼요.
  • upload_progress를 끄면 파일 업로드 진행률 표시 기능도 꺼지니 서비스 영향을 먼저 확인해주세요.

 

Cloudflare, Imperva 등의 WAF 규칙도 보조 수단으로 쓸 수 있어요. 패치 전에 인터넷에 노출돼 있던 서버라면, 웹 로그의 비정상 multipart 요청 및 웹 루트의 수상한 파일·웹 서버 계정으로 실행된 프로세스도 꼭 점검해주세요.

 

6. 보안 용어 정리 🍪

  1. SAPI (Server API) : PHP가 웹 서버와 연결되는 방식을 말해요. 예: PHP-FPM, Apache mod_php, php-cgi
  2. OOM (Out Of Memory) : 프로그램이 쓸 수 있는 메모리를 다 써버린 상태예요. PHP에서는 memory_limit를 넘었을 때 발생해요.
  3. Use-After-Free (UAF) : 이미 해제된 메모리를 다시 사용하는 취약점이에요.
  4. PoC (Proof of Concept) : 취약점이 실제로 동작함을 보여주는 개념 증명 코드예요.

 

  📎  더 알고 싶다면, 이 자료를 찾아보세요! 

 

오늘은 PHP 자체의 뒷정리 실수에서 시작된 PHP2Shell을 함께 살펴봤어요. 앱 코드를 아무리 잘 짜도 런타임이 뚫리면 소용없다는 걸, 그리고 ‘내 서버에 어떤 버전이 돌고 있는지 아는 것’이 보안의 시작이라는 걸 다시 느끼게 되네요 🛡️

다음 호에서도 더 유익한 이야기로 찾아뵙겠습니다. 끝까지 읽어주셔서 감사합니다 🙏

 

다가올 뉴스레터가 궁금하신가요?

지금 구독해서 새로운 레터를 받아보세요

✉️

이번 뉴스레터 어떠셨나요?

tori 해킹 일지 님에게 ☕️ 커피와 ✉️ 쪽지를 보내보세요!

댓글

의견을 남겨주세요

확인
의견이 있으신가요? 제일 먼저 댓글을 달아보세요 !

다른 뉴스레터

이웃 뉴스레터 Beta

비슷한 이야기를 하는 다른 뉴스레터의 글이에요.

© 2026 tori 해킹 일지

"tori 해킹 일지"는 해킹 기법과 취약점 분석을 매주 쉽게 풀어내는 보안 뉴스레터입니다.

메일리 로고

도움말 오류 및 기능 관련 제보

서비스 이용 문의admin@team.maily.so 채팅으로 문의하기

메일리 사업자 정보

메일리 (대표자: 이한결) | 대표번호: 070-8027-1409 | 사업자번호: 717-47-00705 | 서울특별시 송파구 위례광장로 199, 5층 501-2-31호

이용약관 | 개인정보처리방침 | 정기결제 이용약관 | 라이선스