[하루한줄] CVE-2025-62215: Windows Kernel의 Race Condition 및 Double Free로 인한 LPE 취약점

URL

Target

  • Windows 10, 11
  • Windows Server 2016, 2019, 2022, 2025
  • (2025년 11월 보안 업데이트 미적용 시스템)

Explain

이 취약점은 Windows 커널(ntoskrnl.exe)의 보안 하위 시스템, 그중에서도 프로세스 토큰을 관리하는 모듈에서 발생했습니다.

구체적으로는 기존 토큰을 복제하여 새로운 토큰을 생성하는 SepDuplicateToken 과정 중, 토큰의 권한을 재설정하는 내부 함수인 SepMakeTokenEffectiveOnly가 호출되는 시점에 적절한 동기화(Lock)가 유지되지 않아 발생한 Race Condition 취약점입니다.

Race Condition을 악용해 토큰 내부의 메모리 구조를 손상시키고, 객체가 해제될 때 Double Free를 발생시켜 최종적으로 권한 상승(LPE)을 수행할 수 있습니다.

Root Cause

취약점의 근본 원인은 임계구역의 빠른 해제입니다. 아래는 2025년 11월에 패치된 내용을 디핑한 결과입니다.

 // [Root Cause: nt!SepDuplicateToken 내부 로직 (Patch 전)]
__int64 __fastcall SepDuplicateToken(
        __int128 *a1,
        int a2,
        char a3,
        int a4,
        unsigned int a5,
        unsigned __int8 a6,
        unsigned __int8 a7,
        __int64 *a8)
{

// 1. 토큰 복제 및 메모리 할당 작업 진행
// ...

// [!] 문제 지점 1: 보호 장치(Lock)를 너무 일찍 해제함
ExReleaseResourceLite(*((PERESOURCE *)a1 + 6)); 

// [!] 문제 지점 2: 무방비 상태에서(Lock-free) 취약한 내부 함수 호출
if ( a3 ) {
    // 이 함수 내부에는 SpinLock 등 어떠한 동기화 코드도 없음
    SepMakeTokenEffectiveOnly(NewToken); 
}

SepDuplicateToken 함수는 기존 토큰을 복제하여 새로운 토큰을 생성합니다. 이 과정에서 부모 토큰에 대한 잠금(ExReleaseResourceLite)을 해제하는데, 문제는 아직 새로운 토큰의 설정이 완전히 끝나지 않은 시점에 락을 풀어버린다는 점입니다.

// [Vulnerable Function: SepMakeTokenEffectiveOnly]
// 동기화 없이 루프를 돌며 토큰 내부의 UserAndGroups 배열을 조작함
__int64 __fastcall SepMakeTokenEffectiveOnly(__int64 pToken)
{
  __int64 result; // rax
  unsigned int v2; // edx
  __int64 v3; // r8
  __int64 v4; // rbx
  int v5; // r10d
  int v6; // r11d
  unsigned int v7; // eax

  result = *(_QWORD *)(a1 + 72);
  v2 = 1;
  *(_QWORD *)(a1 + 64) &= result;
  *(_QWORD *)(a1 + 80) &= result;
  LODWORD(v3) = *(_DWORD *)(a1 + 124);
  if ( (unsigned int)v3 > 1 )
  {
    do
    // 다중 스레드 경합 시 v2(인덱스)와 v3(카운트)가 오염됨
    // 이로 인해 엉뚱한 메모리 주소를 참조하거나, 포인터가 꼬이게 됨
    {
      v4 = *(_QWORD *)(a1 + 152);
      result = *(unsigned int *)(v4 + 16i64 * v2 + 8);
      if ( (result & 0x34) != 0 )
      {
        ++v2;
      }
      else
      {
        v5 = *(_DWORD *)(a1 + 144);
        if ( v2 == v5 )
        {
          *(_DWORD *)(a1 + 144) = 0;
          v5 = 0;
        }
        v6 = *(_DWORD *)(a1 + 208);
        if ( v2 == v6 )
        {
          *(_DWORD *)(a1 + 208) = -1;
          v6 = -1;
        }
        v7 = v3 - 1;
        v3 = (unsigned int)(v3 - 1);
        if ( v7 == v6 )
          *(_DWORD *)(a1 + 208) = v2;
        if ( (_DWORD)v3 == v5 )
          *(_DWORD *)(a1 + 144) = v2;
        result = 2i64 * v7;
        *(_OWORD *)(v4 + 16i64 * v2) = *(_OWORD *)(v4 + 16 * v3);
      }
    }
    while ( v2 < (unsigned int)v3 );
  }
  *(_DWORD *)(a1 + 124) = v3;
  return result;
}

위 코드에서 보듯, SepMakeTokenEffectiveOnly는 토큰 객체의 핵심 데이터인 UserAndGroups 배열을 재정렬하면서도 커널 락(Lock)을 사용하지 않습니다.

공격자가 다중 스레드를 이용해 NtDuplicateToken을 호출하는 도중 이 타이밍(락 해제 직후 ~ 내부 함수 실행)에 개입하면, 배열의 인덱스(v2, v3)나 내부 포인터가 오염됩니다. 그 결과, 토큰 객체는 이미 해제된 메모리를 가리키거나, 다른 객체와 메모리를 중복해서 가리키는 상태가 되어, 이후 객체 소멸 시 Double Free로 이어집니다.

Reference