testing php2shell detection

PHP2Shell 위협 헌팅: Suricata 시그니처, Falco 런타임 규칙과 Auditd 가이드

php2shell.run Research Lab •

PHP2Shell 위협 헌팅: Suricata 시그니처, Falco 런타임 규칙과 Auditd 가이드

<time datetime="2026-10-23">검증일: 2026년 10월 23일</time>

PHP2Shell(CVE-2026-109618, CVE-2026-109619)은 애플리케이션 로그에 거의 흔적을 남기지 않습니다. 요청은 정상적인 multipart/form-data처럼 보이고, 침해는 사용자 코드 실행 이전에 완료됩니다. 따라서 네트워크 계층, 런타임 계층, 커널 감사 계층을 함께 구성하는 심층 방어가 필요합니다. 이 문서는 운영 환경에 바로 적용 가능한 규칙을 제공합니다.


1. 심층 방어 아키텍처

 ┌─────────────────────────────────────────────────────────────┐
 │ 계층 1: 네트워크 (Suricata / Zeek / Corelight)              │
 │  - multipart 본문 내 PHP_SESSION_UPLOAD_PROGRESS 탐지       │
 │  - 비정상적으로 작은 반복 POST 및 404 대상 업로드 탐지      │
 └───────────────────────────┬─────────────────────────────────┘
                             ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ 계층 2: 호스트 런타임 (Falco / Tetragon / eBPF)             │
 │  - php-fpm 자식이 sh, curl, wget, nc 실행 시 경보           │
 │  - 예상치 못한 아웃바운드 연결 및 파일 생성 탐지            │
 └───────────────────────────┬─────────────────────────────────┘
                             ▼
 ┌─────────────────────────────────────────────────────────────┐
 │ 계층 3: 커널 감사 (auditd / eBPF)                           │
 │  - www-data UID의 execve, connect, openat 추적              │
 │  - 사후 포렌식과 침해 범위 확정                             │
 └─────────────────────────────────────────────────────────────┘

세 계층은 서로를 대체하지 않습니다. 네트워크 계층은 우회 가능하고, 런타임 계층은 커널 수준 은닉에 취약하며, 감사 계층은 예방이 아니라 사후 분석에 강점이 있습니다.


2. 네트워크 계층: Suricata 시그니처

2.1 프로덕션 규칙 (로컬 규칙 예시)

# /etc/suricata/rules/php2shell.rules
alert http any any -> $HOME_NET any (
  msg:"PHP2Shell CVE-2026-109618/109619 multipart upload progress probe";
  flow:established,to_server;
  http.method; content:"POST";
  http.content_type; content:"multipart/form-data"; nocase;
  file.data; content:"PHP_SESSION_UPLOAD_PROGRESS"; nocase; distance:0;
  threshold: type limit, track by_src, count 5, seconds 60;
  classtype:attempted-admin;
  reference:cve,2026-109618;
  reference:cve,2026-109619;
  sid:10261092; rev:1;
)
# 404/정적 경로로 향하는 multipart POST — 정상 업로드 경로가 아닌 경우
alert http any any -> $HOME_NET any (
  msg:"PHP2Shell multipart POST to non-upload path";
  flow:established,to_server;
  http.method; content:"POST";
  http.content_type; content:"multipart/form-data"; nocase;
  http.uri; content:"/"; pcre:"/^\/(api|static|assets|health|404)/i";
  threshold: type limit, track by_src, count 10, seconds 60;
  sid:10261093; rev:1;
)

2.2 규칙 배포와 검증

sudo cp php2shell.rules /etc/suricata/rules/
sudo sed -i 's/^#\s*-\s*php2shell.rules/- php2shell.rules/' \
  /etc/suricata/suricata.yaml

sudo suricata -T -c /etc/suricata/suricata.yaml -v
sudo systemctl reload suricata
sudo tail -f /var/log/suricata/fast.log | grep -E '1026109[23]'

file.data 버퍼는 스트림 재조립 설정에 영향을 받습니다. stream.reassembly.depth가 충분히 크지 않으면 짧은 본문은 문제없지만, 경계 문자열이 분할된 경우 탐지율이 떨어질 수 있습니다.

2.3 Zeek / Corelight 상관 분석

event http_entity_data(c: connection, is_orig: bool, length: count, data: string) {
    if (is_orig && /PHP_SESSION_UPLOAD_PROGRESS/ in data) {
        NOTICE([$note=Notice::ACTION_LOG,
                $msg="PHP2Shell multipart progress field observed",
                $conn=c]);
    }
}

3. 호스트 런타임: Falco 규칙

PHP-FPM 워커가 정상적으로 셸을 실행하는 경우는 드뭅니다. 다음 규칙은 RCE 체인의 마지막 단계(execve)를 포착합니다.

# /etc/falco/falco_rules.local.yaml
- rule: PHP-FPM Child Spawns Shell or Downloader
  desc: php-fpm 워커가 셸/다운로더를 실행 (PHP2Shell RCE 의심)
  condition: >
    spawned_process and
    proc.pname in (php-fpm, php-fpm8.1, php-fpm8.2, php-fpm8.3, php-fpm8.4, php-fpm8.5) and
    proc.name in (sh, bash, dash, zsh, curl, wget, nc, ncat, socat)
  output: >
    "PHP-FPM spawned suspicious process (user=%user.name cmd=%proc.cmdline
     parent=%proc.pname pid=%proc.pid)"
  priority: CRITICAL
  tags: [process, mitre_execution, php2shell]

- rule: PHP-FPM Outbound Connection to Unusual Port
  desc: php-fpm 워커의 비정상 아웃바운드 연결
  condition: >
    outbound and
    proc.pname in (php-fpm, php-fpm8.3, php-fpm8.4, php-fpm8.5) and
    not fd.sport in (80, 443) and
    not fd.rport in (80, 443, 3306, 6379, 5432)
  output: >
    "PHP-FPM outbound connection (cmd=%proc.cmdline dport=%fd.rport)"
  priority: WARNING
  tags: [network, mitre_exfiltration]
sudo falcoctl artifact install falco-rules 2>/dev/null || true
sudo systemctl restart falco
sudo journalctl -u falco -f | grep -i php2shell

컨테이너 환경에서는 container.name과 k8s.ns.name을 조건에 추가하여 워크로드 단위로 경보를 분리하십시오. 정상 배치 작업이 curl을 사용하는 경우, proc.cmdline 화이트리스트로 예외를 관리해야 합니다.


4. 커널 계층: Linux Auditd

# www-data / nginx UID 아래에서 생성되는 프로세스 추적
sudo auditctl -a always,exit -F arch=b64 -S execve \
  -F auid=-1 -F euid=33 -k php2shell_exec

# 의심 다운로드/셸 실행 추적
sudo auditctl -a always,exit -F arch=b64 -S connect -F euid=33 -k php2shell_net

# 임시 디렉터리 내 실행 파일 생성 추적
sudo auditctl -w /tmp -p x -k php2shell_tmp_exec
# 영구 규칙
sudo tee /etc/audit/rules.d/php2shell.rules >/dev/null <<'EOF'
-a always,exit -F arch=b64 -S execve -F euid=33 -k php2shell_exec
-a always,exit -F arch=b64 -S connect -F euid=33 -k php2shell_net
-w /tmp -p x -k php2shell_tmp_exec
EOF

sudo augenrules --load
sudo auditctl -l | grep php2shell
# 사후 조사
sudo ausearch -k php2shell_exec -ts recent --format text | head -50
sudo aureport -k -i --summary

auditd는 예방이 아니라 기록 계층입니다. 경보 임계값과 로그 보존 기간을 함께 설계하고, 중앙 SIEM으로 전송하여 로컬 삭제에 대비하십시오.


5. 경보 상관 분석과 운영 절차

단일 신호는 오탐이 많습니다. 다음 조합을 동일 소스 IP / 동일 워커 기준으로 상관 분석하십시오.

  1. 60초 내 5회 이상 PHP_SESSION_UPLOAD_PROGRESS 포함 multipart POST
  2. 직후 php-fpm 워커의 SIGSEGV/SIGABRT 또는 비정상 재시작
  3. www-data 권한의 sh, curl, wget 실행
  4. 비표준 포트로의 아웃바운드 연결
# 조사용 원라이너
sudo ausearch -k php2shell_exec -ts recent | \
  aureport -f -i | head -20
journalctl -u php8.3-fpm --since "10 min ago" | grep -E 'segfault|SIGSEGV'

대응 순서

  1. 해당 IP를 경계에서 차단하고, 동일 세션 ID의 요청을 무효화합니다.
  2. 워커를 재시작하고 코어 덤프/메모리 스냅샷을 보존합니다.
  3. 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 이상으로 업그레이드하기 전까지 session.upload_progress.enabled=Off를 적용합니다.
  4. 아웃바운드 차단과 자격 증명 회전을 수행합니다.

6. FAQ

Q. Suricata 규칙만으로 충분한가요? 아니요. 멀티파트 인코딩과 경계 분할로 우회될 수 있습니다. Falco/auditd와 패치가 반드시 병행되어야 합니다.

Q. Falco가 컨테이너에서 동작하지 않습니다. 호스트 커널에 eBPF 또는 커널 모듈 드라이버가 로드되어야 하며, privileged 또는 적절한 capability가 필요합니다. 관리형 Kubernetes라면 클라우드 제공자의 런타임 보안 서비스를 대체재로 사용하십시오.

Q. auditd 로그가 너무 많아집니다. euid=33과 execve로 범위를 좁히고, -F success=1을 추가하십시오. 로그는 SIEM으로 전송하고 로컬 보존은 7~14일로 제한하는 것이 일반적입니다.