관리 메뉴

ARTIFEX ;)

CPU Meltdown & Spectre 취약점 1 (in 2022) 본문

# Security/(구)보안

CPU Meltdown & Spectre 취약점 1 (in 2022)

Artifex_Ethan_ 2024. 4. 1. 17:00
반응형

초창기 CPU의 구조는 매우 단순하게 지금 당장 실행해야 하는 명령이 저장된 메모리 주소를 갖는 레지스터(PC : Program Counter)가 있었고, CPU는 Address BUS를 통해 해당 메모리의 내용을 순차적으로 읽어 실행하는 구조였다.

모든 명령이 완료되기 전까지는 아무 것도 할 수 없이 대기해야 했다. 그런데 실제 명령어 실행은 하나의 분해할 수 없는 단위 작업이 아니라 메모리에서 읽기, 해석, 실행, 결과 쓰기 등의 작은 단위 작업으로 나눌 수 있기에 이런 작업을 분담하는 유닛을 따로 두어 전적으로 수행하는 구조로 바꾸면 훨씬 효율적이 될 수 있다. 이러한 구조를 파이프라인(Pipeline)이라고 하며, 현재 CPU는 모두 이러한 구조를 가지고 있다.

Fetch → Decode → Execute → WriteBack

  • Fetch : 프로그램의 실행지점이 가리키는 명령을 가져온다.
  • Decode : 해당 명령들을 마이크로옵으로 변경한다. (인텔과 같은 CISC 구조에서는 하나의 명령이 여러 개의 마이크로옵으로 쪼개지기도 함)
  • Execute : 변경된 마이크로옵을 실행한다.
  • WriteBack : 실행 결과를 저장한다.

각 단계를 담당하는 유닛들이 모두 독립적으로 작동하므로 파이프라인을 여러 개 두어 명령을 동시에 실행할 수 있는 구조로 설계하면 성능을 많이 끌어올릴 수 있는데 이러한 구조를 수퍼스칼라(SuperScalar)라고 한다.

따라서 동일한 클럭의 CPU여도 파이프라인을 몇 개 가지고 있는지, 얼마나 효율적으로 작동하는지 여부에 따라 성능은 많이 차이가 난다.

수퍼스칼라 구조에서 중요한 포인트는 파이프라인을 잠시라도 쉬게 하지 않는 것이다.

Fetch에서 명령을 한번에 하나씩 가져오지 않고, 여러 개를 동시에 가져와서 한번에 실행하게 한다.

파이프라인의 어딘가에서 병목현상이 발생하여 노는 유닛이 있게 되면 그만큼 효율성이 떨어지게 되어 항상 파이프라인의 모든 유닛은 꽉꽉 FULL로 채워서 실행되고 있지 않으면 안된다.

파이프라인이 도입된 이후 CPU 설계자들의 주된 관심사는 파이프라인을 놀게 하지 않게 하는 것이었다. 이러한 필요성에 의해 비순차실행(OOOE : Out-Of-Order-Execution)이나 추측실행(SE : Speculative Execution)이 고안된 것이다.


자 위에서 파이프라인과 수퍼스칼라에 대한 간략적인 개념을 이야기했는데 그 두가지 개념의 필요성에 의해 고안된 비순차실행과 추측실행을 조금 더 이야기하고자 한다.

비순차실행과 추측실행

파이프라인을 놀게 하지 않으려면 당연히 실행유닛은 항상 마이크로옵을 적재하여 구동되고 있어야 한다. 현대 CPU는 실행유닛을 여러 개 가지고 있기 때문에 유닛들을 쉬지 않게 계속 명령을 반복하여 실행하고 있는 것이 가장 효율이 좋다.

그런데, 어떤 상황에서는 명령을 바로 실행하지 못하고 대기해야 하는 경우가 발생할 수 있다.

파이프라인이 3개인 가상의 CPU가 있는데 다음 명령들을 실행하려고 한다고 가정해보자.

  • A: 레지스터 R1과 R2를 더해서 R3에 저장한다.
  • B: R3의 내용이 100보다 크면 C를, 그렇지 않으면 D를 실행한다.
  • C: R3에서 100을 빼서 R4에 저장한다.
  • D: R3에서 100을 더해서 R5에 저장한다.

MeltDown과 Spectre 공격의 기본 원리

멜트다운과 스펙터는 CPU의 실행 구조 자체를 공격하는 것이다.

앞서 언급한대로 CPU는 입력된 명령을 가급적 한꺼번에 병렬 처리하고 하며, 필요하다면 실행 순서를 변경하기도 하고, 가능성이 높은 명령은 추측해 미리 실행하기도 한다. 추측이 틀렸다면, 파이프라인으로부터 이 명령을 지움으로써 오실행의 가능성을 막는다.

다시 말해 최대한 많이 추측해 먼저 실행하고 추측이 맞지 않는다면 취소한다, 라는 것이 기본적인 실행방식이고, 이 과정에서 실행되서는 안되는 명령이 실행될 수도 있으나 바로 취소되어 무효화되기 때문에 그로부터 어떤 이득을 취하거나, 데이터를 유출하는 일은 불가능하다는 것이 지난 정설이었다.

그런데, Google Project Zero의 Report는 이 정설을 뒤집어 엎은 격이다. 두 공격의 기본원리는 흡사하고 이론적으로는 모든 CPU가 대상이된다, 하지만 실제 필드에서의 유효는 구현 차이 및 기타 요소들의 영향을 받기에 테스트해보기 전까지는 판단할 수 없다.

두 개 공격의 핵심을 간단하게 정리하면 아래와 같다.

  1. 데이터를 확인하기 위한 메모리를 준비하고 캐쉬를 삭제한다. (Flush)
  2. Attacker는 비순차실행 또는 추측실행을 통해 원래 실행되서는 안되는 코드를 실행하게 한다.
  3. 2의 코드는 원래 ‘실행되면 안되는’ 것이기에, 이 사실이 확인되는 즉시 파이프라인에서 지워지고 취소된다. 하지만 이렇게 취소되어야 한다는 사실을 알고 실제 취소하기 전까지는 약간의 시간이 걸리는데 이 시간 동안을 Misspeculation Window라 한다. Attacker는 Misspeculation window를 늘리기 위해서는 접근해야 하는 메모리의 캐쉬를 리셋하는 등 다양한 장치를 사용한다.
  4. Misspeculation window 동안 실행된 Attacker의 Code가 보호되는 정보에 접근한 후 이 값에 대응하는 1의 메모리 부분을 읽어서 캐쉬에 남긴다. 만약 공격이 너무 느리거나, 취소가 빨리된다면 캐쉬를 남기는 일이 불가능해지고, 공격은 실패한다.
  5. 1의 메모리를 차례대로 읽으면서 속도를 측정하면 캐쉬가 있는 부분을 확인함으로써 보호된 정보를 간접적으로 알아낼 수 있다. (Reload) 이런 방법을 Flush + Reload Attack이라고 한다.

또한, 프린스턴 대학과 엔비디아에서 MeltdownPrime과 SpectrePrime이라는 새로운 공격 방법에 대한 논문을 발표 했는데 기존 원리와 비슷하다.

다만, 공격 시 Flush + Reload 처럼 메모리 읽기로 캐쉬를 남기는 것이 아니라 Prime + Probe, 즉 쓰기로 인한 캐쉬 탈락으로 데이터를 유출한다는 점이 차이가 있다.

공격의 핵심원리는 동일하며, 단지 Side Channel Attack의 방식만 다르므로 다른 기법보단 기존 방식의 변형 내지 변주 정도로 보아야 할 것이다.

대응책도 동일하다고 판단되나, 프린스턴과 엔비디아의 논문에서는 CPU 아키텍쳐 레벨의 대응책은 재검토가 필요하다 정도의 의견을 남겨두고 있다.

해당 취약점은 대부분의 사람들이 익숙하지 않고, HW의 버그에 기초한데다가 파이프라인 기작, OS 메모리관리, 캐쉬, 분기예측 등 많은 선수지식 등을 필요로 하기에 어려움이 상당히 많다.

그럼에도 불구하고 우리는 이 취약점에 대해 이해하고 해결책에 대한 이해도 1정도는 쌓아두는 것이 나을 것이라 판단된다.

→ 2편에서는 MeltDownPrime & SpectrePrime 공격에서 쓰기에도 캐쉬가 적용되는데 어째서 캐쉬가 탈락되는가 라는 질문에 대한 내용을 풀어가도록 할 것이고, 본 내용에 대한 추가적인 내용을 더 다룰 것이다.

반응형

'# Security > (구)보안' 카테고리의 다른 글

CPU Meltdown & Spectre 취약점 2 (in 2022)  (1) 2024.04.01
Robots.txt에 대해 제대로 알자  (0) 2024.04.01
XSS, CSRF 간단 정리  (0) 2020.05.22
# OWASP TOP 10 ver.2017  (0) 2020.05.22
허니팟 Honey-Pot  (0) 2020.05.20