
"php2shell" CVE-2026-109618 및 CVE-2026-109619에 대한 Zeek 및 Suricata 탐지
"php2shell" CVE-2026-109618 / CVE-2026-109619와 관련된 HTTP 지표에 대한 Zeek 탐지 및 Suricata 규칙으로, PHP longjmp() 메모리 처리에서의 잠재적 RCE입니다. 발견 사항은 의심스러운 트래픽 또는 잠재적으로 영향을 받는 소프트웨어를 식별합니다.
Zeek 7.0.0 이상이 필요합니다. Zeek Package Manager로 설치하십시오:
zkg install https://github.com/corelight/php2shell-detect
@load packages를 사용하여 Zeek 구성에서 설치된 패키지를 로드합니다.
직접 체크아웃한 경우 /path/to/php2shell-detect/scripts를 로드합니다.
이 패키지는 표준 Zeek HTTP, 파일 분석, notice, 클러스터 API를 사용합니다.
이 패키지는 POST 필드 이름 길이, 선택된 직렬화 데이터 구조, 반복되는 MIME 헤더를 검사합니다. 또한 구성된 세션 쿠키를 사용하여 관련 의심스러운 트랜잭션을 연관시킵니다. 응답 헤더는 별도의 PHP 버전 인벤토리를 제공합니다. 트래픽은 Zeek의 HTTP 분석기에 보여야 하며, 암호화된 HTTP는 검사 전에 복호화가 필요합니다.
모든 notice 유형은 PHP2SHELL:: 아래에 있습니다:
| Notice | 관찰 내용 |
|---|---|
PHP_Crash_Sequence | 하나의 세션 내 관련 의심스러운 HTTP 트랜잭션 |
Multipart_Header_Flood | 인식된 MIME 헤더의 과도한 반복 |
Large_Form_Key | 비정상적으로 긴 URL 인코딩 폼 필드 이름 |
Serialized_Integer_Overflow | 비정상적으로 큰 직렬화 정수 토큰 |
Serialized_Integer_Collisions | 인식된 직렬화 정수 키 패턴 |
Serialized_String_Collisions | 인식된 직렬화 문자열 키 패턴 |
Serialized_Duplicate_Array | 반복되는 직렬화 배열 항목 패턴 |
Serialized_Object_Repetition | 반복되는 직렬화 객체 패턴 |
Potentially_Affected_PHP_Version | 응답 헤더가 구성된 영향 범위 내의 버전을 알림 |
결과는 표준 notice.log로 기록되며, 이 패키지는 추가 로그 스트림을
생성하지 않습니다. notice의 sub 필드는 헤더 이름, 트랜잭션 깊이, 횟수,
짧게 이스케이프된 POST 본문 샘플 등 제한된 컨텍스트를 제공합니다. 샘플은
기본적으로 이스케이프 전 원시 바이트 64개로 제한되며,
notice_body_sample_bytes=0으로 설정하면 생략됩니다. 쿠키와 그 다이제스트는
메모리에서만 사용되며 이 패키지에 의해 기록되지 않습니다.
기본적으로 각각의 고유한 요청 지표는 트랜잭션당 하나의 notice를 발생시킬 수 있습니다.
동일한 지표의 반복 일치는 억제됩니다. detect_only_once=T로 설정하면
각 트랜잭션에서 첫 번째 요청 발견 이후 본문 검사를 중단합니다.
버전 인벤토리 notice는 요청 발견과 독립적입니다.
모든 설정은 PHP2SHELL:: 네임스페이스를 사용합니다:
| 설정 | 기본값 | 효과 |
|---|---|---|
detect_only_once | F | 각각의 고유한 요청 지표를 보고하며, T는 첫 번째 발견 후 중단 |
max_trans_depth | 3 | 연결당 처음 세 개의 HTTP 트랜잭션을 검사하며, 0은 전체를 의미 |
inspect_cookies | T | 연관에 세션 쿠키 사용 |
session_cookie_names | {"PHPSESSID"} | 세션을 연관시키는 데 사용되는 쿠키 이름 |
correlation_window | 1 min | 대기 중인 세션 연관 상태의 최대 보존 기간 |
max_pending_sessions | 1000 | 각 연관 테이블에 보존되는 항목 수 상한 |
inspect_post_bodies | T | POST 엔티티 데이터와 MIME 헤더 검사 |
http_body_analysis_byte_depth | 8388608 | POST 본문 검사 작업에 대한 보수적인 8 MiB 상한으로, MIME 파트 간에 공유되며, 0은 본문 전달 비활성화 |
body_overlap_bytes | 256 | 본문 청크를 가로지르는 패턴을 위한 보존 오버랩 |
large_form_key_bytes | 65536 | 폼 이름의 와이어 바이트 임계값 |
multipart_header_threshold | 128 | 발견에 필요한 반복된 인식 MIME 헤더 수 |
notice_body_sample_bytes | 64 | notice 샘플당 최대 원시 본문 바이트 수이며, 0은 샘플 생략 |
detect_affected_php_versions | T | 응답 헤더 버전 인벤토리 활성화 |
php_version_notice_interval | 1 hr | 포트와 버전에 걸쳐 서버 IP별 버전 notice 억제 |
php_fixed_patch_versions | 8.2: 35, 8.3: 36, 8.4: 27, 8.5: 12 | 브랜치별 잠정적 첫 수정 패치 |
예를 들어:
redef PHP2SHELL::detect_only_once = T;
redef PHP2SHELL::notice_body_sample_bytes = 0;
redef PHP2SHELL::session_cookie_names += { "custom_session" };
POST 검사는 전체 본문을 조립하는 대신 경계가 정해진 겹침을 가진 파일 분석 청크를 사용합니다. 폼 이름 계산은 값 구분자와 원시 NUL 종료를 준수합니다. 적격 POST 엔터티만 분석기를 받으며, 본문 예산과 트랜잭션 한도는 의도적으로 탐지 범위를 제한합니다. 이러한 지표는 불완전하며 정상 트래픽과도 일치할 수 있습니다.
Zeek 클러스터에서 워커는 Cluster::publish_hrw()를 사용하여 작은 세션 관찰을 프록시로 라우팅합니다. 동일한 세션에 대한 두 단계 모두 동일한 프록시에 도달하며, 프록시는 이를 매칭하고 PHP_Crash_Sequence를 직접 발생시킵니다. 이 알림은 후속 요청의 연결 ID, UID 및 트랜잭션 깊이를 유지하며, peer_descr는 프록시를 식별합니다. 워커는 콜백을 위해 전체 연결 또는 HTTP 레코드를 유지하지 않습니다.
트리거와 매칭되지 않은 후속 요청은 correlation_window 이후 만료되며, max_pending_sessions가 각 테이블을 제한합니다. 프록시는 각 세션 키에 대해 첫 번째 매칭되지 않은 후속 요청을 유지합니다. 트리거는 해당 키를 직접 조회하여 역순으로 도착하는 메시지를 처리하며, 동일한 키에 대한 이후의 매칭되지 않은 후속 요청은 해당 항목이 매칭되거나 만료될 때까지 병합됩니다. 매칭은 관찰 타임스탬프와 클라이언트, 서버, 서버 포트, HTTP 호스트 및 세션 다이제스트를 사용합니다. 독립 실행형 Zeek은 로컬에서 상관 분석합니다. 정상적인 프록시 연결이 필요합니다.
프록시 알림은 correlation_window에 대해 UID와 트랜잭션 깊이를 키로 하는 표준 알림 억제를 사용합니다. 이는 워커 발견 사항과 독립적이며 워커 본문 검사를 중단하지 않습니다. 서로 다른 프록시에서 서로 다른 세션 쿠키에 대한 동시 매칭은 클러스터 억제가 전파되기 전에 여전히 중복 알림을 생성할 수 있습니다.
외부 Server 및 X-Powered-By 응답 헤더가 검사되며, GET에 대한 응답도 포함됩니다. 헤더 이름은 대소문자를 구분하지 않습니다. 영향을 받는 하한은 PHP 5.4.0입니다. 다음 컷오프는 2026년 10월 18일에 확인된 최신 안정 릴리스 이후의 다음 패치가 두 버그를 모두 해결할 것이라고 가정합니다:
| 브랜치 | 확인 시점의 최신 안정 버전 | 잠정적 첫 수정 릴리스 |
|---|---|---|
| 8.2 | 8.2.34 | 8.2.35 |
| 8.3 | 8.3.35 | 8.3.36 |
| 8.4 | 8.4.26 | 8.4.27 |
| 8.5 | 8.5.11 | 8.5.12 |
구성된 수정 릴리스 및 동일 브랜치의 이후 패치는 알림을 발생시키지 않습니다. 컷오프가 구성되지 않은 브랜치는 후보로 남습니다. 일반적인 벤더 접미사는 기본 숫자 버전을 사용합니다. 배너는 없거나, 재작성되거나, 백포트된 수정 사항이 포함된 벤더 빌드를 반영할 수 있으므로 이는 인벤토리 힌트입니다.
suricata/php2shell.rules의 세 가지 규칙은 Zeek과 독립적으로 실행됩니다. zkg로 Zeek 패키지를 설치해도 이러한 규칙이 Suricata에 로드되지 않습니다. 세 규칙 모두 파일에서 활성화되어 있으며, Zeek 구성 옵션은 이에 영향을 주지 않습니다.
| SID | 검사 버퍼 | 관찰 |
|---|---|---|
| 3000650 | 메서드 POST를 사용하는 http.request_body | 요청 본문의 탐지 마커 |
| 3000651 | http.response_header | Server 또는 X-Powered-By가 알리는 잠재적 영향 PHP 버전 |
| 3000652 | http.response_body | phpinfo 형태의 HTML 제목에 있는 잠재적 영향 PHP 버전 |
suricata.yaml의 기존 rule-files 목록에 규칙 파일을 추가하고 다른 규칙 항목은 유지하십시오. 체크아웃의 절대 경로를 사용하십시오:
rule-files:
- /path/to/php2shell-detect/suricata/php2shell.rules
구성을 검증한 다음, 일반적인 배포 절차를 사용하여 Suricata를 다시 로드하거나 재시작하십시오:
suricata -T -c /path/to/suricata.yaml
이 규칙은 Suricata 8.0.6으로 검증되었습니다. HTTP 본문 검사가 활성화되어 있어야 하며, 요청/응답 본문 제한과 스트림 재조립 제한이 어떤 콘텐츠가 이 규칙들에 도달하는지를 결정합니다. 암호화된 HTTP는 검사 전에 복호화가 필요합니다.
alert.signature_id를 사용하거나 구성된 경고 출력에서 SID로 EVE 경고
레코드에서 이 규칙들을 찾으십시오. POST 마커는 정상 트래픽에서도 발생할
수 있으며 익스플로잇 시도를 입증하지 않습니다. Suricata는 Zeek 패키지의
세션 상관 분석을 수행하지 않습니다.
버전 규칙은 소프트웨어 인벤토리 신호입니다. 헤더 이름은 대소문자를 구분하지 않고 매칭되며, 본문 규칙은 phpinfo 형태의 HTML 제목을 인식합니다. 둘 다 위의 잠정 버전 기준값을 사용하며, 배너의 누락이나 재작성 및 벤더 백포트는 정확도를 제한합니다. 출시 전 TODO: 공식 PHP 보안 공지 및 릴리스 노트를 대조하여 두 규칙의 영향 범위와 최초 수정 릴리스를 검증하십시오.
각 버전 규칙은 클라이언트, 포트, 광고된 버전 전반에 걸쳐 서버 IP당
시간당 하나의 경고로 제한합니다. 이들은 응답을 검사하므로 track by_src는
서버를 추적합니다. 각 SID는 자체 제한을 유지하므로, 하나의 서버가 같은
시간 내에 헤더 경고와 phpinfo 경고를 모두 생성할 수 있습니다. POST 마커
규칙에는 시간당 제한이 없습니다.
btest를 설치하고 zeek, zeek-cut, unzip, bash, make가
PATH에 있는지 확인한 후 다음을 실행하십시오:
make check
make test
make zam
make zam은 Zeek의 ZAM 옵티마이저를 활성화한 상태로 테스트를 반복합니다. 포함된
단일 btest는 압축 병합된 업스트림 Zeek HTTP 코퍼스를 사용하며
전체 TSV HTTP 및 notice 로그를 비교합니다. PHP 8.0.26 인벤토리 notice
하나가 예상되며, reporter.log는 존재하지 않아야 합니다. 입력 세부 정보와
라이선스는 코퍼스 출처를 참조하세요.