IT/하드웨어 보안

[Meltdown and Spectre] 엎질러진 물

kykyky 2024. 6. 12. 01:58

💡Meltdown

: 비순차실행을 이용해, user application 주제에 kernel 데이터를 읽는 공격 

 

발생 원리

✅1: user mode application이 커널 영역의 메모리 주소에 접근 시도

 

예시 1

예시 2

ptr: kernel 영역의 주소.

 

✅2: 비순차실행

1.에 의해 아래 과정이 일어나야 한다.

i) 가상 주소의 Address translation: 가상 주소인 (kernel)을 물리 주소로 변환
ii) PTE 로드: 주소 변환을 위한 Page Table 참조
iii) permission check: 해당 물리 주소에 접근 권한이 있는지를 PTE의 권한 정보를 바탕으로 체크
    -> 권한이 없네! -> page fault exception

 

그런데 이것은 시간이 한참 걸리므로,

비순차실행에 의해, 이 page fault가 일어나기 전까지 그이후의 명령어들이 우선 실행됨


이 명령어들은 커널에서 읽어온 값들을 가지고 연산 등을 하며 CPU cache에도 흔적을 남김 
이 값들은 아까 커널에서 읽어온 값에 따라 다름.

 

예시 1 

 

label 1의 결과

 

label 2의 결과



예시 2

 

value의 single bit = 0이었다면 -> index2 = 0x100 -> data[0x100]을 cache함

value의 single bit = 1이었다면 -> index2 = 0x300 -> data[0x300]을 cache함

 

✅3: 이제서야 page fault exception이 발생하고 exception handler에 의해 핸들링됨

: user application은 강제 종료되고, 비순차실행 때의 명령어들의 결과가 rollback됨

그러나, cache에 있는 흔적들은 사라지지 않음!

 

✅4: flush+reload

이 흔적을 바탕으로 attacker는 flush+reload를 이용하여 kernel 데이터를 역추적할 수 있다.

 

예시 2

data[0x100]과 data[0x300]에 대한 access time (delta1, delta2)를 측정하고,

이전에 cache되어있던 것인지 추측할 수 있음

value의 single bit = 0이었다면 -> index2 = 0x100 -> delta1 < delta2

value의 single bit = 1이었다면 -> index2 = 0x300 -> delta1 > delta2

 

※ 이 예시에서는 한번에 1 bit의 정보만을 얻었지만, 이를 확장해서 1 byte의 정보를 얻을 수도 있다.

 

mitigation

✅KPTI(Kernel page table isolation)

 

: 취약한 원인은 user process와 kernel이 memory에 하나의 주소 공간에 함께 매핑되어 있다는 것이므로,

각각이 사용하는 공간을 분리시킴

문제점: user process와 kernel 간 context switch가 발생할 때마다 page table이 매번 업데이트되어야 하므로 성능 저하 

 

✅PCID, ASID

: page table을 매번 업데이트하지 않고, CPU 내부에서 caching하여 업데이트를 최소화

 

✅Intel 프로세서에만 이 취약점이 있으며, AMD에서는 없다.

 

💡Spectre-v1

: 분기예측을 이용해, bound check를 우회하는 공격

 

발생 원리

✅1: 분기를 일으키는 bound check을 만남

✅2: speculative execution

speculative execution

: 분기 명령어는 시간이 오래 걸리므로, 미리 분기할 주소를 예측하여 이 주소로 미리 분기 실행하는 최적화 방식.

예측이 틀리면 실행했던 명령어를 모두 취소하고 다시 분기

이를 위해선,

이전 실행 결과 히스토리에 기반해 예측하는 분기 명령어가

예측 실패를 하도록 일부러 학습시켜야 한다.

 

이 때문에 bound check 우회 (bound 밖의 메모리를 가지고 명령어들(gadget) 실행되버림)

 

✅3: 예측이 틀렸으니 명령어 취소됨

그러나 cache의 흔적은 남아있음 

 

✅4: flush+reload

bound 밖의 값 (untrusted_offset)

-> trusted_data array의 금지된 offset에 접근

-> 그 값은 trusted_value

-> 이 값의 일부분씩을 mask를 이용해 추출

-> 이걸 offset으로 하여  data array 접근

-> 그 값은 tmp

=> 역추적하기: 어느 cache 부분이 load되었는지를 통해 trusted_value 추측

 

mitigation

✅HW ㅡ 대응 어려움

 

분기예측이 원인인데,

대부분의 아키텍처는 분기예측을 하고, 이걸 안할 수는 없기 때문

 

✅SW ㅡ serializing instruction

: gadget 내에서 분기예측이 일어나지 않게끔 함