본문 바로가기

카테고리 없음

DirectCompoisition, CVE-2019-0685

CVE-2019-0685 취약점은 CVE-2019-0685 win32k reference count leak in DirectComposition — sigpwn 문서에서 상세하게 다루고 있습니다.

 

DirectComposition 컴포넌트에서 그래픽 객체(Graphic Object)를 Marshaler라고 부른다.

 

Marshaler 객체 life cycle을 관리하기 위해 레퍼런스 카운트를 사용한다. 최초 생성된 Marshaler 객체는 레퍼런스 카운트 값으로 1을 갖는다. Marshaler(A)가 Marshaler(B)를 참조하는 경우 Marshaler(B)의 레퍼런스 카운트가 1증가한다. 마찬가지로 만약 내가 Marshaler 객체로부터 참조 받는다면 나의 레퍼런스 카운트가 1증가한다. 객체를 메모리에서 해제하는 ReleaseResource 함수는 해제하려는 객체의 레퍼런스 카운트를 1감소한 후 결과가 0인 경우 해당 객체를 해제한다.

 

Marshaler 객체의 SetReferenceProperty 함수는 현재 객체가 다른 Marshaler 객체를 참조할 때 사용하는 함수로 모든 Marshaler 객체가 이 함수를 구현하고 있다. 위에서 설명한 레퍼런스 카운트를 증가 또는 감소하는 행위가 이 함수에서 이뤄진다. SetReferenceProperty 함수는 보통 아래와 같은 내용을 처리한다.

  • 참조하려는 대상(target)의 포인터가 NULL이 아닌지 확인한다
  • 참조하려는 대상의 타입을 확인한다
  • 대상으로 이미 참조가 이뤄져있는지 확인한다. 그런 경우라면 기존에 맺어진 참조를 끊은(ReleaseResource) 후 새롭게 참조를 맺고 대상의 레퍼런스 카운트를 1증가(AddRef)시킨다.

아래 임의로 선택한 두 개의 SetReferenceProperty 함수는 위에서 언급한 패턴을 따른다. 다른 함수들도 마찬가지다.

(win32kbase.sys version 10.0.17763.404, from winbindex)

 

CCompositionSpotLightMarshaler::SetReferenceProperty

__int64 __fastcall DirectComposition::CCompositionSpotLightMarshaler::SetReferenceProperty(DirectComposition::CCompositionSpotLightMarshaler *this, struct DirectComposition::CApplicationChannel *a2, int a3, struct DirectComposition::CResourceMarshaler *a4, bool *a5)
{
  unsigned int v5; // ebx
  struct DirectComposition::CResourceMarshaler **v9; // r14

  v5 = 0;
  *a5 = 0;
  if ( a3 != 6 )
    return 3221225485i64;
  v9 = (struct DirectComposition::CResourceMarshaler **)((char *)this + 96);
  if ( this == (DirectComposition::CCompositionSpotLightMarshaler *)-96i64
    || a4
    && !(*(unsigned __int8 (__fastcall **)(struct DirectComposition::CResourceMarshaler *, __int64))(*(_QWORD *)a4 + 120i64))(
          a4,
          157i64) )
  {
    return (unsigned int)-1073741811;
  }
  if ( *v9 != a4 )
  {
    DirectComposition::CApplicationChannel::ReleaseResource(a2, *v9);
    *v9 = a4;
    if ( a4 )
      ++*((_DWORD *)a4 + 5);
    *((_DWORD *)this + 4) |= 0x800u;
    *a5 = 1;
  }
  return v5;
}

CCompiledEffectTemplateMarshaler::SetReferenceProperty

__int64 __fastcall DirectComposition::CCompiledEffectTemplateMarshaler::SetReferenceProperty(DirectComposition::CCompiledEffectTemplateMarshaler *this, struct DirectComposition::CApplicationChannel *a2, int a3, struct DirectComposition::CResourceMarshaler *a4, bool *a5)
{
  unsigned int v5; // ebx
  struct DirectComposition::CResourceMarshaler *v9; // rdx

  v5 = 0;
  *a5 = 0;
  if ( a3
    || a4
    && !(*(unsigned __int8 (__fastcall **)(struct DirectComposition::CResourceMarshaler *, __int64))(*(_QWORD *)a4 + 120i64))(
          a4,
          136i64) )
  {
    return (unsigned int)-1073741811;
  }
  v9 = (struct DirectComposition::CResourceMarshaler *)*((_QWORD *)this + 6);
  if ( a4 != v9 )
  {
    DirectComposition::CApplicationChannel::ReleaseResource(a2, v9);
    *((_QWORD *)this + 6) = a4;
    if ( a4 )
      ++*((_DWORD *)a4 + 5);
    *((_DWORD *)this + 4) &= 0xFFFFFFBF;
    *a5 = 1;
  }
  return v5;
}

CVE-2019-0685

CConditionalExpressionMarshaler::SetReferenceProperty 함수는 참조하려는 객체가 이미 있는지 확인하는 코드를 구현하지 않았다. 당연히 ReleaseResource 함수를 통한 레퍼런스 카운트를 감소하는 코드 역시 존재하지 않는다.

__int64 __fastcall DirectComposition::CConditionalExpressionMarshaler::SetReferenceProperty(DirectComposition::CConditionalExpressionMarshaler *this, struct DirectComposition::CApplicationChannel *a2, int a3, struct DirectComposition::CResourceMarshaler *a4, bool *a5)
{
  unsigned int v5; // ebx

  v5 = 0;
  *a5 = 0;
  if ( a3 != 11 )
    return (unsigned int)DirectComposition::CBaseExpressionMarshaler::SetReferenceProperty(this, a2, a3, a4, a5);
  if ( !a4
    || !(*(unsigned __int8 (__fastcall **)(struct DirectComposition::CResourceMarshaler *, __int64))(*(_QWORD *)a4 + 120i64))(
          a4,
          53i64) )
  {
    return (unsigned int)-1073741811;
  }
  *((_QWORD *)this + 16) = a4;
  *a5 = 1;
  *((_DWORD *)this + 4) &= 0xFFFFFBFF;
  ++*((_DWORD *)a4 + 5);
  return v5;
}

ConditionalExpressionMarshaler 객체를 생성하고 이 객체를 다른(target) 객체에 참조하는 행위를 할 때 마다 ++*((_DWORD *)a4 + 5) 코드가 반복 실행되면서 그 값이 계속 증가하는 문제점이 존재한다. 레퍼런스 카운트가 계속 증가하면서 0xFFFFFFFF, 0x00000000, 0x00000001 순으로 값을 가지게 된다. target 객체의 레퍼런스 카운트가 0x1이 되었을 때 target 객체를 해제하면 ConditionalExpressionMarshaler의 참조 객체를 가리키는 포인터는 danling pointer 상태가 된다. 

 

레퍼런스 카운트

출처: CVE-2019-0685 win32k reference count leak in DirectComposition

 

USE-AFTER-FREE 취약점을 트리거 하는 순서는 다음과 같다.

  1. CConditionalExpressionMarshaler 객체를 생성한다
  2. 참조될(to-be referenced) CExpressionMarshaler 객체를 생성한다
  3. CConditionalExpressionMarshaler을 CExpressionMarshaler로 참조한다
  4. 3번 행위를 반복해서 CExpressionMarshaler 객체의 레퍼런스 카운트를 0x1까지 증가시킨다
  5. CExpressionMarshaler 객체를 해제한다. CConditionalExpressionMarshaler는 CExpressionMarshaler를 가리키는 포인터(dangling pointer)를 가지게 된다
  6. CConditionalExpressionMarshaler를 해제한다. 객체 멤버를 정리하는 과정에서 dangling pointer를 참조한다