24/05/2018, 22:02

Tràn bộ đệm

Xét chương trình C trên. Ở đây có ba biến, thông thường lưu trong vùng nhớ liền kề. Gọi chương trình con copy vào str1 dữ liệu Start. Sau đó đọc đầu vào sử dụng hàm gets lưu vào str2. Sau đó so sánh đầu ...

Xét chương trình C trên. Ở đây có ba biến, thông thường lưu trong vùng nhớ liền kề. Gọi

chương trình con copy vào str1 dữ liệu Start. Sau đó đọc đầu vào sử dụng hàm gets lưu vào str2. Sau đó so sánh đầu vào với xâu Start. Nếu thành công valid = true. Vấn đề hàm thư viện gets của C không kiểm tra độ lớn dữ liệu đọc vào. Nếu nhiều hơn 7 ký tự nó đòi hỏi bộ nhớ nhiều hơn. Khi đó dữ liệu thừa viết đè dữ liệu của biến kề, trong trường hợp này là str1. Giả sử xâu đầu vào là “EVILINPUTVALUE”, kết quả xâu Str1 bị viết đè với các ký tự “TVALUE”. Xâu str2 không chỉ sử dụng 8 ký tự của nó mà còn thêm 7 ký tự từ str1. Biết cấu trúc trên, kẻ tấn công có thể thu xếp sao cho giá trị xâu Str1 vẫn bằng Str2. Chẳng hạn nếu nhập xâu đầu vào là “BADINPUTBADINPUT” thì trong phép so sánh kết quả vẫn đúng như trong lần chạy thứ ba trong ví dụ trên.

Để làm tràn bộ đệm, kẻ tấn công cần phải phát hiện lỗ hổng tràn bộ đệm trong chương trình nào đó. Theo dõi, lần theo vết thực hiện, sử dụng các công cụ ẩn. Hiểu bộ đệm lưu trong bộ nhớ như thế nào và xác định khả năng phá hỏng.

Một chút về lịch sử ngôn ngữ lập trình. Ở mức ngôn ngữ máy mọi dữ liệu là mảng các bytes, thông dịch phụ thuộc vào các chỉ lệnh được dùng. Ngôn ngữ bậc cao hiện đại có định nghĩa chặt về kiểu và các phép toán đúng. Không có lỗ hổng tràn bộ đệm, được lường trước, giới hạn khi dùng.

C và ngôn ngữ liên quan có cấu trúc điều khiển bậc cao, nhưng cho phép truy cập trực tiếp đến bộ nhớ. Vì vậy có lỗ hổng tràn bộ đệm. Có kế thừa lớn các code không an toàn, đang được sử dụng rộng rãi, nên có lỗ hổng.

Để hiểu hơn tạo sao lại tràn bộ nhớ, mà không khắc phục được, ta xem xét cơ chế mà lời gọi hàm quản lý trạng thái cục bộ cho mỗi lời gọi. Khi một hàm gọi hàm khác, nó cần phải lưu ở đâu đó địa chỉ trả về để hàm được gọi khi kết thúc trả điều khiển cho hàm gọi. Bên cạnh đó cũng cần có chỗ để cất một số tham số mà cần truyền cho hàm được gọi và cũng cần lưu các giá trị thanh ghi của hàm gọi mà cần được sử dụng khi hàm được gọi kết thúc. Thông thường mọi dữ liệu này được cất ở khung ngăn xếp (stack frame). Mỗi lần gọi hàm lại sinh ra một khung ngăn xếp liên kết.

ngăn xếp xảy ra khi bộ đệm đặt trên ngăn xếp. Nó được khai thác bởi sâu Morris. Bài báo “Smashing the Stack” tuyên truyền nó. Có biến cục bộ phía dưới con trỏ khung lưu trữ và địa chỉ trả lại. Vì vậy tràn bộ đệm cục bộ có nhiều khả năng viết đè các mục điều khiển chính. Kẻ tấn công viết đè địa chỉ trả về với địa chỉ của đọan code cài vào. Đó có thể là địa chỉ của chương trình, thư viện hệ thống hoặc tải vào bộ đệm.

Chúng ta xem cấu tạo vùng nhớ, nơi cất chương trình đang chạy, dữ liệu tổng thể, đống và ngăn xếp. Khi chương trình chạy, hệ điều hành tạo ra một tiến trình cho nó. Tiến trình được cho bởi không gian ảo của riêng nó với cấu trúc như trên hình vẽ sau. Nó bao gồm nội dung chương trình đang chạy, trong đó có dữ liệu tổng thể, bảng cấp bộ nhớ, code của chương trình ở gần đáy của không gian nhớ này. Trên đó không gian cho đống tăng dần lên và trên nữa là không gian cho ngăn xếp giảm dần xuống.

Để mô tả tràn bộ đệm ngăn xếp ta xét chương trình C sau. Nó chứa biến cục bộ duy nhất, bộ đệm inp. Hàm Hello nhắc nhập tên mà được đọc vào bộ đệm nhờ hàm thư viện không an toàn gets(). Sau đó hiện kết quả đọc được nhờ hàm thư viện printf (). Nếu giá trị nhỏ được đọc thì ở đây không có vấn đề gì. Chương trình gọi hàm sẽ chạy thành công như trong lần chạy thứ nhất trên hình sau đó. Nếu dữ liệu lớn như trong lần chạy thứ hai, dữ liệu sẽ vượt qua cuối bộ đệm và ghi đè lên con trỏ khung lưu trữ và trả về địa chỉ sai tương ứng với biểu diễn nhị phân của các ký tự. Và khi hàm trả điều khiển cho địa chỉ trả về, nó nhảy đến vị trí bộ nhớ không hợp lệ, báo lỗi “Segmentation Fault” và dừng chương trình không bình thường như thông báo trên hình sau đó. Kẻ tấn công tận dụng cơ hội này để truyền điều khiển về chương trình đã định trước.

Đây là code chương trình được chuẩn bị bởi kẻ tấn công. Nó được lưu trong bộ nhớ bị tràn và kẻ tấn công tìm cách chuyển điều khiển sang cho shell.

Phát triển code che đậy

Trong ví dụ trên mô tả với Intel Linux shellcode cơ bản để chạy bản dịch Bourne shell. Shellcode cần phải phù hợp với đối số cho execve(). Nó bao gồm mọi code để gọi hàm của hệ thống, phải độc lập với vị trí và không chứa NULLs – xâu kết thúc của C.

Thông thường chương trình che đậy dưới vỏ bọc là tiện ích hệ thống đáng tin cậy, dịch vụ mạng đặc biệt, code thư viện được sử dụng chung, như hình ảnh.

Các hàm của shellcode: giao diện tự sinh, tạo đối tượng nghe để khởi tạo giao diện kết nối, tạo kết nối ngược lại tới kẻ tấn công, vượt qua các qui tắc tường lửa, thoát khỏi môi trường thực thi hạn chế.

được khai thác rộng rãi, có nhiều code có lỗ hổng đang dùng. Mặc dù nguyên nhân và các biện pháp chống đã biết. Có hai cách chống rộng rãi: chương trình mới được gia cố trong thời gian dịch và kiểm soát tấn công chương trình đang có trong thời gian chạy.

Nếu sử dụng ngôn ngữ bậc cao với kiểu mạnh , thì sẽ không có lỗ hổng tràn bộ đệm. Chương trình dịch buộc kiểm tra cỡ và các thao tác cho phép trên các biến. Khi đó phải trả giá khi sử dụng nguồn và hạn chế truy cập đến phần cứng. Tuy nhiên vẫn cần một số code của các ngôn ngữ giống C.

Bảo vệ trong thời gian dịch

Thiết lập các kỹ thuật lập trình an toàn. Nếu sử dụng ngôn ngữ tiềm ẩn không an toàn như C, lập trình viên cần viết code an toàn một cách tường minh. Bằng thiết kế với code mới, sau khi xem xét code cũ. Xem an toàn tràn bộ đệm như tập con các kỹ thuật lập trình an toàn nói chung. Chú ý đến các lỗi nhỏ, kiểm tra đủ không gian trong bộ đệm bất kỳ.

Có đề nghị mở rộng an toàn cho C như tạo điểm phạt thực thi, cần dịch chương trình với chương trình dịch đặc biệt. Có một số phương án thư viện chuẩn an toàn, các hàm mới, như strlcpy(). Cài đặt lại an toàn hơn một số hàm chuẩn như thư viện động, chẳng hạn Libsafe.

Bổ sung code của chức năng nhập và thoát để kiểm tra ngăn xếp ghi nhận việc ghi đè, sử dụng yếu tố ngẫu nhiên như bảo vệ ngăn xếp, kiểm tra viết đè giữa biến cục bộ và con trỏ khung lưu trữ và địa chỉ trả về. Chương trình dừng nếu phát hiện thay đổi. Phát hành: bản dịch lại, hỗ trợ phát hiện lỗi hoặc copy an toàn lưu trữ/kiểm tra địa chỉ trả về.

Bảo vệ trong thời gian chạy

Sử dụng hỗ trợ bộ nhớ ảo để tạo một số vùng bộ nhớ không thực thi được như stack, heap, global data. Cần hỗ trợ từ các phần cứng bộ nhớ như trong SPARC / Solaris systems, x86 Linux/Unix/Windows systems. Phát hành hỗ trợ cho code ngăn xếp thực thi, cần một số dự phòng đặc biệt.

Thao tác trên vị trí của các cấu trúc dữ liệu chính, sử dụng tịnh tiến ngẫu nhiên cho mỗi tiến trình, có vùng địa chỉ lớn trên các phương tiện của các hệ thống hiện đại chống các va chạm và đoán địa chỉ bộ đệm đích là không thể. Vị trí ngẫu nhiên cho bộ đệm heap và vị trí các hàm thư viện chuẩn. Đặt các trang bảo vệ giữa các vùng quan trọng của bộ nhớ, đặt cờ trong bộ nhớ như địa chỉ không hợp lệ. Có thể ngay cả đặt giữa các khung ngăn xếp và các bộ đệm heap trong thời gian thực thi và phải trả giá về không gian.

Có nhiều các phương án tấn công

0