AWS

다운로드 0
업데이트 2026. 10. 19.

[AWS-WAF] PHP2Shell 취약점 공격 시도

PHP의 세션 업로드 진행률 기능(session.upload_progress)에서 발생하는 사전 인증 원격 코드 실행 취약점 PHP2Shell(CVE-2026-109618 · CVE-2026-109619, CVSS 9.2) 공격 요청을 AWS WAF 로그로 탐지합니다.

POST 요청 중 다음 조건을 판정합니다.

  • 요청 헤더 · WAF 규칙 매칭 정보에 PHP_SESSION_UPLOAD_PROGRESS 포함
  • 또는 업로드 기능이 없는 경로로 들어온 multipart/form-data 요청이면서 본문이 1MB를 넘는 경우

공격은 같은 PHP 워커가 처리하는 multipart 요청 2건 이상으로 성립하고 인증도 필요 없으므로 1건부터 탐지합니다. WAF 처리 결과(action · sub_signature)와 본문 크기 대비 WAF 검사 크기를 함께 출력해, 요청이 실제로 오리진에 도달했는지와 WAF가 본문 전체를 검사했는지를 바로 확인할 수 있게 했습니다.

개요

중요도 상
유형 배치 탐지
실행 주기 매달/매일/매시/20분 20 * * * *
메시지 [AWS-WAF] PHP2Shell 취약점 공격 시도 : $src_ip → $domain
출력 필드 순서 _time, src_ip, domain, method, path, action, sub_signature, body, inspected_req_body_size, f_field, f_mp, f_big, user_agent, raw_data
MITRE 기술 T1190
MITRE 전술 TA0001 Initial Access
출처 사용자 제안 최형선

쿼리

1table from=$("from") to=$("to") *:AWS_WAF* 2 3|# [AWS-WAF] PHP2Shell 취약점 공격 시도 4|# 목적 PHP 원격 코드 실행 취약점(PHP2Shell, CVE-2026-109618 · 109619) 공격 요청 탐지 5|# 판정 POST 중 PHP_SESSION_UPLOAD_PROGRESS 포함, 또는 업로드 기능이 없는 경로로 들어온 본문 1MB 초과 multipart 요청 6|# 근거 로그프레소 PHP2Shell 대응 권고(2026-10-21) 웹 액세스 로그 점검 항목, 실제 공격 사례(본문 1,048,575 · 8,388,605 · 20,971,517바이트 multipart) 7 8|# POST 요청만 대상 9| search method == "POST" 10 11|# 경로 · 요청 헤더 · WAF 규칙 정보를 소문자로 정리 (규칙 정보가 없는 환경은 빈 값으로 처리) 12| eval p = lower(str(path)), raw = lower(str(raw_data)), rules = lower(concat(nvl(str(non_match_rules), ""), nvl(str(rule_groups), ""))) 13 14|# 본문 크기 : req_body_size가 없는 환경은 요청 헤더 Content-Length 값으로 대체 15| rex field=raw_data "(?i)content-length:\s*(?<clen>\d+)" 16| eval body = long(nvl(req_body_size, clen)) 17 18|# 업로드 경로 : 경로에 업로드 관련 단어가 있는 요청 (정적 리소스 경로는 업로드 경로로 보지 않음) 19| eval upload = if(match(p, "upload|attach|image|img|media|photo|storage|(^|[/_.-])files?") and not(match(p, "\\.(css|js|png|jpe?g|gif|ico|svg|webp|woff2?|ttf|map|txt|html?)$")), 1, 0) 20 21|# 지표 1 : 요청 헤더 · WAF 규칙에 PHP_SESSION_UPLOAD_PROGRESS 포함 22| eval f_field = if(contains(concat(nvl(raw, ""), rules), "php_session_upload_progress"), 1, 0) 23 24|# 지표 2 : 업로드 경로가 아닌 곳으로 들어온 multipart 요청 (Next.js 서버 액션 요청 제외) 25| eval f_mp = if(contains(raw, "multipart/form-data") and not(contains(raw, "next-action")) and upload == 0, 1, 0) 26 27|# 지표 3 : 업로드 경로가 아닌 곳으로 들어온 본문 1MB 초과 요청 (1MB보다 1바이트 작게 맞춘 공격도 잡도록 1,000,000바이트 기준) 28| eval f_big = if(nvl(body, 0) > 1000000 and upload == 0, 1, 0) 29 30|# 판정 : 지표 1, 또는 지표 2와 3을 함께 만족 31| search f_field == 1 or (f_mp == 1 and f_big == 1) 32 33|# 결과 : WAF 처리(action · sub_signature), 본문 크기와 WAF 검사 크기, 판정 지표, 요청 헤더 원문 34| fields _time, src_ip, domain, method, path, action, sub_signature, body, inspected_req_body_size, f_field, f_mp, f_big, user_agent, raw_data
  1. AWS WAF 로그에서 method == "POST" 요청만 대상으로 합니다.
  2. 경로 · 요청 헤더 · WAF 규칙 매칭 정보(non_match_rules · rule_groups)를 소문자로 정리합니다. 규칙 정보가 없는 환경은 빈 값으로 처리해 동작에 영향이 없도록 했습니다.
  3. req_body_size가 비어 있으면 원본 로그의 Content-Length 헤더를 rex로 추출해 본문 크기를 보정합니다.
  4. 요청 경로가 업로드 기능 경로(upload · attach · image · img · media · photo · storage · files 등)인지 판정합니다(upload). 정적 리소스 확장자 경로는 업로드 경로로 보지 않습니다.
  5. 세 가지 지표를 산출합니다.
    • f_field : 요청 헤더 · WAF 규칙 매칭 정보에 php_session_upload_progress 포함 — 직접 근거
    • f_mp : 업로드 경로가 아닌 곳으로 들어온 multipart/form-data 요청 (Next.js 서버 액션은 next-action으로 제외)
    • f_big : 업로드 경로가 아닌 곳으로 들어온 본문 1MB 초과 요청
  6. 판정은 f_field 단독, 또는 f_mp와 f_big을 함께 만족하는 경우입니다. multipart와 대용량 본문을 AND로 묶어, 일반 폼 전송이나 API 호출로 인한 오탐을 줄였습니다.
  7. 결과로 WAF 처리(action · sub_signature), 본문 크기와 WAF 검사 크기(inspected_req_body_size), 판정 지표, 요청 헤더 원문(raw_data)을 출력해 티켓에서 바로 판단할 수 있게 했습니다.

적용 시 확인 사항

  • 권장 실행 : 매시 20분, 분석 기간 1시간 (WAF 로그 적재 지연 대응)
  • 쿼리의 *:AWS_WAF*는 사용 환경의 WAF 테이블 이름으로 변경하십시오.
  • f_big의 기준을 1,048,576(1MB)이 아니라 1,000,000바이트로 잡았습니다. 1MB에서 1바이트 모자란 값(1,048,575)으로 크기 기반 탐지를 회피하는 사례가 확인되어, 경계값 바로 아래도 포함되도록 한 것입니다.
  • PHP_SESSION_UPLOAD_PROGRESS는 multipart 본문에 들어가는 폼 필드입니다. 요청 헤더만 기록하는 환경에서는 본문을 검사하는 WAF 규칙이 있어야 f_field가 발동하므로, 해당 규칙 유무를 확인하십시오. 규칙이 없으면 f_mp + f_big 조합이 실질 탐지 경로가 됩니다.
  • inspected_req_body_size보다 본문이 크면 WAF가 본문 앞부분만 검사한 것입니다. 뒷부분에 공격 문자열이 있어도 탐지되지 않았을 수 있으므로 두 값을 함께 확인하십시오.
  • AWS WAF는 오리진 응답 코드를 기록하지 않습니다. 공격 성공 여부는 같은 시간대 ALB · CloudFront 접속 로그의 5xx(특히 502) 응답과 대조해 판단하십시오.

위협 분석

취약점 구조

PHP2Shell은 요청 처리가 끝난 뒤에도 해제된 메모리를 가리키는 포인터와 상태가 전역에 남는 결함 두 건입니다. CVE-2026-109618은 메모리 부족 상황에서 처리가 중단되며 발생하는 메모리 손상, CVE-2026-109619는 워커에 남은 과거 상태(OPcache 공유 메모리, PHP 8.5의 치명적 오류 백트레이스 포함)가 다음 요청에서 재사용되는 문제입니다. 두 건 모두 CVSS 9.2이며, 코드 실행뿐 아니라 메모리 내용 유출도 가능합니다.

현재까지 확인된 주요 공격 경로는 멀티파트 업로드 진행률 기록 기능(session.upload_progress)입니다. 이 기능은 기본값이 켜짐(On)이므로 추가 설정 없이 기본 구성만으로 성립하며, 인증도 필요하지 않습니다.

공격 흐름

  1. 공격자가 멀티파트 POST 본문에 PHP_SESSION_UPLOAD_PROGRESS 필드와 세션 ID(PHPSESSID)를 폼 필드로 직접 넣어 전송합니다. 업로드 파일명이 그대로 저장되는 점을 이용해 세션 파일 내용을 조작합니다.
  2. 첫 번째 요청에서 전역에 포인터 · 상태가 남습니다.
  3. 같은 워커가 처리하는 두 번째 요청에서 해제된 메모리를 공격자가 구성한 구조체로 덮어쓰고, 세션 역직렬화 과정에서 함수 호출 흐름을 탈취합니다.
  4. 익스플로잇은 빌드별 메모리 오프셋에 의존합니다. 오프셋이 맞지 않거나 힙 경쟁에서 지면 워커가 비정상 종료되어, 프록시 뒤에서는 502로 관측됩니다. 성공하면 200으로 응답하며 페이로드가 실행됩니다.

악용 현황

  • 2026-10-17 : 공개 전 최초 공격 시도 관측 (AWS MadPot)
  • 2026-10-21 02:00 UTC : PoC 최초 확인
  • 2026-10-21 14:00 UTC : 엠바고 해제 및 패치 공개

공개 이전부터 악용 정황이 있었고 공개 직후 PoC가 다수 유포되었으므로, 패치 적용 여부와 별개로 과거 로그 소급 점검이 필요합니다. 관측된 공격 출발지 상당수는 공용 익명화 인프라로 확인되었습니다.

영향 범위

  • PHP 8.2.35 · 8.3.36 · 8.4.27 · 8.5.12 미만 (2026-10-21 패치 공개)
  • PHP 5.4~8.1은 고정 버전이 제공되지 않아, 인터넷에 노출된 EOL PHP가 가장 큰 위험 구간입니다.
  • Apache mod_php 환경은 애플리케이션과 무관하게 코드 실행이 확인되었고, PHP-FPM 환경도 영향을 받습니다.

오탐 유형

  • 업로드 관련 단어가 없는 경로로 첨부파일을 올리는 정상 기능 (게시글 등록 · 문의 등록 등) → 해당 경로를 upload 조건에 추가. 정상 요청은 세션 쿠키와 경계값이 있어 공격 요청(경계값 없음)과 구분됩니다.
  • 멀티파트로 폼을 전송하는 프레임워크 · SPA → Next.js 서버 액션은 next-action으로 제외하나, 다른 프레임워크는 환경별 확인 필요
  • 세션 ID를 폼 필드로 전송하는 레거시 애플리케이션
  • 취약점 점검 도구 · 모의해킹 · PoC 실행 → 점검 출발지를 제외 대역으로 등록

대응 방안

**1. PHP 업그레이드 **

  • 고정 버전 : 8.5.12 · 8.4.27 · 8.3.36 · 8.2.35 이상
  • 영향 버전 : 8.5.0~8.5.11 · 8.4.0~8.4.26 · 8.3.0~8.3.35 · 8.2.0~8.2.34
  • PHP 5.4~8.1은 고정 버전이 제공되지 않으므로 지원 버전으로 마이그레이션해야 합니다.
  • AWS 환경은 서비스별 조치가 다릅니다. Elastic Beanstalk는 관리형 플랫폼 업데이트를 쓰지 않으면 수동 갱신, Lambda는 사용자 정의 런타임 · 컨테이너 이미지 직접 갱신, Lightsail은 실행 중 인스턴스 패치가 불가해 신규 블루프린트로 재구성이 필요합니다.

2. 본문 크기 제한 병행 (필수)

  • PHP2Shell_BODY는 검사 한도를 넘는 본문을 Continue로 처리합니다. 한도 밖 영역은 검사되지 않으므로 본문 크기 제한 규칙을 반드시 함께 적용해야 합니다.
  • AWSManagedRulesCommonRuleSet의 SizeRestrictions_BODY(8KB 초과 차단)를 활성화하거나, 동등한 사용자 정의 SizeConstraint 규칙(Body · OversizeHandling: MATCH · GT 8192)을 적용합니다.
  • 본문 검사 한도는 ALB · AppSync 8KB, CloudFront · API Gateway 기본 16KB(최대 64KB까지 상향 가능)입니다. 본 규칙 결과에 「검사 한도 초과」가 반복되면 CloudFront 본문 검사 한도를 상향하십시오.

레퍼런스

아직 댓글이 없습니다.