커널 LPE와 결합된 맞춤형 PHP2Shell 익스플로잇을 이용해 야생 방화벽(Firewalls)에 루트 권한 탈취
Dream 연구원들은 10월 25일, 미국 정부 관련 환경의 시스템을 포함하여 총 25개 서버를 장악한 것으로 보이는 중국 AI 지원 캠페인의 흔적을 발견했습니다. 공격자는 PHP2Shell과 커널 권한 상승(kernel privilege-escalation) 익스플로잇을 결합하여 노출된 시스템들을 손상시켰습니다.
영향을 받은 장치들은 특정 대형 벤더의 방화벽 소프트웨어 버전을 실행하고 있습니다. 보안상의 이유로, 저희는 해당 벤더나 영향을 받은 버전을 공개하지 않으며, 해당 벤더에게 통보했습니다.
이후 공격자는 Active Directory를 대상으로 공격을 감행했습니다. 회수된 아티팩트들은 자격 증명 추출(credential extraction)과 디렉터리 열거(directory enumeration)가 이루어졌음을 확인시켜 주었습니다.
이번 작전은 노출된 관리 인터페이스가 어떻게 조직의 ID 인프라로 진입하는 경로가 될 수 있는지 보여줍니다. 정부 방어자들에게 있어 취약점 발견은 시작에 불과하며, 실제 도전 과제는 취약한 시스템을 식별하는 것에서 나아가, 손상으로 인해 가능해진 접근을 이해하고 봉쇄하는 데까지 확장됩니다.
아래 연구는 Dream의 자율 사이버 리서치 제품인 Hero를 사용한 선제적 위협 헌팅(proactive threat hunting)을 통해 수행되었습니다.
발견 (Discovery)
Dream 연구원들은 PHP2Shell 익스플로잇 체인의 맞춤형 무기화(custom weaponization) (CVE-2026-109618/CVE-2026-109619)와 Fragnesia 커널 권한 상승(kernel privilege escalation) (CVE-2026-46300)이 결합된 완전한 공격 작전을 식별했습니다. 여기에는 타겟 목록, 익스플로잇 로그, 그리고 약 40개의 맞춤형 사후 악용 스크립트가 포함되어 있습니다.
이는 미국, 일본, 대만, 대한민국 전역에 걸쳐 25개 서버를 장악한 캠페인의 아티팩트를 포함했습니다. 루트 권한을 탈취한 후, 공격자는 미국 정부 관련 환경의 세 가지 특정 서버에 집중했습니다.
발견된 아티팩트들은 실험실 환경에서의 익스플로잇 개발로 시작하여, 미국 정부 조직의 내부 Active Directory 도메인까지 지속적인 접근(persistent access)으로 끝난 작전을 보여주었습니다.
공격에서의 AI 사용 (AI Usage in Attack)
또한 공격 인프라에서 AI 지원 도구의 증거도 발견했습니다:
- Claude Code가 설치된 것이 확인되었습니다.
- aiscan이라는 AI 기반 공격 보안 플랫폼이 중국 LLM인 DeepSeek 및 Kimi로 구성되어 있었습니다.
- 중국 정찰 플랫폼 FOFA용 API 키가 존재했습니다.
AI 도구가 익스플로잇이나 사후 악용 스크립트를 작성하는 데 구체적으로 사용되었다는 직접적인 증거는 없지만, 코드 구조, 주석 스타일, 약 40개 스크립트에 걸친 일관성은 AI 지원 개발을 시사합니다. 다만, 이 작전은 완전히 자율적인 국가 차원의 공격으로 보이지 않습니다.
익스플로잇: 두 개의 취약점을 하나의 요청에 체인 연결
공격자는 별개의 두 가지 익스플로잇을 개발하여 이를 체인 연결함으로써, 특정 버전의 대형 벤더 방화벽 소프트웨어를 실행하는 모든 방화벽에서 자격 증명 없이 단일 HTTP 요청만으로 루트 접근 권한을 획득했습니다.
PHP2Shell (CVE-2026-109618 / CVE-2026-109619) — 원격 코드 실행(RCE)
우리가 복구한 구현체는 알려진 공개 익스플로잇의 사본이 아닌, 맞춤형 비공개 빌드입니다. 이는 동일한 근본 개념을 따르지만, 독립적으로 개발되었으며 목표 방화벽 플랫폼에 맞춰 특별히 조정된 것으로 보입니다.
이 취약점은 방화벽의 앱웹 관리 서버에서 PHP의 session.upload_progress 메커니즘을 표적으로 합니다. 해당 방화벽 OS는 PHP 8.0.30을 사용하며, 높은 메모리 제한으로 인해 공격의 메모리 손상 단계를 가능하게 합니다.
이 공격은 네 가지 체계적인 단계로 작동합니다:
A1 — 메모리 고갈 (~383MB): 공격자는 대용량 파일 이름과 세션 진행 데이터를 포함하는 조작된 multipart POST 요청으로 서버를 범람시킵니다. 이는 PHP의 메모리 할당자를 한계까지 밀어붙여 시스템을 예측 가능한 메모리 레이아웃 상태로 강제합니다.B1 — 정보 유출: 두 번째 요청은 A1에서 얻은 만료된 세션 ID 길이를 위조하여, 경계를 벗어난 읽기(out-of-bounds read)를 강제합니다. 서버의 Set-Cookie
응답 헤더는 유출된 포인터 값을 담고 있어 공격자에게 실행 중인 프로세스 내 libphp.so의 베이스 주소를 제공합니다.
A2 — 스테일 레퍼런스 설정 (~500MB): 메모리 압박의 또 다른 라운드가 세션을 초기화하면서 http_session_vars를 살아있는 zend_reference로 남기고, 이후 두 번째 OOM(Out-of-Memory)을 유발하여 mod_data=NULL 상태를 만듭니다. 이는 공격자가 B2에서 위조할 수 있는 스테일 레퍼런스를 생성합니다.
B2 — 코드 실행: 마지막 요청은 해제된 메모리 영역에 PHP HashTable 항목을 위조합니다. 이 위조된 항목의 pDestructor 콜백은 zif_system (PHP의 system() 함수)를 가리키고, 데이터는 셸 명령어 문자열을 가리킵니다. PHP의 정리 핸들러가 HashTable을 순회할 때, 위조된 소멸자(destructor)를 호출하여 nobody 사용자로 임의 명령어를 실행합니다.
Fragnesia (CVE-2026-46300) — 커널 권한 상승(Kernel Privilege Escalation)
nobody로 코드 실행을 얻는 것만으로는 충분하지 않습니다. SELinux가 httpd_t의 시스템 파일 수정에 제약을 가하기 때문입니다. root 권한을 확보하기 위해 공격자는 두 번째 익스플로잇, 즉 XFRM ESP-in-TCP 서브시스템의 리눅스 커널 취약점을 체인으로 연결했습니다.
Fragnesia는 페이지 캐시 쓰기(page-cache writes)를 사용하여 /etc/pam.d/su 파일을 덮어쓰고, 인증 규칙을 pam_permit.so (모두 허용)로 대체합니다. PAM 파일이 단지 774바이트에 불과하기 때문에, 이 익스플로잇은 이를 정확하게 쓰기 위해 바이트별 스플라이싱 변형(per-byte splicing variant)을 사용합니다. 덮어쓰기가 완료된 후에는 비밀번호 없이 su root가 성공합니다.
두 익스플로잇 모두 단일 B2 HTTP 요청으로 실행됩니다. 인증되지 않은(unauthenticated) HTTPS 연결부터 영구적인 root 웹셸까지의 전체 체인은 15분 이내에 완료됩니다.
실험실: 대규모 버전 프로파일링 (Version Profiling at Scale)
이 익스플로잇은 단일의 '발사하고 잊는(fire-and-forget)' 페이로드가 아닙니다. 이는 방화벽 OS 버전마다 다른 정밀한 메모리 오프셋을 요구합니다. 여기에는 zval_ptr_dtor의 주소, zif_system의 주소, HashTable 구조체 크기, 그리고 각 단계 동안 힙 레이아웃을 제어하는 메모리 기하학 매개변수들이 포함됩니다.
회수된 아티팩트 중, 테스트되었던 각 방화벽 OS 버전에 대한 11개의 JSON 버전 프로파일 파일을 발견했습니다.
각 프로파일에는 해당 버전의 libphp.so 바이너리에서 추출된 정확한 오프셋(offsets)이 포함되어 있으며, 여기에는 각 라이브러리의 SHA256 해시 값, ELF 빌드 ID, 그리고 실험실 테스트를 통해 조정된 정밀 메모리 지오메트리 매개변수(청크 크기, 채우기 횟수(fill counts), 배수 횟수(drain counts), 스프레이 오프셋(spray offsets))가 포함되어 있습니다.
'보정됨(calibrated)' 프로파일과 '임시적(provisional)' 프로파일 사이의 차이점은 주목할 만합니다. 보정됨이란 공격자가 해당 버전의 실행 중인 가상 머신(VM)에 대해 익스플로잇을 테스트하고 메모리 지오메트리가 작동함을 확인했다는 의미입니다. 임시적이란 바이너리에서 오프셋을 계산했지만, 라이브 인스턴스에서 검증하지 않았다는 의미입니다.
캠페인 전개 과정: 19,000개 발견 목표부터 25개 서버 장악까지
Operational timeline in ten steps across setup, exploitation, pivot and post-exploitation, from October 8 to October 24.
그림 1: 공격 캠페인의 운영 타임라인으로, 10월 8일 설정 단계부터 익스플로잇 실행, 내부 네트워크 피벗(pivot) 및 10월 24일 사후 활동까지를 보여줍니다. 익스플로잇이 준비되고 9개의 취약 버전 프로파일을 확보한 후, 공격자는 명확한 깔때기(funnel) 형태의 표적화 캠페인을 시작했습니다:
단계 1: 글로벌 발견 (19,000개 목표)
FOFA — Shodan과 유사한 중국 인터넷 자산 검색 엔진을 사용하여, 공격자는 전 세계적으로 해당 공급업체의 인터넷에 노출된 방화벽 약 19,000개를 식별했습니다. 이 장치들은 관리 인터페이스가 인터넷에 노출되어 있으며, 인증서 지문(certificate fingerprints) 및 HTTP 응답 시그니처를 통해 발견할 수 있었습니다.
단계 2: 버전 지문 인식 (5,000개 활성)
맞춤형 버전 스캐너가 19,000개 목표 전체에 대해 200개의 동시 스레드(concurrent threads)로 실행되어, 실제로 온라인 상태인 장치와 해당 장치가 실행 중인 OS 버전을 식별했습니다. 약 5,000개가 버전 정보를 응답했습니다.
단계 3: 지리적 및 버전 필터링 (240개 목표)
5,000개의 활성 장치 중 공격자는 미국, 일본, 대만, 한국에 위치한 서버로 필터링했습니다. 이후 익스플로잇 프로파일과 일치하는 버전(PHP 8.0.30을 실행하는 최신 릴리스 브랜치)으로 추가 필터링했습니다. 이를 통해 최종 목표 목록인 방화벽 240개가 생성되었습니다.
미국, 일본, 대만, 한국은 태평양 지역에서 중국 국가와 연계된 사이버 작전의 네 가지 주요 전략적 적대국이자 정보 목표이기 때문에, 이는 잠재적인 국가 차원의 적대 세력의 노력을 시사합니다.
단계 4: 자동 익스플로잇 (24개 루트 권한 확보)
240개의 목표는 자동 배치 러너(automated batch runner)에 입력되었고, 이 러너는 타겟당 1800초(30분)의 시간 초과를 설정하여 한 번에 다섯 개씩 익스플로잇을 수행했습니다. 성공할 때마다 배치는 다음 작업을 수행했습니다:
- 전체 PHP2Shell + Fragnesia 체인을 실행했습니다.
- 방화벽의 공개적으로 접근 가능한 디렉토리에 103바이트 크기의 PHP 루트 웹쉘(webshell)을 심었습니다.
- 웹쉘을 통해
id 명령을 실행하여 root 권한을 검증했습니다.
- 목표의 호스트 이름과 IP를 성공 CSV 파일에 기록했습니다.
- 다음 목표로 이동했습니다.
이 시점에서 방화벽 24개는 성공적으로 루트 접근 권한이 확보되었습니다.
단계 5: 분류 (2개 방화벽 우선순위 지정)
루트 권한이 확보된 24개의 방화벽 중 22개에 대해서는 공격자가 웹쉘을 심는 것 외에는 아무것도 하지 않았습니다. 지속성(persistence) 확보도, 터널링(tunnels)도, 포스트 익스플로잇 도구 사용도 없었습니다. 이 서버들은 사실상 장악되어 주차(owned and parked) 상태였으며, 공격자가 언제든지 웹쉘을 통해 돌아올 수 있도록 했습니다.
하나의 목표와 관련이 있는 것으로 보이는 두 대의 서버는 이 단계를 넘어섰습니다. 두 서버 모두 미국 정부와 연계된 환경에 속한 것으로 보였습니다.
단계 6: 피벗(The Pivot) (방화벽에서 도메인으로 전환)
24개의 루트 권한이 있는 방화벽 중 두 개는 미국 정부 관련 기관 소속이었다. 첫 번째 방화벽에서 추출된 실행 구성(running configuration)은 다음과 같은 특징을 보여 정부 소속임을 시사하는 환경이었다: CAC (Common Access Card) 인증, 특정 명명 접두사, 그리고 표준화된 정부 스타일 자산 명명. 이 구성 파일에는 조직의 전체 내부 네트워크 토폴로지(internal network topology)가 포함되어 있었다: 도메인 컨트롤러(domain controllers), 중앙 방화벽 관리 서버, Keycloak SSO 서버, GitLab 인스턴스, VPN 게이트웨이, 그리고 Active Directory 도메인.
첫 번째 방화벽은 자동 배치(automated batch) 중 UTC 기준 10월 23일 18:14에 루트 권한을 얻었다. 그러나 이 방화벽은 직후 — 아마도 약 900MB의 익스플로잇 트래픽 때문이었을 — 다운되었고 다시 온라인 상태가 되지 않았다. 공격자는 두 시간 동안 240개의 자동 프로브(automated probes)를 사용하여 이를 감시하며 복구를 기다렸다. 하지만 돌아오지 않았다. 대상 조직과 확실하게 연결되는 인증서 데이터를 가진 두 번째 방화벽은 원래 배치 과정에서 타임아웃되었다.
공격자는 첫 번째 방화벽의 구성에서 조직의 가치를 인식하고 별도의 전용 캠페인을 만들어 UTC 기준 10월 24일 10:09에 성공적으로 이를 악용했다.
몇 시간 만에 공격자는:
내부 네트워크로 영구적인 터널을 구축했다.
8포트 리버스 SSH 터널(reverse SSH tunnel)이 침해된 방화벽과 운영자 인프라를 연결하여 다음 접근 권한을 포워딩했다:
- 도메인 컨트롤러 (LDAP, LDAPS, RDP)
- 내부 방화벽 관리 서버
- VPN 인프라
- 임의의 내부 네트워크 액세스를 위한 SOCKS5 프록시.
이 터널은 systemd 서비스와 rc.local 부트 훅(boot hook)으로 감싸져 있어 재부팅에도 살아남고 자동으로 재연결된다.
터널을 통해 관리 서버 장악. UTC 기준 11시 35분, 두 번째 방화벽을 루트 권한으로 확보한 지 90분도 채 되지 않아 공격자는 새로 얻은 서버 접근 권한과 동일한 PHP2Shell + Fragnesia 체인을 사용하여 세 번째 정부 서버—즉, 터널을 통해서만 접근 가능한 내부 방화벽 관리 서버—를 대상으로 삼았다. 이 서버는 조직의 모든 방화벽을 중앙에서 관리한다. 이를 손상시킴으로써 공격자는 관리되는 모든 방화벽의 구성(configuration), 자격 증명(credentials), 네트워크 설정에 접근할 수 있게 되었다. 이로써 총 장악된 서버 수는 25개에 달했다.
관리 서버에서 AD 공격 캠페인 시작. 관리 서버가 이미 Active Directory (AD)에 대한 LDAP 연결성을 가지고 있었기 때문에(VPN 사용자 인증을 위해 필요함), 공격자는 이를 도메인 공격의 발사대(launch platform)로 사용했다:
- LDAP 서비스 계정 비밀번호를 복호화했다. — 장치 구성에는 암호화되어 저장되어 있지만, 루트 접근 권한을 가진 공격자는 마스터 키(master key)를 가지고 있다. 그들은 이를 평문(plaintext)으로 추출하여 이후 모든 스크립트에 하드코딩(hardcoded)했다.
- LDAP 쿼리(queries)를 통해 도메인의 모든 사용자 및 그룹을 열거(enumerate)했다.
- 조직 테마의 비밀번호를 사용하여 모든 사용자 계정에 대해 패스워드 스프레이(password spraying) 공격을 감행했다. 해당 도메인은
lockoutThreshold=0이었다.