☰
jena.run
♡

패치 다 했는데 VPN이 털렸다

Ivanti Connect Secure 3-Zero-Day Chain

패치 다 했는데 VPN이 털렸다

— Ivanti Connect Secure 3-Zero-Day Chain

보안팀에서 꽤 듣기 싫은 말들이 있다.

“인터넷에 직접 노출된 장비입니다.”

별로다.

“인증 없이 공격 가능합니다.”

더 별로다.

“현재 알려진 패치는 전부 적용돼 있었습니다.”

슬슬 머리가 아프다.

“root까지 올라갔고 로그도 지웠습니다.”

퇴근은 글렀다.

2026년 10월 실제로 거의 이 조건을 전부 만족하는 사건이 발견됐다.

대상은 Ivanti Connect Secure, 기업에서 원격접속용 SSL VPN으로 많이 사용하는 보안 장비다.

Volexity가 10월 초 침해사고를 조사하던 중, 당시 최신 패치가 적용된 Ivanti Connect Secure 장비가 정체불명의 공격자에게 완전히 장악된 사실을 발견했다.

조사 결과 단일 취약점이 아니었다.

인증 우회 → 원격 코드 실행 → 권한 상승

세 단계가 연결된 Zero-Day Chain이었다.

최종 결과는 상당히 심플했다.

Internet
   |
   v
Ivanti Connect Secure
   |
   v
Authentication Bypass
   |
   v
Remote Code Execution
   |
   v
Privilege Escalation
   |
   v
ROOT

사용자 계정도 필요 없었다.

VPN 계정도 필요 없었다.

그리고 당시 장비는 fully patched 상태였다. Volexity는 조사 대상 장비가 당시 최신 버전이었던 22.8R2.4였다고 밝혔다.

개인적으로 최근 나온 보안 이슈 중 기업 환경에서 가장 골치 아픈 사건 하나를 고르라면 이걸 고를 것 같다.

이유는 단순하다.

VPN은 원래 회사 내부로 들어가기 위한 문이기 때문이다.


CVE 하나짜리 사건이 아니다

이번 공격 체인을 구성한 핵심 취약점은 현재 세 개의 CVE로 정리돼 있다.

CVE-2026-104935

그리고 이후 추가로 공개된

CVE-2026-111357

CVE-2026-118527

이다.

처음 공개됐던 CVE-2026-104935는 인증 우회와 원격 코드 실행 경로의 핵심 역할을 했다.

이후 공격자가 함께 사용하던 권한 상승 취약점과 추가 파일 업로드 관련 문제가 각각 별도 CVE로 정리됐다. 10월 29일 Ivanti가 추가 패치를 내놓으면서 전체 공격 체인을 막는 수정이 완료됐고, CISA 역시 추가 두 취약점을 Known Exploited Vulnerabilities, 즉 KEV 목록에 올렸다.

중요한 점은 CVSS 숫자가 아니다.

공격 흐름을 보는 게 훨씬 중요하다.

CVE-2026-104935
        |
        v
Authentication Bypass + RCE
        |
        v
CVE-2026-111357
        |
        v
Privilege Escalation
        |
        v
Root Access

여기에 공격 과정에서 사용된 추가 파일 처리 문제가 결합됐다.

하나씩 따로 보면 취약점이다.

연결하면 인터넷에서 VPN 장비의 root까지 가는 attack path가 된다.

이게 Chain의 무서운 점이다.


시작부터 꽤 이상했다

2026년 10월 5일.

Volexity가 고객 환경에서 의심스러운 네트워크 활동을 조사하기 시작한다.

Root Cause를 따라가다 보니 Ivanti Connect Secure VPN 장비가 나온다.

여기서 보통 제일 먼저 하는 게 있다.

패치 안 했나?

확인한다.

그런데 패치돼 있다.

당시 최신 버전.

22.8R2.4
Build 19097

였다.

이 순간부터 보안팀 입장에서는 사건 성격이 달라진다.

패치가 안 된 서버가 털렸으면 일단 원인은 설명된다.

Known CVE
   |
   v
Missing Patch
   |
   v
Compromise

물론 이것도 큰일이다.

그런데 최신 패치 상태에서 털렸다면?

Fully Patched
     |
     v
Compromise
     |
     v
Unknown Exploit

이제 Zero-Day 가능성을 봐야 한다.

실제로 Volexity가 확인한 공격 활동은 적어도 10월 3일부터 이어지고 있었다. Ivanti가 공개적으로 취약점을 발표한 건 10월 13일이었다.


첫 번째 — 인증을 안 했는데 인증된 영역으로 들어갔다

첫 단계는 authentication bypass였다.

Ivanti Connect Secure에는 외부에서 접근 가능한 로그인 관련 CGI와, 인증된 사용자나 내부 로직만 접근해야 하는 CGI가 존재한다.

정상적인 구조라면 당연히 이래야 한다.

Public Request
      |
      v
Authentication
      |
      v
Protected Handler

그런데 요청을 처리하는 frontend와 실제 요청 목적지를 결정하는 backend 사이의 판단이 달랐다.

한쪽에서는

“Public URL이네.”

라고 판단하는데,

다른 쪽에서는 요청에 포함된 정보를 기반으로 실제로 보호되어야 하는 다른 CGI handler로 전달할 수 있었다.

쉽게 말하면 이렇다.

경비원에게는

“로비 가는 사람입니다.”

라고 보여주고,

건물 안에서는 엘리베이터가 서버실로 데려다주는 식이다.

Security Check
     |
     | "Public"
     v
Request Router
     |
     | "Protected"
     v
Internal Handler

같은 요청을 서로 다른 컴포넌트가 서로 다른 기준으로 신뢰하고 있었다.

개인적으로 이런 취약점이 꽤 재미있다.

엄청난 암호학 공격도 아니고,

메모리 corruption도 아니다.

두 시스템이 같은 값을 다르게 해석한다.

그 사이에 trust boundary가 무너진다.


두 번째 — 업로드한 파일이 지워지기 전에 실행한다

그다음 단계는 더 재밌다.

공격자는 특정 파일 업로드 처리 로직을 이용했다.

서버는 업로드된 파일을 일단 disk에 기록한다.

그다음 파일이 정상적인 ZIP인지 검사한다.

정상이 아니면 삭제한다.

구조 자체만 보면 이상하지 않다.

Upload
   |
   v
Write File
   |
   v
Validate
   |
   +--> Valid   -> Continue
   |
   +--> Invalid -> Delete

그런데 문제가 있다.

검사 전에 파일이 이미 disk에 존재한다.

아주 짧은 시간이지만 존재한다.

공격자는 이 틈을 노렸다.

Write
  |
  |<---- Race Window ---->|
  |
Validate
  |
Delete

파일이 생성된 직후 삭제되기 전에 다른 요청을 동시에 보내 실행시키는 것이다.

전형적인 Race Condition이다.

Volexity 분석에 따르면 공격자는 여러 실행 요청을 빠르게 보내 이 짧은 window에서 payload가 실행될 확률을 높였다.

여기서 재미있는 건 서버 입장이다.

서버는 나름대로 검증을 하고 있다.

잘못된 파일도 결국 삭제한다.

그런데 검증 순서가 잘못됐다.

Write -> Validate

가 아니라 가능했다면

Validate -> Trust -> Execute

라는 경계가 더 명확해야 했다.

보안에서 순서 하나가 이렇게 중요하다.


그런데 아직 root는 아니다

여기까지 성공해도 공격자가 얻는 건 제한된 cgi-server 사용자 권한이었다.

그래서 공격자는 세 번째 취약점을 연결했다.

Privilege Escalation.

Ivanti 장비에는 dsinvoked라는 privileged background service가 있었고, maintenance task를 root 권한으로 실행하는 역할을 한다.

공격자는 이 서비스가 작업 정보를 신뢰하는 구조를 악용했다.

그리고 이미 확보한 code execution을 이용해 메모리상의 정보를 조작하고,

root service에게 사실상

이 프로그램 실행해줘.

라고 시킨다.

결과.

cgi-server
    |
    v
Trusted Broker
    |
    v
Root Task
    |
    v
ROOT

공격 코드는 실제로 /etc/shadow를 읽을 수 있는지 확인해 root 권한 획득 여부까지 체크했다.

여기까지 오면 VPN 장비는 사실상 공격자 장비다.


근데 VPN 하나 털린 게 그렇게 큰일인가?

엄청 큰일이다.

VPN은 보통 이런 위치에 있다.

Internet
   |
   v
VPN Gateway
   |
   v
Internal Network
   |
   +-- Active Directory
   |
   +-- Servers
   |
   +-- Admin Systems
   |
   +-- Internal Applications

외부와 내부의 경계다.

그리고 인증을 위해 내부 시스템과 통신한다.

LDAP.

Active Directory.

RADIUS.

각종 Identity Provider.

즉 VPN은 단순히 인터넷 앞에 있는 장비가 아니다.

내부 네트워크와 인증 인프라를 모두 볼 수 있는 매우 좋은 위치에 있다.

공격자도 당연히 그걸 알고 있었다.


Root를 딴 다음 뭘 했나

UTA0576이라는 이름으로 추적된 공격자는 장비를 장악한 뒤 바로 내부 자격증명을 찾기 시작했다.

Volexity 조사에서는 공격자가 VPN 장비에 저장된 LDAP 관련 domain credential을 추출하고, Active Directory metadata를 가져가고, Kerberos 관련 도구를 사용하고, password spraying까지 진행한 사실이 확인됐다.

특히 꽤 놀라운 타깃이 있었다.

LAPS Password

그리고

BitLocker Recovery Key

였다.

VPN Root
   |
   v
AD Service Account
   |
   v
LDAP
   |
   +-- LAPS Passwords
   |
   +-- BitLocker Recovery Keys
   |
   +-- Account Metadata
   |
   +-- Password Attributes

LAPS는 각 Windows 장비의 Local Administrator password를 안전하게 관리하기 위한 기능이다.

그런데 적절한 AD 권한을 가지고 있는 계정이 털리면 공격자가 그 정보를 조회할 수 있다.

그 순간 VPN 장비 하나의 침해가 endpoint 침해로 확장될 가능성이 생긴다.

VPN
 |
 v
Credential
 |
 v
Endpoint
 |
 v
Lateral Movement

이게 enterprise 환경에서 가장 무서운 부분이다.

처음 뚫린 장비 자체보다 그 장비가 가지고 있는 Trust가 더 위험하다.


BitLocker Key를 왜 찾았을까?

이것도 꽤 흥미롭다.

BitLocker Recovery Key는 원래 장애가 발생했을 때 암호화된 Windows 장비를 복구하기 위해 사용한다.

기업에서는 이런 key를 Active Directory나 Entra ID 등에 저장하기도 한다.

공격자가 해당 recovery key를 얻을 수 있다면,

특정 조건에서 물리적 또는 관리 수준의 접근과 결합됐을 때 암호화된 디스크 접근 위험까지 커진다.

즉 공격자가 단순히

VPN 계정 좀 훔쳐보자.

수준에서 움직인 게 아니다.

조직 전체 Identity Infrastructure를 보고 있었다.


그리고 패킷도 봤다

공격자는 VPN 장비의 네트워크 위치도 활용했다.

Volexity는 공격자가 packet capture 데이터를 조사하면서 LDAP traffic에서 credential을 찾으려 한 정황도 확인했다.

VPN은 네트워크의 아주 좋은 choke point다.

Remote User
     |
     v
VPN Gateway
     |
     v
Internal Services

여기서 root 권한으로 packet을 볼 수 있다면,

어떤 protocol이 암호화되지 않았는지,

어떤 credential material이 이동하는지,

내부 서버가 무엇인지

상당한 정보를 얻을 수 있다.

보안 장비가 뚫렸을 때 일반 서버보다 위험한 이유 중 하나다.

보안 장비는 원래 남의 traffic을 볼 권한을 가지고 있다.


이 공격이 특히 골치 아픈 이유 — Web Shell이 없었다

보통 VPN 장비가 털렸다고 하면 이런 걸 찾는다.

Web Shell
Backdoor
Malware
Persistence

그런데 이번 공격자는 특이하게 움직였다.

Volexity가 분석한 공격에서는 첫 단계 malware나 web shell이 발견되지 않았다.

공격자는 명령을 실행하고 싶을 때마다 exploit chain을 다시 실행했다.

Exploit
  |
  v
Run Command
  |
  v
Return Output
  |
  v
Clean Up

그리고 다음 명령이 필요하면?

다시 exploit.

Exploit
  |
  v
Run Command

별도의 C2 implant를 설치할 필요가 없었다.

명령 결과 역시 exploit에 사용되는 HTTPS response를 통해 전달됐다.

공격자가 굳이

Victim -> C2 Server

라는 새로운 네트워크 패턴을 만들지 않는 것이다.

외부에서 보면 계속 VPN HTTPS traffic처럼 보일 수 있다.

꽤 영리하다.


로그도 지웠다

공격자는 흔적까지 없앴다.

Ivanti runtime log와 Linux audit log에서 공격 시간대의 기록을 선택적으로 제거하는 기능이 확인됐다.

중요한 건 무식하게

rm /var/log/*

같은 짓을 한 게 아니라는 점이다.

그렇게 하면 오히려 더 티가 난다.

로그가 왜 텅 비었지?

바로 Incident다.

대신 공격자는 특정 시간대의 공격 흔적만 제거하고 기존 로그는 남기는 방식을 사용했다.

Normal Logs
Normal Logs
Attack Logs      <- Remove
Attack Logs      <- Remove
Normal Logs
Normal Logs

결과는

Normal Logs
Normal Logs
Normal Logs
Normal Logs

처럼 보인다.

Forensics 하는 사람 입장에서 진짜 싫다.


Integrity Checker도 못 찾았다

이 부분이 개인적으로 이번 사건에서 제일 재미있었다.

Ivanti에는 장비 변조 여부를 확인하기 위한 Integrity Checker Tool이 있다.

그런데 이번 공격은 이 도구에서도 잡히지 않을 수 있었다.

왜?

공격자가 주요 조작을 memory에서 수행했기 때문이다.

Privilege Escalation 과정에서 library의 in-memory copy를 수정하고,

공격이 끝나면 다시 원래 상태로 돌려놨다.

disk에 존재하는 원본 library는 건드리지 않는다.

Disk
 |
 +--> Clean

Memory
 |
 +--> Patched
      |
      v
   Exploit
      |
      v
   Restore

Integrity Checker가 disk modification을 본다면?

Disk: Clean

이라고 나온다.

하지만 장비는 이미 털렸을 수 있다.

Volexity 역시 이 공격의 경우 Integrity Checker만으로는 compromise를 확인할 수 없으며, memory와 network forensic analysis가 필요하다고 설명했다.

이건 상당히 중요한 이야기다.


“검사 결과 Clean”은 “침해되지 않음”이 아니다

Incident Response에서 자주 하는 착각이 있다.

Scanner: Clean

그러면

System: Safe

라고 생각하는 것이다.

둘은 같은 말이 아니다.

No Evidence Found
        !=
No Compromise

이번 사건이 딱 그 예시다.

파일이 없다.

Web Shell도 없다.

Integrity Check도 정상이다.

로그도 일부 지워졌다.

그런데 공격자는 root였다.

그래서 Incident Response에서는 항상

내가 보고 있는 Evidence Source 자체가 신뢰 가능한가?

를 생각해야 한다.

root를 가진 공격자가 있는 시스템에서 그 시스템의 로그를 100% 믿으면 안 된다.


패치했는데 또 패치해야 했다

운영팀 입장에서 특히 화나는 부분이다.

10월 13일 Ivanti는 CVE-2026-104935에 대한 패치를 공개했다.

보안팀은 긴급 작업을 잡는다.

VPN 장비라 maintenance window 잡기도 쉽지 않다.

사용자 공지하고,

HA 확인하고,

backup 확인하고,

업데이트한다.

끝.

...인 줄 알았다.

그런데 공격 체인에 사용된 나머지 취약점에 대한 수정이 추가로 나오면서 앞서 패치했던 일부 환경도 다시 업데이트해야 했다.

SecurityWeek에 따르면 10월 29일 공개된 추가 수정은 CVE-2026-111357과 CVE-2026-118527을 해결했고, Ivanti는 최신 release가 실제 공격에서 사용된 전체 vulnerability chain을 해결한다고 밝혔다.

운영 보안을 해본 사람이라면 이게 왜 고통스러운지 안다.

Emergency Patch
      |
      v
Change Request
      |
      v
Maintenance
      |
      v
Validation
      |
      v
Done

하고 잤는데

New Advisory
      |
      v
Patch Again

이다.


그래서 CVSS만 보면 안 된다

CVE-2026-104935는 CVSS 8점대였다.

숫자만 보면 9.8이나 10.0짜리 CVE보다 덜 위험해 보일 수도 있다.

그런데 현실에서는

Actively Exploited
        +
Internet-facing VPN
        +
No Authentication
        +
Root Privilege
        +
Credential Access
        +
Anti-forensics

가 붙어 있었다.

내가 Vulnerability Management에서 꽤 중요하다고 생각하는 부분이다.

CVSS
 !=
Risk

실제 우선순위에는

Exploitability
Exposure
Asset Criticality
Active Exploitation
Privileges Obtained
Business Context

같은 요소가 같이 들어가야 한다.

인터넷에 노출된 VPN의 CVSS 8.8 Zero-Day가,

내부 테스트 서버에 있는 CVSS 10.0 취약점보다 급할 수 있다.

숫자만 보고 patch queue 정하면 안 되는 이유다.


왜 계속 VPN 장비가 털릴까

Ivanti만의 문제라는 이야기는 아니다.

최근 몇 년 동안 공격자들은 계속 network edge device를 노리고 있다.

VPN
Firewall
Load Balancer
Email Gateway
Remote Access Appliance

왜냐하면 너무 좋은 타깃이기 때문이다.

첫째.

인터넷에 노출되어 있다.

둘째.

회사 내부 네트워크와 연결되어 있다.

셋째.

높은 권한을 가진다.

넷째.

LDAP나 AD 같은 인증 시스템과 통신한다.

다섯째.

일반 서버보다 EDR이 설치되지 않은 경우가 많다.

구조적으로 공격자가 좋아할 수밖에 없다.

Internet-facing
      +
Trusted Network Position
      +
High Privilege
      +
Limited Endpoint Visibility

공격자 입장에서 상당히 맛있는 조합이다.


EDR이 없다는 게 생각보다 크다

일반 Windows 서버에서 공격자가 이런 행동을 한다고 해보자.

PowerShell
Credential Dumping
Network Recon
Persistence

EDR이 이것저것 소리를 낼 가능성이 있다.

그런데 VPN appliance는 vendor에서 제공하는 closed appliance 형태인 경우가 많다.

마음대로 CrowdStrike나 Defender를 설치할 수도 없다.

결국 보안팀이 볼 수 있는 게 제한된다.

Vendor Logs
Network Logs
SIEM
External Telemetry

그런데 이번 공격에서는 vendor log 자체까지 조작했다.

그래서 network telemetry가 더 중요해진다.


우리 회사에서 Ivanti를 쓴다면 무엇을 봐야 할까

제일 먼저 해야 할 건 당연히 vendor의 최신 advisory 기준으로 현재 사용하는 version이 안전한지 확인하는 것이다.

Ivanti는 영향을 받는 고객에게 즉시 업데이트할 것을 권고했고, 10월 말 추가 release에서 실제 공격에 사용된 전체 chain을 수정했다고 밝혔다.

그다음은 조금 더 중요하다.

패치했다고 Incident Response가 끝난다고 생각하면 안 된다.

만약 취약 기간 동안 장비가 인터넷에 노출되어 있었다면

Patch
   |
   v
Investigation

이 같이 가야 한다.

이미 공격자가 들어온 뒤라면 패치는 미래의 공격을 막는 것이지,

과거 침해를 없애주는 게 아니기 때문이다.


Senior 보안팀이라면 질문이 달라진다

초반에는 이런 질문을 한다.

취약한 버전인가?

그다음에는

패치했나?

여기까지는 Vulnerability Management다.

Incident Response에서는 더 나간다.

패치 전 exploitation evidence는 없나?

VPN에서 내부 AD로 어떤 credential이 사용됐나?

해당 service account 권한은 어디까지인가?

LAPS나 BitLocker attribute를 읽을 수 있었나?

VPN에서 시작된 lateral movement 흔적은 없나?

같은 기간 password spraying은 없었나?

인증 로그가 비정상적으로 끊긴 구간은 없나?

질문이 이렇게 변한다.

Is it vulnerable?
      |
      v
Was it exploited?
      |
      v
What did the attacker access?
      |
      v
What trust did the device have?
      |
      v
Where could they move next?

이게 실제 Incident의 Blast Radius를 판단하는 과정이다.


개인적으로 이번 사건에서 제일 중요한 교훈

Zero-Day 3개를 연결했다는 것도 흥미롭다.

Race Condition도 재밌다.

Memory patch로 Integrity Checker를 피한 것도 재밌다.

그런데 기업 보안 관점에서 제일 중요한 건 다른 데 있다고 생각한다.

보안 장비 자체도 공격 표면이다.

우리는 흔히 VPN을 보안 솔루션이라고 생각한다.

Firewall도 보안 솔루션이다.

Email Gateway도 보안 솔루션이다.

그래서 묘하게 신뢰한다.

Internet
   |
   v
Security Device
   |
   v
Trusted Network

그런데 공격자가 보는 구조는 다르다.

Internet
   |
   v
High-value Target
   |
   v
Trusted Network

같은 그림이다.

보안을 위해 설치한 장비이기 때문에 오히려

더 높은 신뢰와 더 좋은 네트워크 위치를 가지고 있다.

그 신뢰가 공격자에게 넘어가면 피해도 커진다.


그리고 가장 무서운 문장

이번 사건을 한 줄로 정리하면 이렇다.

Fully Patched VPN
       |
       v
Unauthenticated Exploitation
       |
       v
Root Access
       |
       v
Credential Theft
       |
       v
Anti-Forensics

“패치 안 해서 털렸다.”

면 해결책은 비교적 단순하다.

패치 프로세스를 고치면 된다.

그런데

“패치가 전부 되어 있었는데 털렸다.”

면 이야기가 달라진다.

그때부터 필요한 건

Patch Management뿐만 아니라,

Network Monitoring,

Identity Security,

Credential Hygiene,

Segmentation,

Incident Response,

Memory Forensics,

Detection Engineering

전부다.

결국 Defense in Depth라는 뻔한 말을 다시 하게 된다.

뻔한데,

이럴 때 보면 왜 뻔한 말이 됐는지도 알 것 같다.


마지막으로

Ivanti는 공격이 제한된 수의 고객에게서 확인됐다고 밝혔고, 10월 말 전체 공격 chain을 해결하는 추가 패치를 공개했다. CISA도 공격에서 실제 사용된 추가 취약점을 KEV에 등재했다.

그러니까 이 글의 요점이

“Ivanti 쓰면 큰일 난다.”

는 아니다.

오히려 내가 이 사건에서 재미있게 본 건,

공격자가 보안 장비의 구조적 위치를 어떻게 이용했느냐는 점이다.

인증을 우회하고,

짧은 race window에서 코드를 실행하고,

root까지 올라가고,

VPN이 가진 Active Directory 신뢰를 이용하고,

로그를 지우고,

disk에는 최대한 흔적을 남기지 않는다.

하나씩 보면 전부 익숙한 기술이다.

근데 연결하면 꽤 무섭다.

그리고 실무 보안에서 가장 중요한 것도 결국 비슷하다.

취약점 하나를 보는 게 아니라

Entry Point
    |
    v
Privilege
    |
    v
Trust
    |
    v
Credential
    |
    v
Lateral Movement

전체 경로를 보는 것.

이번 Ivanti 사건은 그걸 정말 잘 보여주는 케이스라고 생각한다.

보안 장비라고 해서 신뢰하면 안 된다.

오히려 보안 장비일수록,

“이게 털리면 공격자는 어디까지 갈 수 있지?”

를 한 번 더 생각해야 한다.

끝까지 읽었네… 메밀 승인 🐾
+