레포 틴이란 무엇이며 어떻게 작동합니까?


244

커널이나 프로세스 간 메모리 공개합니다 (을 완화하기 위해 유령의 공격), 리눅스는 커널 (1) 새로운 옵션으로 컴파일 될 것이다 , -mindirect-branch=thunk-extern소개 gcc소위를 통해 간접 호출을 수행 할 retpoline .

Google 검색이 최근에 사용한 것 (일반적으로 2018 년)으로 바뀌기 때문에 새로 발명 된 용어 인 것 같습니다.

리포 틀린이란 무엇이며 최근 커널 정보 공개 공격을 어떻게 방지합니까?


1 리눅스에만 국한된 것은 아니지만 유사하거나 동일한 구성이 다른 OS에 대한 완화 전략의 일부로 사용되는 것 같습니다 .


6
Google 의 흥미로운 지원 기사 .
sgbj

2
오, 그래서 발음됩니다 / ˌtræmpəˈlin / (미국식) 또는 / ˈtræmpəˌliːn / (영국식)
Walter Tross

2
당신은 이것이 리눅스 커널 이라고 언급 할 수도 있습니다 gcc. Linux Kernel Mailing List 사이트에서 lkml.org/lkml/2018/1/3/780 을 인식하지 못했습니다 .
PJTraill

@PJTraill-Linux 커널 태그 추가
RichVel

@ PJTraill-좋은 지적, 질문 텍스트를 업데이트했습니다. 필자는 Linux 커널에서 비교적 공개적인 개발 프로세스로 인해 처음으로 보았지만, 오픈 소스 및 폐쇄 소스 OS의 스펙트럼에서 동일하거나 유사한 기술이 완화로 사용되고 있다는 것은 의심의 여지가 없다. 그래서 나는 이것을 리눅스 고유의 것으로 보지 않지만 링크는 확실합니다.
BeeOnRope 2016 년

답변:


158

구글의 폴 터너 (Paul Turner)가 쓴 주석에서 sgbj가 언급 한 기사 는 다음을 훨씬 더 자세히 설명하지만 한 번 살펴 보겠다.

지금은 제한된 정보로 이것을 함께 정리할 수있는 한, 레토 폴린은 폴린 귀환 트램펄린입니다 CPU가 간접 점프의 대상을 추측하지 못하도록 실행되지 않는 무한 루프를 사용하는 입니다.

기본 접근 방식은 Andi Kleen의 커널 브랜치 에서 볼 수 있습니다이 문제를 해결하는 .

__x86.indirect_thunk메모리 주소 (내가 호출 할 ADDR)가 스택 위에 저장된 호출 대상을로드 하고 RET명령을 사용하여 점프를 실행 하는 새로운 호출을 소개합니다 . 썽크 자체는 NOSPEC_JMP / CALL 매크로를 사용하여 호출 되는데, 이는 많은 간접 호출과 점프를 대체하는 데 사용되었습니다. 매크로는 단순히 호출 대상을 스택에 배치하고 필요한 경우 반환 주소를 올바르게 설정합니다 (비선형 제어 흐름 참고).

.macro NOSPEC_CALL target
    jmp     1221f            /* jumps to the end of the macro */
1222:
    push    \target          /* pushes ADDR to the stack */
    jmp __x86.indirect_thunk /* executes the indirect jump */
1221:
    call    1222b            /* pushes the return address to the stack */
.endm

call간접 호출을 마치면 NOSPEC_CALL매크로 사용 후 제어 흐름이 계속 되어 일반 대신에 사용할 수 있도록 끝에 배치 가 필요합니다.call

썽크 자체는 다음과 같습니다.

    call retpoline_call_target
2:
    lfence /* stop speculation */
    jmp 2b
retpoline_call_target:
    lea 8(%rsp), %rsp 
    ret

제어 흐름은 여기에서 약간 혼란 스러울 수 있으므로 명확히하십시오.

  • call 현재 명령 포인터 (라벨 2)를 스택으로 푸시합니다.
  • lea스택 포인터에 8을 추가 하여 가장 최근에 푸시 된 쿼드 워드 (마지막 리턴 주소)를 효과적으로 버립니다 (라벨 2로). 그런 다음 스택의 맨 위가 실제 리턴 주소 ADDR을 다시 가리 킵니다.
  • ret*ADDR호출 포인터의 시작 부분으로 스택 포인터를 이동하여 재설정합니다.

결국이 전체 동작은 실제로로 바로 이동하는 것과 같습니다 *ADDR. 우리가 얻는 한 가지 이점은 call명령어를 실행할 때 리턴 명령문 (RSB)에 사용되는 분기 예측 변수 가 해당하는 것으로 가정한다는 것입니다.ret 명령문이 레이블 2로 이동 것입니다.

레이블 2 다음 부분은 실제로 실행되지 않으며 이론적으로 명령 파이프 라인을 JMP명령으로 채우는 무한 루프 일뿐 입니다. 사용하여 LFENCE, PAUSE또는 더 일반적으로 지시 명령 파이프 라인 스톨이되게 추측 실행에 어떤 전력과 시간 낭비의 CPU를 정지한다. 이는 retpoline_call_target에 대한 호출이 정상적으로 리턴 LFENCE될 경우 다음에 실행될 명령이되기 때문입니다. 이것은 또한 분기 예측자가 원래 반송 주소 (라벨 2)를 기반으로 예측하는 것입니다.

인텔 아키텍처 매뉴얼에서 인용하려면 :

LFENCE 이전의 명령어는 LFENCE 전에 메모리에서 가져올 수 있지만 LFENCE가 완료 될 때까지 실행되지 않습니다.

그러나 사양에 LFENCE와 PAUSE가 파이프 라인을 멈추게한다고 언급하지 않았으므로 여기서 줄 사이를 조금 읽습니다.

이제 원래 질문으로 돌아가십시오. 두 가지 아이디어의 조합으로 인해 커널 메모리 정보 공개가 가능합니다.

  • 추측이 잘못되었을 때 추측 실행은 부작용이 없어야하지만, 추측 실행은 여전히 ​​캐시 계층에 영향을 미칩니다 . 이는 메모리로드가 추론 적으로 실행될 때 여전히 캐시 라인이 제거 될 수 있음을 의미합니다. 캐시 계층의 이러한 변경은 동일한 캐시 세트에 맵핑 된 메모리에 대한 액세스 시간을 신중하게 측정하여 식별 할 수 있습니다.
    메모리 읽기의 소스 주소 자체가 커널 메모리에서 읽 혔을 때 임의의 비트의 임의 메모리가 누출 될 수도 있습니다.

  • Intel CPU의 간접 분기 예측기는 소스 명령의 최하위 12 비트 만 사용하므로 사용자 제어 메모리 주소를 사용하여 2 ^ 12 가능한 예측 기록을 모두 독살하기 쉽습니다. 그러면 커널 내에서 간접 점프가 예측 될 때 커널 권한으로 추측 적으로 실행될 수 있습니다. 캐시 타이밍 사이드 채널을 사용하면 임의의 커널 메모리가 누출 될 수 있습니다.

업데이트 : 커널 메일 링리스트 에서 RSB (Return Stack Buffer)가 비었을 때 retpoline이 브랜치 예측 문제를 완전히 완화하지 못한다고 생각하는 지속적인 논의가 있습니다. 최신 Intel 아키텍처 (Skylake +) 취약한 분기 대상 버퍼 (BTB)에 :

완화 전략으로서의 Retpoline은 공격자가 중독 될 수 있기 때문에 BTB에서 오는 예측을 사용하지 않기 위해 간접 분기를 반환으로 교환합니다. Skylake +의 문제점은 RSB 언더 플로가 BTB 예측을 사용하게되어 공격자가 추측을 제어 할 수 있다는 것입니다.


LFENCE 명령이 중요하다고 생각하지 않습니다. Google 구현에서는 PAUSE 명령을 대신 사용합니다. support.google.com/faqs/answer/7625886 인용 한 문서에 "실행하지 않음"이 아니라 "추측 적으로 실행되지 않습니다"라는 점에 유의하십시오.
로스 릿지

1
Google FAQ 페이지에서 : "위의 추론 루프의 일시 중지 명령은 정확성을 위해 필요하지 않습니다. 그러나 비생산적인 추론 실행은 프로세서에서 기능적인 단위를 덜 차지합니다." LFENCE가 핵심이라는 결론을지지하지 않습니다.
로스 릿지

@RossRidge 부분적으로 동의합니다. 이것은 CPU가 PAUSE / LFENCE 다음에 코드를 추측 적으로 실행하지 않도록 암시하는 무한 루프의 두 가지 가능한 구현처럼 보입니다. 그러나 LFENCE 추론 적 으로 실행되고 추측이 정확하여 롤백되지 않은 경우, 메모리로드가 완료된 후에 만 ​​실행된다는 주장과 모순됩니다. (그렇지 않으면, 추론 적으로 실행 된 전체 명령 세트는 사양을 충족시키기 위해 롤백 한 후 다시 실행되어야합니다)
Tobias Ribizel

1
이것은 장점이있다 push/ ret이 것을 하지 않는 반환 주소 예측 스택 불균형. lfence실제 반송 주소가 사용되기 이전으로 이동하는 한 가지 오해 가 있지만 call+ 수정을 사용하면 rsp균형을 이룰 수 ret있습니다.
Peter Cordes

1
죄송합니다, 장점 이상 push / ret(내 마지막 코멘트에서). re : 편집 : retpoline에을 포함하므로 RSB 언더 플로우가 불가능합니다 call. 커널 선점에서 컨텍스트 전환을 수행 한 경우 RSB에서 call시작하여 스케줄러로 실행을 다시 시작 합니다. 그러나 인터럽트 핸들러 ret는 RSB를 비우기에 충분한 s로 끝날 수 있습니다.
Peter Cordes

46

retpoline은 분기 타겟 주입 (방지하도록 설계 CVE-2017-5715 )을 이용한다. 이것은 커널의 간접 분기 명령을 사용하여 임의의 코드 덩어리를 추측 적으로 실행하는 공격입니다. 선택된 코드는 공격자에게 유용한 "가제트"입니다. 예를 들어, 캐시에 영향을주는 방식으로 커널 데이터가 유출되도록 코드를 선택할 수 있습니다. retpoline은 모든 간접 분기 명령어를 반환 명령어로 간단히 바꾸어 이러한 악용을 방지합니다.

retpoline의 핵심은 "ret"부분이라고 생각합니다. 간접 분기를 리턴 명령으로 대체하여 CPU가 악용 가능한 분기 예측기 대신 리턴 스택 예측기를 사용합니다. 간단한 푸시와 리턴 명령이 대신 사용되면 추측 적으로 실행될 코드는 코드가 될 것입니다.이 함수는 결국 어쨌든 공격자에게 유용한 일부 장치가 아닙니다. 트램펄린 부분의 주요 이점은 리턴 스택을 유지하는 것 같습니다. 따라서 함수가 실제로 호출자에게 리턴 할 때 올바르게 예측됩니다.

분기 대상 주입의 기본 개념은 간단합니다. CPU가 분기 대상 버퍼에 분기의 소스 및 대상의 전체 주소를 기록하지 않는다는 사실을 이용합니다. 따라서 공격자는 고유 한 주소 공간에서 점프를 사용하여 버퍼를 채울 수 있으며, 이는 특정 간접 점프가 커널 주소 공간에서 실행될 때 예측 적중을 초래합니다.

retpoline은 커널 정보 공개를 직접 방해하지 않으며, 정보를 공개 할 가젯을 추측 적으로 실행하는 데 간접 분기 명령어가 사용되는 것만 방지합니다. 공격자가 가제트를 추측 적으로 실행하는 다른 방법을 찾을 수 있다면, retpoline은 공격을 막지 않습니다.

Spectre Attacks (종이 공격) : Paul Kocher, Daniel Genkin, Daniel Gruss, Werner Haas, Mike Hamburg, Moritz Lipp, Stefan Mangard, Thomas Prescher, Michael Schwarz 및 Yuval Yarom의 투기 적 사형 집행 :

간접 지점 활용 ROP (return oriented programming)에서이 방법으로 공격자는 가젯을 선택합니다. 피해자의 주소 공간에서 주소를 하고 피해자가 가제트를 추측 적으로 실행하도록 영향을줍니다. ROP와 달리 공격자는 피해자 코드의 취약점에 의존하지 않습니다. 대신 공격자는 BTB (Branch Target Buffer)를 교육하여 분기를 간접 분기 명령에서 가제트 주소로 잘못 예측하여 가제트를 추론 적으로 실행합니다. 추론 적으로 실행 된 명령어는 폐기되지만 캐시에 미치는 영향은 되 돌리지 않습니다. 이러한 효과는 가제트에서 민감한 정보를 유출하는 데 사용할 수 있습니다. 가제트를 신중하게 선택하면이 방법을 사용하여 희생자로부터 임의의 메모리를 읽는 방법을 알 수 있습니다.

BTB를 잘못 훈련시키기 위해 공격자는 피해자의 주소 공간에서 가제트의 가상 주소를 찾은 다음이 주소에 대한 간접 분기를 수행합니다. 이 교육은 공격자의 주소 공간에서 수행되며 공격자의 주소 공간에있는 가젯 주소에있는 내용은 중요하지 않습니다. 필요한 것은 지점이 동일한 대상 가상 주소를 사용하도록 훈련시키는 데 사용됩니다. (실제로 공격자가 예외를 처리하는 한 공격자의 주소 공간에있는 가젯의 가상 주소에 코드가 매핑되지 않은 경우에도 공격이 작동 할 수 있습니다.) 소스 주소와 완전히 일치 할 필요도 없습니다. 교육에 사용 된 지점 및 대상 지점의 주소 따라서 공격자는 교육을 설정하는 데 상당한 유연성을 갖습니다.

Google의 Project Zero 팀 이 사이드 채널 을 사용하여 권한있는 메모리 읽기 라는 제목의 블로그 항목은 분기 대상 삽입을 사용하여 실제 악용을 만드는 방법에 대한 또 다른 예를 제공합니다.


9

이 질문은 얼마 전에 요청되었으며 더 새로운 답변이 필요합니다.

행정상 개요 :

"레토 폴린"서열은 간접 분기가 추론 적 실행으로부터 분리 될 수있게하는 소프트웨어 구조이다. 이는 운영 체제 또는 하이퍼 바이저 구현과 같은 민감한 바이너리를 간접 분기에 대한 분기 대상 주입 공격으로부터 보호하기 위해 적용될 수 있습니다.

" ret poline " 이라는 단어 는 " rel poline "이 "relative call"과 "trampoline"에서 만들어진 것과 마찬가지로 "return"과 "trampoline"이라는 단어 의 포트만 토 입니다 . 리턴 연산을 사용하여 구성된 트램폴린 구조로, 관련된 추론 실행이 끝없이 "튀길"것임을 비 유적으로 보장합니다.

커널 또는 프로세스 간 메모리 공개 (스펙터 공격)를 방지하기 위해 Linux 커널 [1] 은 새로운 옵션으로 컴파일 -mindirect-branch=thunk-extern되어 gcc에 도입되어 소위 레토 폴린을 통해 간접 호출을 수행합니다.

[1] 그러나 리눅스에만 국한된 것은 아니지만 유사하거나 동일한 구조가 다른 OS에 대한 완화 전략의 일부로 사용되는 것 같습니다.

이 컴파일러 옵션을 사용하면 CVE-2017-5715에 필요한 마이크로 코드 업데이트가있는 영향을받는 프로세서의 Spectre V2 보호합니다 . 커널뿐만 아니라 모든 코드에서 ' 작동 '하지만 "비밀"을 포함하는 코드 만 공격 할 가치가 있습니다.

Google 검색이 최근에 사용한 것 (일반적으로 2018 년)으로 바뀌기 때문에 새로 발명 된 용어 인 것 같습니다.

LLVM 컴파일러는 했다 -mretpoline이후 스위치를 2018년 1월 4일 전에 . 그 날짜는 취약점이 공개적으로보고 된 시점입니다 . GCC 2018 년 1 월 7 일에 패치를 제공했습니다 .

CVE 날짜는이 취약점이 2017 년 에 ' 발견됨 '을 암시 하지만 지난 20 년 동안 제조 된 일부 프로세서에 영향을 미칩니다 (따라서 오래 전에 발견되었을 가능성이 있음).

리포 틀린이란 무엇이며 최근 커널 정보 공개 공격을 어떻게 방지합니까?

먼저 몇 가지 정의가 있습니다.

  • 트램폴린 -때때로 간접 점프 벡터 트램폴린이라고도하는 서비스 위치, I / O 루틴 등을 방해하는 주소를 보유한 메모리 위치입니다. GCC는 전통적으로 중첩 함수의 주소를 가져올 때 런타임에 실행 가능한 트램펄린을 만들어 중첩 함수를 지원했습니다. 이것은 일반적으로 포함 함수의 스택 프레임에서 스택에 상주하는 작은 코드입니다. 트램펄린은 정적 체인 레지스터를로드 한 다음 중첩 함수의 실제 주소로 점프합니다.

  • 썽크 -썽 크는 추가 계산을 다른 서브 루틴에 주입하는 데 사용되는 서브 루틴입니다. 썽 크는 주로 결과가 필요할 때까지 계산을 지연 시키거나 다른 서브 루틴의 시작 또는 끝에 연산을 삽입하는 데 사용됩니다.

  • 메모 - 메모 기능은 특정 입력 세트에 해당하는 결과를 "기억"합니다. 기억 된 입력이있는 후속 호출은 기억 된 결과를 다시 계산하지 않고 반환하므로 주어진 매개 변수를 사용하여 첫 번째 호출을 제외한 모든 호출에서 해당 매개 변수를 사용하여 호출 한 주요 비용을 제거합니다.

아주 거칠게하는 retpoline는 A는 트램 폴린 A를 반환 A와 썽크 '에 탈취 ' 메모이 제이션 간접 분기 예측한다.

소스 : retpoline에는 Intel에 대한 PAUSE 명령이 포함되어 있지만 해당 프로세서에서 PAUSE 명령이 직렬화 명령이 아니기 때문에 AMD에 LFENCE 명령이 필요합니다. 따라서 pause / jmp 루프는 반환 대기를 초과하여 추측되는 초과 전력을 사용합니다 올바른 목표를 잘못 예측합니다.

Arstechnica 는 문제에 대한 간단한 설명을 제공합니다.

"각 프로세서에는 아키텍처 동작 (명령이 작동하는 방식과 프로그래머가 프로그램 작성에 의존하는 방식을 설명하는 문서화 된 동작)과 마이크로 아키텍처 동작 (아키텍처의 실제 구현 방식)이 있습니다. 이들은 미묘한 방식으로 분기 될 수 있습니다. 예를 들어, 구조적으로 메모리의 특정 주소에서 값을로드하는 프로그램은로드를 수행하기 전에 주소가 알려질 때까지 기다립니다. 그러나 아키텍처는 프로세서가 주소를 추측 할 수 있도록 추측 적으로 시도 할 수 있습니다. 어떤 주소를 사용해야하는지 확실하지 않더라도 메모리에서 값을로드합니다 (느리게).

프로세서가 잘못 추측하면 추측 한 값을 무시하고 이번에는 올바른 주소로로드를 다시 수행합니다. 따라서 구조적으로 정의 된 동작이 유지됩니다. 그러나이 잘못된 추측은 프로세서의 다른 부분, 특히 캐시의 내용을 방해합니다. 이러한 마이크로 아키텍처 교란은 캐시에 있거나 없어야하는 데이터에 액세스하는 데 걸리는 시간을 타이밍으로 감지하고 측정 할 수 있으므로 악의적 인 프로그램이 메모리에 저장된 값을 추론 할 수 있습니다. "

인텔 백서 : " 레토 폴린 : 분기 대상 주입 완화 "( .PDF ) :

"레토 폴린 시퀀스는 프로세서의 추론 적 실행이"간접 분기 예측기 "(프로그램 흐름 예측 방법 중 하나)를 사용하여 익스플로잇 (지점 타겟 주입의 5 가지 요소 중 4 번째 요소 만족)에 의해 제어되는 주소를 추측하는 것을 방지합니다 (스펙트 변종 2 ) 위에 나열된 악용 구성).

요소 4는 "이 악용은이 간접 브랜치에 성공적으로 영향을 주어 가제트를 잘못 예측하고 실행합니다.이 악용에 의해 선택된이 가젯은 일반적으로 캐시 타이밍에 의해 사이드 채널을 통해 비밀 데이터를 유출합니다."

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.