Về cơ bản, tràn bộ nhớ đệm thường xuất phát từ một nguyên nhân duy nhất. Đó là do người dùng gửi quá nhiều dữ liệu tới một chương trình và một phần của dữ liệu này buộc phải lưu trữ ra ngoài bộ nhớ mà lập trình viên đã cấp cho chương trình đó. Quá trình tràn bộ nhớ đệm có thể gây ra nhiều vấn đề, trong đó có một vấn đề mà chúng ta cần quan tâm đó là khi bộ nhớ đệm lưu trữ dữ liệu tới một mức độ nhất định nào đó thì tin tặc có thể chạy các đoạn mã chương trình trên hệ thống. Trong bài viết này chúng ta sẽ tìm hiểu một tình huống tràn bộ nhớ đệm mà tin tặc có thể khai thác để chạy mã trên hệ thống. Sau đó chúng ta sẽ tìm hiểu Data Execution Preventions (DEP), một tính năng được tích hợp trong hệ điều hành Windows có chức năng ngăn tràn bộ nhớ đệm.
Nhận biết hiện tượng tràn bộ nhớ đệm Để có thể hiểu rõ về hiện tượng tràn bộ nhớ đệm chúng ta phải nắm vững ngôn ngữ lập trình cao cấp như C hay C++, cũng như có một vốn kiến thức sâu về quá trình vận hành của các ngăn xếp bộ nhớ.
Khi viết một chương trình, một trong những điều mà lập trình viên cần phải tính toán cẩn trọng đó là kích thước vùng trống của bộ đệm được phân bổ cho những hàm cụ thể. Bộ nhớ đệm là một vùng trống liền kề của bộ nhớ mà một chương trình có thể sử dụng để lưu trữ dữ liệu mà các chức năng khác có thể sử dụng. Chúng ta hãy xem xét đoan mã ví dụ sau:
Hình 1: Hàm A C rất dễ bị tấn công khi tràn bộ nhớ đệm.
Hàm này rất rõ ràng, bắt đầu bằng việc khai bào hai biến bufferA và bufferB có kích thước lần lượt là 50 và 16. Chương trình này hiển thị một câu hỏi tới người dùng yêu cầu nhập tên và sử dụng hàm gets đễn nhận thông tin nhập vào. Sau đó dữ liệu mà người dùng cung cấp được copy từ bufferA sang tham số bộ nhớ đệm và hàm này được hoàn thành.
Với một chương trình đơn giản như vậy có thể hơi quá khi cho rằng nó có thể bị tác động bởi mọi cuộc tấn công. Tuy nhiên vấn đề ở đây nằm trong hàm gets. Vì hàm gets không tự kiểm tra giới hạn do đó khó có thể khẳng định rằng thông tin nhập vào bufferA không vượt quá 50 kí tự. Nếu người dùng nhập hơn 50 kí tự thì chương trình sẽ bị sập.
Hàm strcopy sẽ copy dữ liệu trong bufferA sang bufferB. Tuy nhiên bufferB có kích thước nhỏ hơn bufferA, điều này có nghĩa là dù người dùng nhập vào ít hơn 50 kí tự nhưng vẫn có thể nhiều hơn 16 kí tự vào bufferA, và khi copy sang bufferB sẽ gây ra hiện tượng tràn bộ nhớ đệm và chương trình cũng sẽ bị sập. Chương trình nhỏ này không chỉ có một mà là hai lỗ hổng tràn bộ nhớ đệm.
Khai thác tràn bộ nhớ Tiếp theo chúng ta sẽ tìm hiểu những điều kiện gây ra tràn bộ nhớ và vấn đề phát sinh từ tràn bộ nhớ? Trong những trường hợp bộ nhớ đệm bị tràn thì dữ liệu tràn ra ngoài bộ nhớ đệm được chỉ định sẽ phải lưu trữ vào một nơi khác. Dữ liệu này sẽ chảy vào các vùng nhớ lân cận, và thông thường khi đó chương trình sẽ bị lỗi vì nó không thể xử lý các dữ liệu bổ sung. Mặt khác, khi lỗi này bị một ai đó hiểu biết về ngôn ngữ Assembly (một bộ ngôn ngữ lập trình cấp thấp được sử dụng trong lập trình máy tính, vi xử lý, vi điều khiển và mạch tích hợp) và ngăn xếp bộ nhớ khai thác thì mọi chuyện còn tồi tệ hơn. Trong tình huống này, tin tặc có thể gây tràn bộ nhớ đệm theo phương pháp mà chúng có thể tạo những lệnh hệ thống riêng, rồi chuyển đổi những lệnh này thành mã byte cấp thấp sau đó gửi chúng tới chương trình này theo định dạng phù hợp thì những lệnh này sẽ được chạy.
Hình 2: Một mẫu Shellcode của Assembly và C được viết để trả về
một dấu nhắc C:\ của Windows.
Lúc này đoạn mã được chạy trong ngữ cảnh ứng dụng dễ bị tấn công ban đầu của người dùng. Điều đó có nghĩa là nếu chương trình này được quản trị hệ thống chạy thì mã kết hợp cũng chạy trong ngữ cảnh của một quản trị viên hệ thống. Tùy thuộc vào kích thước của bộ nhớ đệm mà tin tặc có thể kết hợp nhiều loại mã khác nhau. Những loại mã thường được sử dụng là những loại được gọi là Shellcode. Mã này sẽ trả về một Shell (ví dụ một dấu nhắc C:\ của Windows) tới người đã chạy đoạn mã đó. Trong một ngữ cảnh phù hợp thì người chạy mã đó sẽ có toàn quyền kiểm soát với máy trạm này. Tràn bộ nhớ đệm có thể xảy ra dưới nhiều hình thức và quy mô, một người thành thạo trong việc điều khiển ngăn xếp sẽ chiếm được toàn quyền kiểm soát mọi hệ thống dễ bị tấn công.
Data Execution Prevention Phương pháp đơn giản nhất để chặn khả năng khai thác lỗ hổng phát sinh do tràn bộ nhớ đệm mà các lập trình viên thường sử dụng là luôn đảm bảo mã lập trình được bảo mật. Thực ra đây không phải là một tiến trình được tự động hóa vì nó yêu cầu tiêu tốn nhiều thời gian và công sức cho việc kiểm tra lại mã để đảm bảo rằng tính toàn vẹn của mã chương trình được duy trì, do đó số lượng dòng lệnh tỉ lệ thuận với thời gian và công sức cần phải bỏ ra. Xuất phát từ yêu cầu đó Microsoft đã phát triển một tính năng có tên Data Execution Prevention (DEP).
DEP, một tính năng bảo mật được giới thiệu trong Windows XP SP2, được thiết kế để chặn ứng dụng chạy mã trong vùng không thể chạy của bộ nhớ. DEP xuất hiện trong cả cấu hình Hardware-based DEP (nền tảng phần cứng) và Software-based DEP (nền tảng phần mềm).
Hardware-based DEP DEP được cho là bảo mật nhất khi sử dụng Hardware-based DEP. Trong trường hợp này vi xử lý sẽ đánh dấu mọi vị trí nhớ là “không thể thực thi” nếu vị trí này không chứa mã thực thi. Mục đích của việc này là DEP sẽ chặn mọi mã chạy trong những vùng không thể thực thi.
Vấn đề chính của việc sử dụng Hardware-based DEP là nó chỉ được hỗ trợ bởi một số ít tiến trình. Vi xử lý có thể thực hiện được điều này là nhờ có tính năng NX của bộ vi xử lý AMD và XD của Intel.
Software-based DEP Khi Hardware-based DEP không tồn tại thì Software-based DEP phải được sử dụng. Loại DEP này được tích hợp trong hệ điều hành Windows. Software-based DEP vận hành bằng cách dò tìm thời điểm mà những ngoại lệ được các chương trình đưa vào và đảm bảo rằng những ngoại lệ này là một phần hợp lệ của chương trình này trước khi cho phép chúng xử lý.