요약 보고서
Akamai 연구원들이 즉각적인 근본 원인 분석을 위한 새로운 AI 기능을 개발했습니다. 이 툴은 PatchDiff-AI라는 감독형 다중 에이전트 시스템입니다. 이 블로그 게시물에서는 거의 모든 Windows 버전에 영향을 미치는 CVE-2025-60719에 대한 심층 분석에 이 기능을 사용했습니다.
취약점: 경쟁 조건으로 인한 Winsock용 Windows Ancillary Function Driver(afd.sys)의 UAF(use-after-free) 취약점
영향: 로컬 권한 상승: 낮은 권한을 가진 공격자가 커널 메모리를 조작하고 시스템 권한을 얻을 수 있습니다.
근본 원인: 드라이버가 소켓 엔드포인트의 바인딩 해제(해제)를 방지하지 못했습니다. 다른 작업(예: Transfer, GetInformation 또는 Connect)이 관련 오브젝트를 능동적으로 역참조하고 있습니다.
수정 사항: Microsoft는 중요한 작업 중에 엔드포인트 상태를 명시적으로 잠글 수 있도록 동기화 장벽 메커니즘(AfdPreventUnbind/AfdReallowUnbind)을 추가했습니다.
PatchDiff-AI 툴을 사용해 영향력이 큰 패치 화요일 취약점의 근본 원인을 신속하게 분석하고 보안팀이 원데이 취약점을 더 잘 식별하고 방어할 수 있도록 지원하는 방법을 알아보세요.
취약점
CVE-2025-60719는 특히 Winsock API의 초석인 afd.sys 내에 있어 Windows 커널 네트워킹 스택의 심각한 결함을 나타냅니다. 악용에 성공하면 권한이 낮은 프로세스가 시스템 권한을 얻을 수 있습니다. 앱 컨테이너 프로세스 내에서 악용을 테스트했지만 권한이 부족하여 실패했습니다.
이 취약점은 다음과 같은 Windows 버전을 모두 비롯하여 많은 Windows 제품에 영향을 미칩니다.
Windows Server - 2008 SP2(x86/x64), 2008 R2 SP1, 2012, 2012 R2, 2016, 2019, 2022, 2022 23H2, 2025
Windows 11 - 23H2, 24H2, 25H2
Windows 10 - 버전 1607, 1809, 21H2, 22H2
기타 단종 버전
이렇게 영향을 받는 버전이 많으면 공격 표면이 대폭 증가합니다.
근본 원인 분석
PatchDiff-AI를 사용하면 벤더사 권고에 따라 취약점을 수정하는 보안 패치를 신속하게 분석할 수 있습니다. 툴의 분석 결과를 읽어보면 관련 코드 변경 내용에 AfdSocketTransfer*, AfdGetInformation, AfdBind 및 AfdConnect 메서드가 포함되어 있음을 알 수 있습니다.
자세히 살펴보면 이 취약점의 근본 원인은 AFD 엔드포인트 구조체(AFD_ENDPOINT)의 수명 주기 관리와 오브젝트 포인터를 가져와 사용하는 사이에 원자성(동시 작업 동기화)이 부족하기 때문임을 알 수 있습니다.
변경 사항 분석
PatchDiff-AI 툴에서는 취약점의 핵심 로직과 직접적으로 관련된 구성 요소인 Ancillary Function Driver(afd.sys)를 강조했으며, 이는 Windows의 핵심 전송 레이어 네트워킹 지원을 구현합니다. 이 드라이버는 Winsock 인터페이스를 제공하고, 네트워크 소켓을 관리하고, 기타 I/O 관련 기능을 처리합니다.
이 툴의 분석은 KB5068861 보안 업데이트의 일부로 수정된 버전인 10.0.26100.7171을 기반으로 하며, KB5066835 보안 업데이트의 일부로 취약한 버전인 10.0.26100.6899에 대해 테스트되었습니다.
총 5개의 함수가 수정되었으며 이러한 함수는 모두 이 툴에 의해 보안 관련 함수로 분류되었습니다.
AfdGetInformation 함수를 분석하면 바인딩 해제 작업을 차단해 소켓이 해제되지 않도록 함으로써 현재 보호되고 있는 코드 블록을 식별할 수 있습니다(그림 1). 이 변경은 소켓 수명 주기를 참조하고 그 상태를 방해한다는 것을 알고 있기 때문에 이것이 UAF 취약점과 관련이 있다고 합당하게 추측할 수 있습니다.
Ancillary Function Driver - 빠른 시작
Ancillary Function Driver(afd.sys)의 작동 방식과 그 사용 방법에 대해 포괄적으로 다룬 몇 가지 글이 있습니다. 간단히 말해, 이 드라이버는 Winsock API를 제공하는 Windows 구성 요소입니다. 이를 통해 권한이 낮은 프로세스가 TCP/IP 및 기타 통신 프로토콜의 네트워크 스택을 사용할 수 있습니다.
그림 2에는 IOCTL(인풋/아웃풋 제어)을 통해 afd.sys를 호출하는 많은 참조 바이너리가 나와 있습니다. 또한 사용자 모드 애플리케이션 호출과 커널 모듈 로직 활성화 사이를 점으로 연결하면 어떤 호출이 어떤 인터페이스와 관련이 있는지 이해할 수 있습니다. 이는 나중에 드라이버를 직접 호출하고 드라이버의 동작을 디버깅하는 데 도움이 됩니다.
로직 분석
이 취약점은 UAF 시나리오로 이어지는 고전적인 경쟁 조건입니다. 이 취약점은 AfdSocketTransfer*, AfdGetInformation, AfdBind, AfdConnect를 비롯한 여러 디스패치 루틴에 영향을 미칩니다.
핵심 문제는 사용자 모드 애플리케이션이 특정 IOCTL 요청을 발행하는 동시에 별도의 스레드에서 소켓을 닫을 때 발생합니다. 패치되지 않은 버전에서는 이 드라이버가 엔드포인트, 파일 오브젝트 또는 디바이스 오브젝트에 대한 포인터를 가져와 동시 닫기 작업에 의해 기본 메모리가 해제되지 않도록 조치하지 않은 상태로 이 포인터를 사용했습니다. 이로 인해 커널이 유효하지 않은 메모리에서 작동할 수 있는 틈이 생겨 로컬 공격자가 커널 메모리를 손상시키거나 상승된 권한을 얻을 수 있습니다.
프로세스가 바인딩된 소켓(수신)의 핸들을 사용하여 특정 정보 클래스에 대한 afd!AfdGetInformation 함수를 호출하면 소켓 종류, 프로토콜 레이어와 같은 몇 가지 필수 조건을 확인하는 작업이 수반됩니다. 이 호출 흐름을 따라가면 기본 함수에 호출자가 사용할 수 있는 다양한 작업에 대한 switch case가 있음을 알 수 있습니다.
취약한 코드 블록은 AFD_MAX_PATH_SEND_SIZE case 내에 있습니다(그림 3). 즉, 취약한 경로에 도달하려면 호출자가 인터페이스를 호출할 때 올바른 값을 제공해야 하고 소켓이 연결된 상태여야 합니다.
class AFD_INFORMATION_CLASS(IntEnum):
AFD_INLINE_MODE = 1 # s: BOOLEAN
AFD_NONBLOCKING_MODE = 2 # s: BOOLEAN
AFD_MAX_SEND_SIZE = 3 # q: ULONG
AFD_SENDS_PENDING = 4 # q: ULONG
AFD_MAX_PATH_SEND_SIZE = 5 # q: ULONG
AFD_RECEIVE_WINDOW_SIZE = 6 # q; s: ULONG
AFD_SEND_WINDOW_SIZE = 7 # q; s: ULONG
AFD_CONNECT_TIME = 8 # q: ULONG (seconds)
AFD_CIRCULAR_QUEUEING = 9 # s: BOOLEAN
AFD_GROUP_ID_AND_TYPE = 10 # q: AFD_GROUP_INFO
AFD_REPORT_PORT_UNREACHABLE = 11 # s: BOOLEAN
AFD_REPORT_NETWORK_UNREACHABLE = 12 # s: BOOLEAN
AFD_DELIVERY_STATUS = 14 # q: SIO_DELIVERY_STATUS
AFD_CANCEL_TL = 15 # s: void
그림 4에서 취약한 코드의 디컴파일된 버전(이전)과 패치된 코드(이후)를 비교할 수 있습니다.
경쟁 조건
afd.sys 드라이버는 엔드포인트의 수명 주기를 유지하며, 엔드포인트 오브젝트는 여러 스레드에서 동시에 접속할 수 있습니다. 그림 5에서 m_Endpoint->State는 소켓의 상태를 나타냅니다.
소켓을 유효한 상태로 유지하려면 커널 모듈에서 이를 매우 주의해서 처리해야 합니다. 이 경우, 다른 스레드를 통해 상태를 변경하고 결국에는 unbind 제어 명령어를 사용하여 엔드포인트를 완전히 해제할 수 있는 틈이 있습니다.
로컬 공격자는 두 스레드 사이에 "경쟁 상태"를 유도하여 이를 악용할 수 있습니다.
스레드 A: 취약한 IOCTL(예: IOCTL_AFD_GET_INFORMATION)을 반복 전송
스레드 B: 소켓 핸들을 반복적으로 바인딩 및 바인딩 해제
스레드 A가 포인터를 가져온 후 스레드 A가 이 포인터를 사용하기 전에 스레드 B가 경쟁에서 승리하면 스레드 A는 해제된 메모리를 역참조합니다.
공격 과정
PatchDiff-AI 보고서와 기타 리소스를 사용하면 afd!AfdGetInformation 함수를 사용하여 올바른 인수로 호출해 경쟁에서 승리하는 과정을 이해할 수 있습니다(그림 6).
그림 6: 취약점을 트리거하는 개념 증명(PoC) 코드
그림 7에서는 AfdGetInformation 호출 시점부터 PageFault 예외가 발생하는 시점까지의 충돌 호출 스택을 찾을 수 있습니다.
공격 기법
로컬 시스템에만 영향을 미치는 로컬 권한 상승 버그입니다. 권한이 낮은 로컬 프로세스에서 임의의 코드를 실행할 수 있는 공격자는 경쟁 조건을 악용하고 더 높은 권한을 얻을 수 있습니다.
PatchDiff-AI 툴은 악용을 위한 것이 아니지만, 악용이 의심되는 절차를 이해하는 데 도움이 될 수 있습니다.
초기화: 공격자가 NtCreateFile()을 사용하여 유효한 AfdSocket을 엽니다.
그루밍/스프레잉: 충돌에 반드시 필요한 것은 아니지만, EoP에 대한 안정적인 악용을 위해 해제된 AFD_ENDPOINT를 공격자가 제어하는 가짜 구조체로 대체할 수 있도록 힙 스프레잉을 사용하는 경우가 많습니다.
트리거링: 공격자가 두 개의 스레드를 생성합니다. 스레드 1은 취약한 IOCTL(예: IOCTL_AFD_GET_INFORMATION)이 있는 NtDeviceIoControlFile을 반복 실행합니다. 스레드 2는 IOCTL_AFD_BIND와 IOCTL_AFD_UNBIND를 연속으로 반복 실행합니다.
실행: 경쟁에서 승리하면 커널은 공격자의 가짜 오브젝트에 대해 IoGetRelatedDeviceObject와 같은 호출을 실행하고, 이는 커널 메모리를 조작으로 이어집니다.
시스템을 보호하는 방법
패치 적용: 유일한 효과적인 보호 방법은 관련 Windows 보안 업데이트를 설치하는 것입니다. 이 패치는 바인딩 해제 장벽을 엄격하게 적용하여 afd.sys를 강화합니다.
방어: 악용 시도를 나타낼 수 있는 AFD와의 상호 작용을 탐지하기 위해 SIGMA 또는 YARA 룰 세트를 사용하면 위협을 줄일 수 있습니다. 대부분의 경우는 afd 드라이버에 대한 IOCTL 호출을 시작하는 프로세스를 추적하고 반복된 호출을 사용해 커널 풀 메모리를 스프레잉하는 것으로 충분할 수 있습니다(그림 8).
탐지: AFD 엔드포인트(소켓)는 궁극적으로 파일 핸들로서 Windows I/O 하위 시스템을 통해 노출되므로, IRP 호출을 가로채 의심스러운 활동, 특히 연결이 끊긴 엔드포인트의 활동을 모니터링하면 됩니다.
import "pe"
rule CVE_2025_60719 {
meta:
description = "Detects exploits for CVE-2025-60719 targeting afd.sys"
author = "Maor Dahan"
date = "2025-12-01"
reference = "https://github.com/akamai/CVE-2025-60719-AFD.SYS"
strings:
// Core Indicators - The specific device name is strong indicator
$str_device = "\\Device\\Afd\\Endpoint" ascii wide
$str_device_double = "\\\\Device\\\\Afd\\\\Endpoint" ascii
// Specific IOCTL constants used in the exploit
// 0x12003 = IOCTL_AFD_BIND
$ioctl_str_bind = "0x12003" ascii
$ioctl_hex_bind = { 03 20 01 00 }
// 0x12113 = IOCTL_AFD_UNBIND
$ioctl_str_unbind = "0x12113" ascii
$ioctl_hex_unbind = { 13 21 01 00 }
// 0x1207B = IOCTL_AFD_GET_INFORMATION
$ioctl_str_info = "0x1207B" ascii
$ioctl_hex_info = { 7B 20 01 00 }
condition:
($str_device or $str_device_double) and
(
(
pe.is_pe and
pe.imports("ntdll.dll", "NtDeviceIoControlFile") and
all of ($ioctl_hex_*)
)
or
(
not pe.is_pe and
(
(
all of ($ioctl_str_*) or
3 of ($ioctl_*)
)
)
)
)
}
결론
PatchDiff-AI를 사용하면 바이너리 변경과 보안 로직 간의 상관관계를 자동화하고, 관련 없는 코드 수정으로 인해 혼돈에 빠질 일 없이 근본 원인을 신속하게 파악할 수 있습니다. 이를 통해 보안팀은 신속하게 조치를 취해 원데이 악용을 방지할 수 있습니다.
CVE-2025-60719 사례 연구는 커널 드라이버에 대한 패치 관리의 복잡성과 PatchDiff-AI 툴의 분석을 통해 다양한 목적에 대한 통찰력 있는 세부 정보를 제공하는 방법을 강조합니다.
태그