[SSS] CPU vulnerabilities and SideChannel Attack
BOF 같은 버그는 모두 프로그래머가 코드를 잘못 작성해서 발생하는 취약점이다.
근데 당연히 취약점 중에는 이런 소프트웨어 버그 말고도 CPU 아키텍처 자체의 구조적 허점을 찌르는 공격 방식도 많음.
Meltdown과 Spectre가 그 예시.
설계나 명세의 오류가 아니고, CPU의 성능을 위해 필수적인 기능이기에.. 보안을 위해 기능을 끌 수도 없음.
Meltdown

공격자가 데이터를 가져가려면 데이터가 어떻게 저장되고 보호되는지 알아야 한다.
운영체제에서 다 다룬 내용이긴 함..
A 프로세스의 주소 공간은 B 프로세스와 완전히 격리되어 있다.
그런데 커널 코드와 데이터는 모든 프로세스의 가상 주소 공간에 매핑됨.
커널 데이터가 내 주소 공간에 있다고 읽을 수 있는건 아니긴 함..
유저 모드 코드가 커널 메모리에 접근하려고 하면 CPU 하드웨어가 예외를 뱉음.
다만, CPU가 예외를 뱉기 직전에 데이터를 읽어서 숨겨둔다면?
Meltdown 공격은 해당 컨셉을 기반으로 한다.
CPU가 죽기 직전에 훔쳐온 데이터를 외부로 유출하려면.. Timing과 Cache를 잘 활용해야 한다.

CPU가 데이터에 접근할 때.. 레지스터에 저장된 데이터를 가져오는 경우가 가장 빠르다. (1 cycle)
피라미드에서 아래로 내려갈수록 느려지고, RAM에 접근할 때는 200 cycle이 소모됨.
데이터가 캐시에 있으면 접근이 매우 빠르고, 메모리에만 있으면 접근이 느리다는거임.
이 시간 차이를 이용해 정보를 0과 1의 신호로 바꾼다.
A와 B가 서로 통신한다고 하자. 송신자 B는 수신자 A에게 비밀 값 X를 전달한다.
두 사람은 공유 메모리에 Probe_Array 배열을 가지고 있다. 각 배열의 요소는 Cache Line만큼 떨어져 있음.
1. Flush
수신자는 clflush 명령어로 Probe_Array의 모든 데이터를 캐시에서 강제로 지워버린다.
이제 누가, Probe_Array의, 어떤 데이터에 접근하든, 메인 메모리까지 다녀와야 하니 접근 속도가 느리다.
2. Transmit
송신자는 비밀 값 X를 수신자에게 보내려 한다.
Probe_Array[X] 에 접근하는데, 이 때 Probe_Array[X]의 데이터만 캐시로 로드되고 나머지 배열 요소는 RAM에 있음.
3. Receive / Prove
수신자는 Probe_Array의 모든 요소를 순서대로 읽으면서 Access Time을 측정한다.
Probe_Array[X] 만 캐시에 올라갔으니까 저 부분을 읽을 때만 속도가 빠를거임. 그러니 비밀 값 X를 유추할 수 있다.
Side-Channel Attack : 피해자는 줄 생각이 없는데 유출되는 정보를 의미한다. (하드웨어의 물리적 특성 때문..)
Covert Channel : 두 사람이 몰래 소통하기 위해 통신용이 아닌걸 통신 수단으로 악용하는걸 의미한다.
Meltdown 공격은 Covert Channel을 구축하기 위해 Cache Timing Side-Channel 기술을 사용함.
공격자가 미래 캐시를 전부 비우고, 비밀 데이터를 조작하도록 유도하고, 뭐가 빨라졌는지 검사하는 방식으로 역추적한다.
코드는 순차적으로 실행되지 않는다. CPU 내부에서는 성능 최적화를 위해 코드를 깎고 또 깎는다. (Out-of-Order Execution)
앞선 명령어가 메모리에서 데이터를 가져오는 동안 뒤에 있는 명령어를 미리 처리해 CPU Cycle 낭비를 막음.
즉, 데이터가 준비된 명령어부터 실행시키는데.. 이 때 예측이 틀리거나 예외가 발생하면 미리 실행한 결과를 롤백친다.
1. raise_exception(); // 예외 터짐
// 그러니 여기 아래는 절대 실행되면 안된다...
3. access(probe_array[data * 4096]);
1번 줄에서 예외가 발생하면 CPU는 처리를 멈추고 Exception Handler로 넘어가야 하는데.
예외를 처리하는 동안 CPU의 Out-of-Order Execution 엔진은 3번 줄의 데이터가 준비됐으니 미리 실행해버린다.
이렇게 실행만 되고 예외가 발생한 탓에 결과가 반영되지 않고 버려지는 명령어를 Transient Instructions라고 부름.
예외가 확정되면 Register같은 Architectural State는 원래대로 되돌아가는데...
미리 데이터를 읽어오면서 변한 캐시 상태는 되돌리지 않는다.
즉, 실행된 흔적을 남기게 되는 것..
그러니 여기서 Meltdown 공격이 가능하다. 에러 떠서 죽기 전에 캐시를 찾아보면 CPU가 읽은 값이 뭔지 알 수 있다.

Meltdown 공격 설계도는 위와 같이 구성됨. 그냥 지금까지 말한 내용 그대로..
이제 이 원리를 바탕으로 어셈블리어 코드를 어떻게 구현하는지 살펴보자.
retry:
1. mov al, byte [rcx]
2. shl rax, 0xc
3. jz retry
4. mov rbx, qword [rbx + rax]
rcx : 공격자가 읽으려고 하는 커널 메모리 주소
rbx : 공격자가 준비한 탐지 배열 주소 (Probe_Array)
1.
유저모드에서 커널 주소를 읽으려고 하니까 당연히 예외가 터진다.
CPU는 예외처리를 준비하면서 다음 명령어를 미리 실행해버린다.
2.
읽어온 비밀 값을 왼쪽으로 12비트만큼 shift.(4096) -> Cache Line 때문
비밀 값이 1이랑 2라고 치면, 너무 가까워서 같은 캐시 라인에 들어갈 수 있음. 이걸 방지하기 위해 데이터를 띄엄띄엄 배치하자.
4.
Probe_Array의 특정 위치에 접근한다. 인덱스는 비밀값 * 4096
이 명령어가 실행되는 순간 해당 배열의 메모리 페이지가 L1 L2 L3 캐시에 로드된다.
3번 라인에서 jz retry같은 루프를 설치해 둔 이유는.. 공격자는 1번 줄의 예외가 터지기 전에 4번 줄까지 실행을 마쳐야 하기 때문.
하드웨어 예외 처리 속도와 Out-of-Order Execution 실행 속도간 Race 가 발생한다.
특정 CPU는 예외 발생 시 레지스터 값을 0으로 밀어버린다. 4번 라인 실행 직전에 rax가 0이 되어버리면.. 공격자는 0을 읽게 됨.
그러니 0이 읽히면 retry로 돌아가서 성공할 때 까지 다시 시도한다.
이걸 반복하면 커널 메모리 전체를 dump 할 수 있다. 단순히 커널의 비밀 값 하나를 가져가는 수준이 아님.
운영체제는 RAM 전체를 커널의 가상 주소 공간의 특정 영역에 매핑해 둔다.
Meltdown이 가능하다는 건, 컴퓨터에 박힌 RAM의 모든 내용을 읽을 수 있다는 것. (다른 사용자 프로세스, 파일 캐시, 네트워크 패킷 등..)

관련된 실제 해킹 사례도 많음. 우분투에서 Firefox 브라우저를 대상으로 공격한 예시.
Firefox 비밀번호 관리자에 여러 사이트의 비밀번호를 저장해 뒀는데..
Meltdown 공격 코드를 실행하니 메모리 덤프 화면에 비밀번호들이 평문으로 쏟아져 나옴.
공격자는 브라우저의 취약점을 찾을 필요도 없이 CPU의 구조적 허점을 이용해 브라우저가 사용하는 메모리 영역을 긁어 비밀번호를 얻는다.
CPU 결함이지만, 막긴 해야하니까.. OS 수준에서 Kernel Page Table Isolation (KPTI)패치를 배포함.
유저 프로세스의 주소 공간에 커널 메모리를 매핑해둔게 문제였으니, 유저 모드일때는 아예 커널 메모리를 매핑하지 않는 방식이다.
이제 페이지 테이블에 커널 공간이 없다.
공격자가 접근하려고 해도 물리적으로 주소 변환 자체가 불가능하니 Out-of-Order Execution을 근본적으로 막아버림.
근데 당연히 시스템콜 호출할 때 마다 페이지 테이블을 교체해야 하니 성능이 저하됨.
그럼에도 탈탈 털리는것보단 훨씬 나으니까 웬만한 OS에는 패치가 적용됨.
Spectre
Meltdown의 공격 대상은 대부분 인텔 CPU였는데, 스펙터는 인텔, AMD 등 현대 프로세서에 영향을 미친다.
Branch Prediction이라는 또 다른 형태로 공격해버림.
if 같은 분기문은 어셈블리어로 cmp -> jz 로 처리된다.
문제는 cmp 명령어를 실행해두고 결과가 나올 때 까지 기다리면 CPU 파이프라인이 비어버려 성능이 떨어진다는 것;

그래서 현대 CPU는 안기다리고 찍는다.
CPU 내부에는 Branch Predictor가 있어 예전의 기록을 바탕으로 이번에 TRUE인지 FALSE인지 예측한다.
이전에 TRUE 였으니까 이번에도 TRUE 겠지 ㅋㅋ 걍 결과 나오기 전에 TRUE 브랜치에 있는 명령어 미리 실행해두자~
진짜 TRUE 였으면 좋은데 까보니까 FALSE 였으면 롤백쳐야 함. 어 롤백?
여기서도 Meltdown 때 처럼 이미 캐시에 로드된건 롤백하지 않는다.
스펙터는 실행하면 안 되는 코드 경로를 CPU가 억지로 실행하도록 해버림.
기계학습마냥 if (x < 10) 가 있다고 치면 x에 10보다 작은거만 계속 입력해서 TRUE 라고 학습시킨다.
그리고 중요할 때 x에 11 넣어버린다.
// x는 공격자가 조작 가능해야 함
if (x < array1_size) {
y = array2[array1[x] * 256];
}
대충 눈치챘겠지만 저거랑 비슷하게 생긴 가젯이 있어야 스펙터 공격이 가능함.
x에 자기가 원하는 큰 값을 넣어서 array1 베이스로부터 멀리 떨어진 비밀 데이터가 가리키게 한다.
array1[x] 가 Secret Memory를 읽게 만듦.
1. Stall
x < array1_size를 검사하려는데 array1_size 가 캐시에 없어서 RAM에서 가져와야 한다.
2. Speculative Execution
CPU는 기다리기 싫으니 지금까지 계속 TRUE 였으니 이번에도 TRUE겠거니 하고 if 문 내부 코드를 실행한다.
3. Leak
Temp = array1[x] 에서, x가 가리키는 비밀 메모리 주소의 값을 읽어온다. (캐시에 있으니까 바로 읽힘)
Target = array2[Temp * 256] 읽어온 비밀 값을 인덱스로 사용해 array2 에 접근하고, array2의 특정 페이지가 캐시에 로드된다.
4. Rollback
array1_size가 도착함. x가 범위 밖인걸 깨달아 버린.. 롤백을 시도하지만 캐시에 올라간건 취소되지 않음.
5. Recovery
공격자는 array2의 모든 인덱스에 차례대로 접근하며 시간을 기록함.
접근 속도가 빠른 인덱스가 하나 있음. 그게 훔쳐온 값이다.
브라우저에는 보안을 위해 Sandbox라는 격리된 환경에서 JavaScript 코드를 실행한다.
덕분에 악의적인 페이지가 옆 탭에 있는 사이트의 데이터나 내 컴퓨터의 로컬 파일에 접근할 수 없음.
스펙터는 이 원칙을 깨버림.
브라우저 프로세스의 메모리 전체를 Speculative Execution으로 다 읽어버린다. (로컬 파일은 안전함)
ROP 랑 섞어서 쓰면 가젯 구성하기도 쉽다. 꼭 if 구문 뿐만 아니라 간접 분기도 공격 대상임.
사실 그냥 캐시도 같이 롤백해버리면 되는데, 하드웨어 Cost가 너무 비싸고 복잡해서 이게 안됨..
캐시를 롤백하려면 추측 실행 중에 변경된 캐시 라인 목록을 또 따로 둬야 하는데, 이게 가능하려면 CPU 안에 트랜지스터가 훨씬 더 많이 필요하고, 전기도 많이 먹고 발열도 심해진다.
즉, 지금까지 최적화한 설계를 다 뜯어 고쳐야 함
'Computer Science > Computer Security' 카테고리의 다른 글
| [SSS] Hardware-based Security Techniques (0) | 2025.11.27 |
|---|---|
| [SSS] Trusted Execution Environment 2 (0) | 2025.11.24 |
| [SSS] OPTEE - Open Portable Trusted Execution Environment (0) | 2025.11.20 |
| [SSS] Trusted Execution Environment (0) | 2025.11.14 |
| [SSS] Software Defense (0) | 2025.11.10 |
댓글
이 글 공유하기
다른 글
-
[SSS] Hardware-based Security Techniques
[SSS] Hardware-based Security Techniques
2025.11.27 -
[SSS] Trusted Execution Environment 2
[SSS] Trusted Execution Environment 2
2025.11.24 -
[SSS] OPTEE - Open Portable Trusted Execution Environment
[SSS] OPTEE - Open Portable Trusted Execution Environment
2025.11.20 -
[SSS] Trusted Execution Environment
[SSS] Trusted Execution Environment
2025.11.14