플랫폼 보안을 위한 Flow Guard 제어

Control Flow Guard란?

CFG(Control Flow Guard)는 메모리 손상 취약성에 대처하기 위해 만들어진 고도로 최적화된 플랫폼 보안 기능입니다. 애플리케이션이 코드를 실행할 수 있는 위치에 엄격한 제한을 두면 악용이 버퍼 오버플로와 같은 취약성을 통해 임의의 코드를 실행하는 것이 훨씬 더 어려워집니다. CFG는 /GS, DEPASLR과 같은 이전 익스플로잇 완화 기술을 확장합니다.

  • 메모리 손상 및 랜섬웨어 공격을 방지합니다.
  • 공격 표면을 줄이기 위해 서버의 기능을 특정 시점에 필요한 기능으로 제한합니다.
  • 버퍼 오버플로와 같은 취약성을 통해 임의 코드를 악용하기 어렵게 만듭니다.

이 기능은 Microsoft Visual Studio 2015에서 사용할 수 있으며 Windows의 "CFG 인식" 버전(데스크톱용 x86 및 x64 릴리스 및 Windows 10 및 Windows 8.1 Update 서버(KB3000850)에서 실행됩니다.

개발자는 애플리케이션에 CFG를 사용하도록 설정하는 것이 좋습니다. CFG 사용 및 비 CFG 사용 코드의 혼합이 정상적으로 실행되므로 코드의 모든 부분에 대해 CFG를 사용하도록 설정할 필요는 없습니다. 그러나 모든 코드에 대해 CFG를 사용하도록 설정하지 못하면 보호에 공백이 열릴 수 있습니다. 또한 CFG 사용 코드는 Windows의 "CFG를 인식하지 못하는" 버전에서 잘 작동하므로 완전히 호환됩니다.

CFG를 사용하도록 설정하려면 어떻게 해야 하나요?

대부분의 경우 소스 코드를 변경할 필요가 없습니다. Visual Studio 2015 프로젝트에 옵션을 추가하기만 하면 컴파일러와 링커가 CFG를 사용하도록 설정됩니다.

가장 간단한 방법은 Project | 로 이동하는 것입니다. 속성 | 구성 속성 | C/C++ | 코드를 생성 하고 제어 흐름 보호에 대해 예(/guard:cf) 를 선택합니다.

Visual Studio의 cfg 속성

또는 Project |에 /guard:cf 를 추가합니다. 속성 | 구성 속성 | C/C++ | 명령줄 | 추가 옵션 (컴파일러의 경우) 및 /guard:cf 에서 Project로 | 속성 | 구성 속성 | 링커 | 명령줄 | 추가 옵션 (링커용).

커의 컴파일러cfg 속성에 대한 cfg 속성

자세한 내용은 /guard(Control Flow Guard 사용) 를 참조하세요.

명령줄에서 프로젝트를 빌드하는 경우 동일한 옵션을 추가할 수 있습니다. 예를 들어 test.cpp라는 프로젝트를 컴파일하는 경우 cl /guard:cf test.cpp /link /guard:cf를 사용합니다.

또한 메모리 관리 API의 SetProcessValidCallTargets 를 사용하여 CFG에서 유효한 것으로 간주되는 icall 대상 주소 집합을 동적으로 제어할 수도 있습니다. 동일한 API를 사용하여 페이지가 CFG의 유효하지 않은지 유효한 대상인지 여부를 지정할 수 있습니다. VirtualProtectVirtualAlloc 함수는 기본적으로 실행 파일 및 커밋된 페이지의 지정된 영역을 유효한 간접 호출 대상으로 처리합니다. VirtualAlloc을 호출할 때 PAGE_TARGETS_INVALID 지정하거나 메모리 보호 상수에 자세히 설명된 대로 VirtualProtect를 호출할 PAGE_TARGETS_NO_UPDATE 지정하여 Just-In-Time 컴파일 러를 구현하는 경우와 같이 이 동작을 재정의할 수 있습니다.

이진 파일이 제어 흐름 보호 아래에 있음을 어떻게 알 수 있나요?

/headers 및 /loadconfig 옵션인 dumpbin /headers /loadconfig test.exeVisual Studio 명령 프 롬프트에서 덤프빈 도구(Visual Studio 2015 설치에 포함됨)를 실행합니다. CFG 아래의 이진 파일에 대한 출력은 헤더 값에 "Guard"가 포함되고 부하 구성 값에 "CF Instrumented" 및 "FID 테이블이 있음"이 포함됨을 보여 줘야 합니다.

dumpbin /headers의 출력

dumpbin /loadconfig의 출력

CFG는 실제로 어떻게 작동하나요?

소프트웨어 취약성은 실행 중인 프로그램에 가능성, 비정상적 또는 극단적인 데이터를 제공하여 악용되는 경우가 많습니다. 예를 들어 공격자는 예상보다 많은 입력을 프로그램에 제공하여 버퍼 오버플로 취약성을 악용하여 프로그램에서 예약한 영역을 과도하게 실행하여 응답을 보유할 수 있습니다. 이로 인해 함수 포인터를 보유할 수 있는 인접 메모리가 손상될 수 있습니다. 프로그램이 이 함수를 통해 를 호출하면 공격자가 지정한 의도하지 않은 위치로 이동할 수 있습니다.

그러나 CFG의 컴파일 및 런타임 지원의 강력한 조합은 간접 호출 명령이 실행되는 위치를 엄격하게 제한하는 제어 흐름 무결성을 구현합니다.

컴파일러는 다음을 수행합니다.

  1. 컴파일된 코드에 간단한 보안 검사를 추가합니다.
  2. 간접 호출의 유효한 대상인 애플리케이션의 함수 집합을 식별합니다.

Windows 커널에서 제공하는 런타임 지원:

  1. 유효한 간접 호출 대상을 식별하는 상태를 효율적으로 유지 관리합니다.
  2. 간접 호출 대상이 유효한지 확인하는 논리를 구현합니다.

설명하려면 다음을 수행합니다.

cfg 의사 코드

런타임에 CFG 검사 실패하면 Windows는 프로그램을 즉시 종료하므로 간접적으로 잘못된 주소를 호출하려고 시도하는 모든 악용이 중단됩니다.