it-swarm-vi.com

Retpoline là gì và nó hoạt động như thế nào?

Để giảm thiểu chống lại việc tiết lộ bộ nhớ hoặc xử lý bộ nhớ chéo (tấn công Spectre ), nhân Linux1 sẽ được biên dịch với một tùy chọn mới , -mindirect-branch=thunk-extern được giới thiệu với gcc để thực hiện các cuộc gọi gián tiếp thông qua cái gọi là retpoline .

Đây dường như là một thuật ngữ mới được phát minh khi một tìm kiếm Google chỉ xuất hiện lần sử dụng gần đây (nói chung là tất cả vào năm 2018).

Retpoline là gì và làm thế nào để ngăn chặn các cuộc tấn công tiết lộ thông tin hạt nhân gần đây?


1 Tuy nhiên, nó không phải là Linux cụ thể - cấu trúc tương tự hoặc giống hệt nhau dường như được sử dụng như một phần của chiến lược giảm thiểu trên các HĐH khác.

221
BeeOnRope

Bài viết được đề cập bởi sgbj trong các nhận xét được viết bởi Google Paul Turner giải thích những điều sau đây chi tiết hơn nhiều, nhưng tôi sẽ cho nó một shot:

Theo như tôi có thể ghép nó từ thông tin hạn chế vào lúc này, một retpoline là một return trampolinesử dụng một vòng lặp vô hạn không bao giờ được thực thi để ngăn CPU suy đoán mục tiêu của một bước nhảy gián tiếp.

Cách tiếp cận cơ bản có thể được nhìn thấy trong nhánh hạt nhân của Andi Kleen giải quyết vấn đề này:

Nó giới thiệu cuộc gọi __x86.indirect_thunk mới tải mục tiêu cuộc gọi có địa chỉ bộ nhớ (mà tôi sẽ gọi ADDRname__) được lưu trữ trên đỉnh ngăn xếp và thực hiện bước nhảy bằng cách sử dụng lệnh RETname__. Bản thân thunk sau đó được gọi bằng cách sử dụng macro NOSPEC_JMP/CALL , được sử dụng để thay thế nhiều cuộc gọi và nhảy gián tiếp (nếu không phải tất cả). Macro chỉ cần đặt mục tiêu cuộc gọi lên ngăn xếp và đặt địa chỉ trả về chính xác, nếu cần (lưu ý luồng điều khiển phi tuyến tính):

.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

Cuối cùng, việc đặt calllà cần thiết để khi cuộc gọi gián tiếp kết thúc, luồng điều khiển tiếp tục đằng sau việc sử dụng macro NOSPEC_CALL, do đó, nó có thể được sử dụng thay cho callthông thường

Bản thân thunk trông như sau:

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

Luồng điều khiển có thể hơi khó hiểu ở đây, vì vậy hãy để tôi làm rõ:

  • callđẩy con trỏ lệnh hiện tại (nhãn 2) vào ngăn xếp.
  • leathêm 8 vào con trỏ ngăn xếp, loại bỏ hiệu quả tứ giác được đẩy gần đây nhất, là địa chỉ trả lại cuối cùng (vào nhãn 2). Sau này, đỉnh của các điểm xếp chồng tại địa chỉ trả lại thực ADDR một lần nữa.
  • retnhảy tới *ADDR và đặt lại con trỏ ngăn xếp về đầu ngăn xếp cuộc gọi.

Cuối cùng, toàn bộ hành vi này thực tế tương đương với việc nhảy trực tiếp vào *ADDR. Lợi ích chúng ta nhận được là bộ dự báo nhánh được sử dụng cho các câu lệnh trả về (Return Stack Buffer, RSB), khi thực hiện lệnh callname__, giả sử rằng câu lệnh rettương ứng sẽ nhảy đến nhãn 2.

Phần sau nhãn 2 thực sự không bao giờ được thực thi, nó chỉ đơn giản là một vòng lặp vô hạn mà theo lý thuyết sẽ lấp đầy đường dẫn lệnh bằng các hướng dẫn JMPname__. Bằng cách sử dụng LFENCEname __, PAUSEhoặc nói chung là một lệnh làm cho đường dẫn lệnh bị đình trệ sẽ ngăn CPU lãng phí bất kỳ năng lượng và thời gian nào cho việc thực hiện đầu cơ này. Điều này là do trong trường hợp lệnh gọi retpoline_call_target sẽ trở lại bình thường, LFENCEsẽ là hướng dẫn tiếp theo được thực thi. Đây cũng là những gì người dự đoán chi nhánh sẽ dự đoán dựa trên địa chỉ trả lại ban đầu (nhãn 2)

Để trích dẫn từ hướng dẫn kiến ​​trúc của Intel:

Các hướng dẫn tuân theo một LỚN có thể được lấy từ bộ nhớ trước khi có LẦN, nhưng chúng sẽ không được thực thi cho đến khi hoàn thành.

Tuy nhiên, xin lưu ý rằng thông số kỹ thuật không bao giờ đề cập đến việc LỚN và PAUSE khiến đường ống bị đình trệ, vì vậy tôi đang đọc một chút giữa các dòng ở đây.

Bây giờ trở lại câu hỏi ban đầu của bạn: Việc tiết lộ thông tin bộ nhớ kernel là có thể do sự kết hợp của hai ý tưởng:

  • Mặc dù thực thi đầu cơ phải không có hiệu ứng phụ khi đầu cơ sai, thực thi đầu cơ vẫn ảnh hưởng đến hệ thống phân cấp bộ đệm. Điều này có nghĩa là khi tải bộ nhớ được thực thi theo suy đoán, nó vẫn có thể khiến một dòng bộ đệm bị xóa. Thay đổi này trong hệ thống phân cấp bộ đệm có thể được xác định bằng cách cẩn thận đo thời gian truy cập vào bộ nhớ được ánh xạ vào cùng một bộ bộ đệm.
    [.___.] Bạn thậm chí có thể rò rỉ một số bit của bộ nhớ tùy ý khi địa chỉ nguồn của bộ nhớ đã đọc chính từ bộ nhớ kernel.

  • Bộ dự báo nhánh gián tiếp của CPU Intel chỉ sử dụng 12 bit thấp nhất của lệnh nguồn, do đó dễ dàng đầu độc tất cả 2 ^ 12 lịch sử dự đoán có thể có với các địa chỉ bộ nhớ do người dùng kiểm soát. Những điều này sau đó có thể, khi bước nhảy gián tiếp được dự đoán trong kernel, được thực hiện một cách đặc biệt với các đặc quyền kernel. Sử dụng kênh bên thời gian bộ đệm, do đó bạn có thể rò rỉ bộ nhớ kernel tùy ý.

CẬP NHẬT: Trên danh sách gửi thư kernel , có một cuộc thảo luận đang diễn ra khiến tôi tin rằng các retpolines không giảm thiểu hoàn toàn các vấn đề dự đoán nhánh, vì khi Bộ đệm ngăn xếp trả lại (RSB) trống, gần đây hơn Kiến trúc (Skylake +) rơi trở lại Bộ đệm mục tiêu nhánh dễ bị tổn thương (BTB):

Retpoline như một chiến lược giảm thiểu hoán đổi các nhánh gián tiếp để trả lại, để tránh sử dụng các dự đoán đến từ BTB, vì chúng có thể bị kẻ tấn công đầu độc. Vấn đề với Skylake + là dòng chảy RSB rơi trở lại sử dụng dự đoán BTB, cho phép kẻ tấn công kiểm soát đầu cơ.

145
Tobias Ribizel

A retpoline được thiết kế để bảo vệ chống lại tiêm mục tiêu nhánh (CVE-2017-5715) khai thác. Đây là một cuộc tấn công trong đó một lệnh nhánh gián tiếp trong kernel được sử dụng để buộc thực thi đầu cơ của một đoạn mã tùy ý. Mã được chọn là một "tiện ích" bằng cách nào đó hữu ích cho kẻ tấn công. Ví dụ mã có thể được chọn để sẽ rò rỉ dữ liệu kernel thông qua cách nó ảnh hưởng đến bộ đệm. Retpoline ngăn chặn việc khai thác này bằng cách thay thế tất cả các lệnh rẽ nhánh gián tiếp bằng lệnh quay lại.

Tôi nghĩ chìa khóa của retpoline chỉ là phần "ret", nó thay thế nhánh gián tiếp bằng lệnh quay lại để CPU sử dụng bộ dự báo ngăn xếp trở lại thay vì bộ dự báo nhánh có thể khai thác. Nếu một lệnh Đẩy và trả lại đơn giản được sử dụng thay thế thì mã sẽ được thực thi theo suy đoán sẽ là mã mà cuối cùng hàm sẽ quay trở lại, không phải là một tiện ích hữu ích cho kẻ tấn công. Lợi ích chính của phần tấm bạt lò xo dường như là duy trì ngăn xếp trả về để khi chức năng thực sự quay trở lại với người gọi của nó, điều này được dự đoán chính xác.

Ý tưởng cơ bản đằng sau việc tiêm mục tiêu chi nhánh rất đơn giản. Nó lợi dụng thực tế là CPU không ghi lại địa chỉ đầy đủ của nguồn và đích của các nhánh trong bộ đệm đích của nhánh. Vì vậy, kẻ tấn công có thể lấp đầy bộ đệm bằng cách sử dụng các bước nhảy trong không gian địa chỉ của chính nó sẽ dẫn đến các lần truy cập dự đoán khi một bước nhảy gián tiếp cụ thể được thực hiện trong không gian địa chỉ kernel.

Lưu ý rằng retpoline không ngăn chặn việc tiết lộ thông tin hạt nhân trực tiếp, nó chỉ ngăn chặn các hướng dẫn chi nhánh gián tiếp được sử dụng để thực thi một cách cụ thể một tiện ích sẽ tiết lộ thông tin. Nếu kẻ tấn công có thể tìm thấy một số phương tiện khác để thực thi tiện ích thì retpoline sẽ không ngăn chặn cuộc tấn công.

Bài báo Tấn công bóng ma: Khai thác thực thi đầu cơ của Paul Kocher, Daniel Genkin, Daniel Gruss, Werner Haas, Mike Hamburg, Moritz Lipp, Stefan Mangard, Thomas Prescher, Michael Schwarz và Yuval Yarom đưa ra tổng quan sau đây về cách gián tiếp chi nhánh có thể được khai thác:

Khai thác các nhánh gián tiếp. Vẽ từ lập trình hướng trở lại (ROP), trong phương thức này, kẻ tấn công chọn một tiện ích từ không gian địa chỉ của nạn nhân và ảnh hưởng đến nạn nhân để thực thi tiện ích theo cách cụ thể. Không giống như ROP, kẻ tấn công không dựa vào lỗ hổng trong mã nạn nhân. Thay vào đó, kẻ tấn công huấn luyện Bộ đệm mục tiêu chi nhánh (BTB) để dự đoán sai một nhánh từ một lệnh rẽ nhánh gián tiếp đến địa chỉ của tiện ích, dẫn đến việc thực hiện đầu cơ của tiện ích. Trong khi các hướng dẫn được thực hiện theo suy đoán bị bỏ qua, các hiệu ứng của chúng trên bộ đệm không được hoàn nguyên. Những hiệu ứng này có thể được sử dụng bởi các tiện ích để rò rỉ thông tin nhạy cảm. Chúng tôi chỉ ra cách thức, với một lựa chọn cẩn thận của một tiện ích, phương pháp này có thể được sử dụng để đọc bộ nhớ tùy ý từ nạn nhân.

Để đánh lừa BTB, kẻ tấn công tìm địa chỉ ảo của tiện ích trong không gian địa chỉ nạn nhân, sau đó thực hiện các nhánh gián tiếp đến địa chỉ này. Việc huấn luyện này được thực hiện từ không gian địa chỉ của kẻ tấn công, và không có vấn đề gì ở địa chỉ tiện ích trong không gian địa chỉ của kẻ tấn công; tất cả những gì được yêu cầu là nhánh được sử dụng cho các nhánh đào tạo để sử dụng cùng một địa chỉ ảo đích. (Trên thực tế, miễn là kẻ tấn công xử lý các trường hợp ngoại lệ, cuộc tấn công có thể hoạt động ngay cả khi không có mã được ánh xạ tại địa chỉ ảo của tiện ích trong không gian địa chỉ của kẻ tấn công.) Cũng không cần phải khớp hoàn toàn địa chỉ nguồn. của chi nhánh được sử dụng cho đào tạo và địa chỉ của chi nhánh được nhắm mục tiêu. Do đó, kẻ tấn công có sự linh hoạt đáng kể trong việc thiết lập đào tạo.

Một mục blog có tiêu đề Đọc bộ nhớ đặc quyền với kênh phụ bởi nhóm Project Zero tại Google cung cấp một ví dụ khác về cách tiêm mục tiêu nhánh có thể được sử dụng để tạo khai thác hoạt động.

42
Ross Ridge

Câu hỏi này đã được hỏi cách đây một thời gian và xứng đáng có câu trả lời mới hơn.

Tóm tắt điều hành :

Các chuỗi Retpoline của Viking là một cấu trúc phần mềm cho phép tách các nhánh gián tiếp khỏi thực thi đầu cơ. Điều này có thể được áp dụng để bảo vệ các nhị phân nhạy cảm (chẳng hạn như triển khai hệ điều hành hoặc trình ảo hóa) khỏi các cuộc tấn công tiêm mục tiêu nhánh chống lại các nhánh gián tiếp của chúng.

Từ " ret poline " là một portmanteau của các từ "return" và "trampoline", giống như sự cải tiến " rel poline "được tạo ra từ" cuộc gọi tương đối "và" tấm bạt lò xo ". Đó là một cấu trúc tấm bạt lò xo được xây dựng bằng cách sử dụng các hoạt động hoàn trả, điều này cũng đảm bảo theo nghĩa bóng rằng bất kỳ thực thi đầu cơ liên quan nào cũng sẽ nảy lên không ngừng.

Để giảm thiểu việc tiết lộ bộ nhớ hoặc xử lý bộ nhớ chéo (tấn công Spectre), nhân Linux [1] sẽ được biên dịch với một tùy chọn mới, -mindirect-branch=thunk-extern được giới thiệu cho gcc để thực hiện các cuộc gọi gián tiếp thông qua cái gọi là retpoline.

[1] Tuy nhiên, nó không phải là đặc thù của Linux - cấu trúc tương tự hoặc giống hệt nhau dường như được sử dụng như một phần của chiến lược giảm thiểu trên các HĐH khác.

Việc sử dụng tùy chọn trình biên dịch này only bảo vệ chống lại Spectre V2 trong các bộ xử lý bị ảnh hưởng có cập nhật vi mã cần thiết cho CVE-2017-5715. Nó sẽ 'hoạt động' trên bất kỳ mã nào (không chỉ là kernel), mà chỉ mã chứa "bí mật" mới đáng để tấn công.

Đây dường như là một thuật ngữ mới được phát minh khi một tìm kiếm Google chỉ xuất hiện lần sử dụng gần đây (nói chung là tất cả vào năm 2018).

Trình biên dịch LLVM đã có công tắc -mretpoline kể từ trước ngày 4 tháng 1 năm 2018 . Ngày đó là khi lỗ hổng được báo cáo công khai đầu tiên . GCC đã cung cấp các bản vá của họ ngày 7 tháng 1 năm 2018.

Ngày CVE cho thấy lỗ hổng bảo mật là 'được phát hiện' vào năm 2017, nhưng nó ảnh hưởng đến một số bộ xử lý được sản xuất trong hai thập kỷ qua (do đó nó có thể được phát hiện từ lâu).

Retpoline là gì và làm thế nào để ngăn chặn các cuộc tấn công tiết lộ thông tin hạt nhân gần đây?

Đầu tiên, một vài định nghĩa:

  • Trampoline - Đôi khi được gọi là trampolines vectơ nhảy gián tiếp là các vị trí bộ nhớ chứa các địa chỉ chỉ ra các thói quen dịch vụ bị gián đoạn, các thói quen I/O, v.v. . GCC có truyền thống các hàm lồng nhau được hỗ trợ bằng cách tạo ra một tấm bạt lò xo thực thi trong thời gian chạy khi lấy địa chỉ của hàm lồng nhau. Đây là một đoạn mã nhỏ thường nằm trên ngăn xếp, trong khung ngăn xếp của hàm chứa. Tấm bạt lò xo tải thanh ghi chuỗi tĩnh và sau đó nhảy đến địa chỉ thực của hàm lồng nhau.

  • Thunk - Thunk là một chương trình con được sử dụng để tiêm một phép tính bổ sung vào một chương trình con khác. Thunk chủ yếu được sử dụng để trì hoãn một phép tính cho đến khi cần kết quả của nó hoặc để chèn các hoạt động ở đầu hoặc cuối của chương trình con khác

  • Ghi nhớ - Hàm ghi nhớ "ghi nhớ" các kết quả tương ứng với một số bộ đầu vào cụ thể. Các cuộc gọi tiếp theo với các đầu vào đã nhớ trả về kết quả đã nhớ thay vì tính toán lại, do đó loại bỏ chi phí chính của một cuộc gọi với các tham số đã cho từ tất cả trừ cuộc gọi đầu tiên được thực hiện cho hàm với các tham số đó.

Rất đại khái, a retpoline là atrampolinevới a return as a thunk , to 'spoil 'ghi nhớtrong bộ dự báo nhánh gián tiếp.

Nguồn : Retpoline bao gồm một lệnh PAUSE cho Intel, nhưng một lệnh LFENT là cần thiết cho AMD vì trên bộ xử lý đó, lệnh PAUSE không phải là một lệnh nối tiếp, do đó, vòng lặp tạm dừng/jmp sẽ sử dụng năng lượng dư thừa như được suy đoán quá chờ đợi để trở về dự đoán sai cho mục tiêu chính xác.

Arstechnica có một lời giải thích đơn giản về vấn đề:

"Mỗi bộ xử lý có một hành vi kiến ​​trúc (hành vi được ghi lại để mô tả cách thức hoạt động của các hướng dẫn và các lập trình viên phụ thuộc vào việc viết chương trình của họ) và hành vi vi kiến ​​trúc (cách thực hiện kiến ​​trúc thực tế). Ví dụ, về mặt kiến ​​trúc, một chương trình tải một giá trị từ một địa chỉ cụ thể trong bộ nhớ sẽ đợi cho đến khi biết được địa chỉ đó trước khi thử thực hiện tải. tải giá trị từ bộ nhớ (chậm) ngay cả trước khi hoàn toàn chắc chắn nên sử dụng địa chỉ nào.

Nếu bộ xử lý đoán sai, nó sẽ bỏ qua giá trị đoán và thực hiện tải lại, lần này với địa chỉ chính xác. Các hành vi được xác định kiến ​​trúc được bảo tồn. Nhưng dự đoán sai đó sẽ làm phiền các bộ phận khác của bộ xử lý, đặc biệt là nội dung của bộ đệm. Những nhiễu loạn vi kiến ​​trúc này có thể được phát hiện và đo lường bằng cách xác định thời gian cần thiết để truy cập dữ liệu cần (hoặc không nên) trong bộ đệm, cho phép một chương trình độc hại suy luận về các giá trị được lưu trong bộ nhớ. ".

Từ bài viết của Intel: " Retpoline: Giảm thiểu mục tiêu tiêm chi nhánh " ( .PDF ):

"Chuỗi retpoline ngăn không cho thực thi đầu cơ của bộ xử lý sử dụng" bộ dự báo nhánh gián tiếp "(một cách dự đoán luồng chương trình) để suy đoán một địa chỉ được kiểm soát bởi một khai thác (đáp ứng yếu tố 4 trong năm yếu tố của tiêm mục tiêu nhánh (Biến thể Spectre 2 ) khai thác thành phần được liệt kê ở trên). ".

Lưu ý, yếu tố 4 là: "Việc khai thác phải ảnh hưởng thành công đến nhánh gián tiếp này đến dự đoán sai và thực thi một tiện ích. Tiện ích này, được khai thác chọn, rò rỉ dữ liệu bí mật qua kênh bên, thường là theo thời gian bộ đệm.".

5
Rob