원문 캡처 · github.com
원문 캡처 · github.com

php2shell-scanner는 PHP 애플리케이션에서 CVE-2026-109618과 CVE-2026-109619를 탐지하는 명령줄 도구다. Assetnote 보안 연구팀이 Searchlight의 Exposure Platform용으로 만든 탐지 로직을 사용한다.

스캐너는 먼저 최소 크기의 멀티파트 제어 요청을 보낸 뒤, 최대 세 차례에 걸쳐 조작한 요청을 전송한다. 이 요청에는 62단계로 중첩한 PHP 배열 키가 들어가며 크기는 1MiB에서 20MiB까지 늘어난다. PHP 메모리 고갈 오류, 차이가 확인되는 빈 HTTP 500 응답, PHP-FPM 작업자 실패, 또는 패치되지 않은 것으로 알려진 PHP 버전이 표시되면 취약한 대상으로 판정한다. 초기 5xx 응답이나 일반적인 요청 거부 상태, PHP 치명적 오류 서명이 없는 비어 있지 않은 HTTP 500 응답은 충돌 신호로 보지 않는다.

기본 검사 경로는 /index.php다. URL에 경로가 포함돼 있으면 이를 유지하며, --path나 --path-file로 사용자 지정 경로를 검사할 수 있다. phpMyAdmin 대상은 /show_config_errors.php에서도 자동으로 다시 검사한다. 리디렉션은 따라가지 않는다.

기본 --sapi auto 모드는 mod_php와 PHP-FPM 판정 규칙을 모두 적용한다. 알려진 SAPI에 맞춰 검사하려면 --sapi mod_php 또는 --sapi fpm을 지정한다. 검사 경로 하나에는 42바이트 제어 요청과 총 약 29MiB의 프로브가 전송될 수 있으며, 판정이 끝나면 추가 전송을 멈춘다. 중복 URL은 한 번만 검사한다.

필요한 환경은 Python 3.9 이상, h11, tqdm이다. 설치와 실행 예시는 다음과 같다.

pip install -r requirements.txt

단일 호스트는 python3 scanner.py -u https://example.com으로 검사한다. 호스트 목록을 검사하려면 python3 scanner.py -l hosts.txt를 사용한다. 여러 스레드로 실행해 결과를 저장하는 예시는 python3 scanner.py -l hosts.txt -t 20 -o results.json이다. 사용자 지정 헤더는 -H 옵션을 여러 번 지정해 전달할 수 있고, 결과는 기본적으로 터미널에 표시된다. -o를 지정하면 취약 판정 이유와 가린 HTTP 요청·응답 증거를 JSON 파일에 저장한다.

주의할 점은 일부 PHP 설정에서 오탐이 발생할 수 있다는 것이다. 멀티파트 프로브가 PHP의 memory_limit을 넘으면 패치된 버전과 취약한 버전이 모두 빈 HTTP 500 응답을 반환할 수 있다. Assetnote는 패치된 PHP 8.4.27에서 memory_limit이 2M~32M일 때, PHP 8.3.36에서 max_multipart_body_parts가 2785 이상이고 memory_limit이 128M일 때 이를 재현했다고 밝혔다. 정확한 임계값은 빌드에 따라 달라질 수 있다. PHP 버전이 공개되지 않으면 스캐너는 패치 후 발생한 안전한 메모리 고갈과 패치 전 취약 동작을 구별할 수 없으므로, 설치된 PHP 버전과 공급업체의 백포트 적용 여부를 확인해야 한다.

php2shell의 최초 발견은 Calif 팀에 공로가 돌아간다. 팀의 설명은 calif.io/php2shell에서 볼 수 있다. 검색 플랫폼은 Searchlight의 Exposure Platform이다.