Ngăn xếp tràn bộ đệm
từ Wikipedia, bách khoa toàn thư miễn phí
Đối với các ứng dụng khác, xem ngăn xếp tràn (định hướng). Trong phần mềm, một tràn bộ đệm hoặc đống đệm tràn xảy ra khi một chương trình viết cho một địa chỉ bộ nhớ của chương trình cuộc gọi stack bên ngoài của các dữ liệu nhằm mục đích cơ cấu; thường là một bộ đệm chiều dài cố định. [1] [2] lỗi tràn bộ đệm gây ra khi một chương trình ghi dữ liệu nhiều hơn đến một bộ đệm nằm trên stack hơn là thực sự phân bổ cho bộ đệm. Điều này hầu như luôn luôn dẫn đến tham nhũng của dữ liệu liền kề trên stack, và trong trường hợp tràn bộ nhớ đã được kích hoạt bởi sai lầm, thường sẽ gây ra các chương trình sụp đổ hoặc hoạt động không chính xác. Ngăn xếp tràn bộ đệm là một loại của sự cố lập trình tổng quát hơn được gọi là tràn bộ đệm (hoặc đệm tràn ngập). [1] Overfilling một bộ đệm trên stack có nhiều khả năng làm hỏng chương trình thực hiện hơn overfilling một bộ đệm trên heap vì stack chứa trở lại địa chỉ cho tất cả các cuộc gọi chức năng hoạt động. Ngăn xếp tràn bộ nhớ đệm có thể được gây ra cố ý như là một phần của một cuộc tấn công được gọi là chồng đập. Nếu chương trình bị ảnh hưởng đang chạy với ưu đãi đặc biệt, hoặc chấp nhận dữ liệu từ máy chủ mạng không tin cậy (ví dụ như một máy chủ web) thì lỗi là một lỗ hổng bảo mật tiềm năng. Nếu ngăn xếp đệm được làm đầy với dữ liệu được cung cấp từ một người sử dụng không đáng tin cậy sau đó người sử dụng có thể bị hỏng ngăn xếp theo cách như vậy là để đưa mã thực thi vào chương trình đang chạy và kiểm soát quá trình này. Đây là một trong những phương pháp lâu đời nhất và đáng tin cậy hơn cho một kẻ tấn công để truy cập trái phép vào máy tính. [3] [4] [5] Nội dung [ẩn] 1 Khai thác ngăn xếp tràn bộ đệm 2 Platform liên quan đến sự khác biệt 2.1 Stacks mọc lên 3 Bảo vệ chương trình 3.1 stack chim hoàng yến 3.2 Nonexecutable chồng 3.3 ngẫu nhiên 4 ví dụ đáng chú ý 5 Xem thêm 6 Tham khảo Khai thác stack tràn bộ đệm [sửa] Phương pháp kinh điển cho việc khai thác một chồng dựa tràn bộ đệm là để ghi đè lên địa chỉ trả lại chức năng với một con trỏ để kẻ tấn công kiểm soát dữ liệu ( . thường trên stack chính nó) [3] [6] Điều này được minh họa trong ví dụ dưới đây: Ví dụ với strcpy #include
void foo (char * bar)
{
char c [12]; strcpy (c, quán bar); // Không có giới hạn kiểm tra } int main (int argc, char ** argv) { foo (argv [1]); } Đoạn mã này có một đối số từ dòng lệnh và sao chép nó vào một địa phương ngăn xếp biến c. Này làm việc tốt cho các đối số dòng lệnh nhỏ hơn 12 ký tự (như bạn có thể thấy trong hình B bên dưới). Bất kỳ đối số lớn hơn 11 ký tự dài sẽ dẫn đến tham nhũng của stack. (Số lượng tối đa của nhân vật đó là an toàn là một trong ít hơn kích thước của bộ đệm ở đây bởi vì trong lập trình C chuỗi ngôn ngữ được chấm dứt bởi một nhân vật không byte. Một đầu vào mười hai ký tự do đó đòi hỏi mười ba byte để lưu trữ, đầu vào tiếp theo các byte trọng điểm zero. Các byte không kết thúc sau đó ghi đè lên một vị trí bộ nhớ đó là một byte vượt ra ngoài cuối của bộ đệm.) Các chương trình ngăn xếp trong foo () với các đầu vào khác nhau A. -. Trước khi dữ liệu được sao chép B. - "Hello" là đối số dòng lệnh đầu tiên. C. - là đối số dòng lệnh đầu tiên. Lưu ý trong hình C ở trên, khi một cuộc tranh cãi lớn hơn 11 byte được cung cấp trên dòng lệnh foo () ghi đè dữ liệu địa phương stack, con trỏ khung lưu, và quan trọng nhất, địa chỉ trả lại. Khi foo () trả về nó hiện ra địa chỉ trở lại ra khỏi ngăn xếp và nhảy đến địa chỉ đó (tức là bắt đầu thực hiện những lệnh từ địa chỉ đó). Do đó, kẻ tấn công đã ghi đè địa chỉ trở lại với một con trỏ để ngăn xếp đệm char c [12], mà bây giờ chứa dữ liệu kẻ tấn công cung cấp. Trong một tràn bộ đệm thực tế khai thác chuỗi "A" 's thay vào đó sẽ là shellcode phù hợp với nền tảng và chức năng mong muốn. Nếu chương trình này có ưu đãi đặc biệt (ví dụ như bit SUID được thiết lập để chạy như superuser), sau đó những kẻ tấn công có thể sử dụng lỗ hổng này để đạt được các đặc quyền superuser trên máy tính bị ảnh hưởng. [3] Những kẻ tấn công cũng có thể thay đổi giá trị biến nội bộ để khai thác một số lỗi . Với ví dụ này: #include
#include
void foo (char * bar)
{
nổi My_Float = 10,5; // Địa chỉ = 0x0023FF4C
char c [28]; // Địa chỉ = 0x0023FF30 // Sẽ in 10,500000 printf ("giá trị nổi của tôi =% f n", My_Float); / * ~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bản đồ bộ nhớ: @: c cấp phát bộ nhớ #: My_Float cấp phát bộ nhớ * c * My_Float 0x0023FF30 0x0023FF4C | | @@@@@@@@@@@@@@@@@@@@@@@@@@##### foo ("chuỗi của tôi quá dài !!!!! XXXXX") ; memcpy sẽ đặt 0x1010C042 (về cuối nhỏ) trong My_Float giá trị. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~ * / memcpy (c, bar, strlen (bar)); // Không có giới hạn kiểm tra ... // Sẽ in 96.031372 printf ("My giá trị Float =% f n", My_Float); } int main (int argc, char ** argv) { foo ("chuỗi của tôi quá dài !!!!! x10 x10 xc0 x42 "); return 0; } khác biệt Platform liên quan [sửa] Một số nền tảng có sự khác biệt tinh tế trong việc thực hiện các cuộc gọi stack có thể ảnh hưởng đến cách một tràn bộ đệm khai thác sẽ làm việc. Một số kiến trúc máy tính lưu trữ các địa chỉ trở lại mức đỉnh của stack trong một đăng ký. Điều này có nghĩa rằng bất kỳ địa chỉ trả lại ghi đè sẽ không được sử dụng cho đến khi thư giãn sau của cuộc gọi stack. Một ví dụ khác của một chi tiết cụ thể máy có thể ảnh hưởng đến sự lựa chọn của các kỹ thuật khai thác là một thực tế rằng hầu hết các phong cách kiến trúc RISC máy sẽ không cho phép truy cập unaligned vào bộ nhớ. [7] Kết hợp với một chiều dài cố định cho các opcodes máy hạn chế máy tính này có thể thực hiện chuyển để ESP kỹ thuật hầu như không thể thực hiện (với một ngoại lệ là khi chương trình thực sự chứa mã không để nhảy một cách rõ ràng để ngăn xếp đăng ký). [8] [9] Stacks rằng lớn lên [sửa] Trong chủ đề của stack tràn bộ đệm , một kiến trúc thường được thảo luận nhưng hiếm thấy là một trong đó ngăn xếp phát triển theo hướng ngược lại. Thay đổi trong kiến trúc này thường được đề xuất như là một giải pháp cho vấn đề tràn bộ đệm bởi vì bất kỳ tràn của một chồng đệm xảy ra trong stack frame cùng không thể ghi đè lên con trỏ trở lại. Tiếp tục điều tra của bảo vệ tuyên bố này tìm thấy nó là một giải pháp ngây thơ lúc tốt nhất. Bất kỳ tràn xảy ra trong một bộ đệm từ một stack frame trước đó vẫn sẽ ghi đè lên một con trỏ trở lại và cho phép khai thác nguy hiểm của lỗi này. [10] Ví dụ, trong ví dụ trên, con trỏ trở lại cho foo sẽ không được ghi đè bởi vì tràn thực sự xảy ra trong khung stack cho strcpy. Tuy nhiên, do bộ đệm mà tràn trong các cuộc gọi đến cư trú strcpy trong một stack frame trước đó, con trỏ trở lại cho strcpy sẽ có một địa chỉ bộ nhớ số lượng cao hơn so với bộ đệm. Điều này có nghĩa rằng thay vì con trỏ trở lại cho foo được ghi đè, con trỏ trở lại cho strcpy sẽ được ghi đè. Tại hầu hết các này có nghĩa là phát triển các ngăn xếp theo hướng ngược lại sẽ thay đổi một số chi tiết về cách thức ngăn xếp tràn bộ đệm là khai thác được, nhưng nó sẽ không làm giảm đáng kể số lượng các lỗi có thể khai thác. Bảo vệ các chương trình [sửa] Bài chi tiết: Buffer overflow bảo vệ Trong những năm qua một số đề án đã được phát triển để ngăn chặn khai thác tràn bộ đệm độc hại. Chúng thường có thể được phân loại thành ba loại: Phát hiện một tràn bộ đệm đã xảy ra và do đó ngăn chặn chuyển hướng của con trỏ hướng dẫn để mã độc. Ngăn chặn việc thực thi mã độc từ ngăn xếp mà không cần trực tiếp phát hiện tràn bộ đệm. Chọn ngẫu nhiên không gian bộ nhớ như vậy mà việc tìm kiếm mã thực thi trở nên không đáng tin cậy. Ngăn xếp chim hoàng yến [sửa] stack chim hoàng yến, được đặt tên tương tự của họ đến một con chim hoàng yến trong mỏ than, được sử dụng để phát hiện một tràn bộ đệm trước khi thực hiện các mã độc hại có thể xảy ra. Phương pháp này hoạt động bằng cách đặt một số nguyên nhỏ, giá trị được lựa chọn ngẫu nhiên tại chương trình bắt đầu, trong bộ nhớ ngay trước khi con trỏ ngăn xếp trở lại. Hầu hết các lỗi tràn bộ đệm ghi đè lên bộ nhớ từ thấp đến địa chỉ bộ nhớ cao hơn, do đó, để ghi đè lên con trỏ trở lại (và do đó kiểm soát quá trình) giá trị con chim hoàng yến cũng phải được ghi đè. Giá trị này được kiểm tra để chắc chắn rằng nó đã không thay đổi trước khi một thói quen sử dụng con trỏ trở lại trên stack. [2] Kỹ thuật này rất có thể làm tăng sự khó khăn của việc khai thác một tràn bộ đệm bởi vì nó buộc những kẻ tấn công giành quyền kiểm soát con trỏ chỉ dẫn bởi một số phương tiện phi truyền thống như làm hư biến quan trọng khác trên stack [2]. Thông tin thêm: giá trị Canary Nonexecutable chồng [sửa] Bài chi tiết: Data Execution Prevention Một cách khác để ngăn chặn khai thác lỗi tràn bộ đệm ngăn xếp là để thực thi một chính sách bộ nhớ trên các khu vực bộ nhớ ngăn xếp không cho phép thực hiện từ stack (W ^ X, "Viết XOR Thực hiện"). Điều này có nghĩa rằng để thực hiện shellcode từ stack kẻ tấn công phải thể tìm thấy một cách để vô hiệu hóa bảo vệ thực hiện từ bộ nhớ, hoặc tìm một cách để đưa cô ấy / tải trọng shellcode của mình trong một khu vực không được bảo vệ bộ nhớ. Phương pháp này đang trở nên phổ biến hơn bây giờ mà hỗ trợ phần cứng cho không thực hiện lá cờ là có sẵn trong hầu hết các bộ vi xử lý máy tính để bàn. Trong khi phương pháp này chắc chắn sẽ khiến các phương pháp kinh điển để ngăn xếp khai thác lỗi tràn bộ đệm không, nó không phải là không có vấn đề của nó. Đầu tiên, người ta thường tìm cách để lưu trữ shellcode trong vùng bộ nhớ được bảo vệ như đống, và do đó rất ít thay đổi cần thiết trong cách khai thác. [11] Ngay cả nếu điều này là không phải như vậy, có những cách khác. Các tổn thương nhất là cái gọi là trở lại với phương pháp libc để tạo shellcode. Trong cuộc tấn công này payload độc hại sẽ được tải stack không phải với shellcode, nhưng với một cuộc gọi stack thích hợp để thực hiện được vectored đến một chuỗi các cuộc gọi thư viện chuẩn, thường
đang được dịch, vui lòng đợi..