Marcus1337
VIP Members
-
01/04/2021
-
62
-
76 bài viết
Lỗ hổng tràn bộ đệm là gì?
Khai thác lỗi tràn bộ đệm được biết đến đầu tiên là vào năm 1988 và từ khi nó xuất hiện đến nay đã có nhiều CVE được tìm dựa trên lỗi này.
Memory
Khi bạn chạy một file thực thi thì máy tính sẽ tiến hành load tài nguyên các đoạn code cần thực thi lên bộ nhớ. CPU sẽ đọc và lấy tài nguyên từ memory để thực thi chứ không lấy tài nguyên trực tiếp từ ổ cứng nơi lưu trữ file. Các chương trình sẽ được cấp 1 không gian bộ nhớ nhất định theo yêu cầu của chương trình. Do đó để thay đổi cách hoạt động của chương trình ngoài cách thay đổi file thực thi gốc thì có thể thay đổi nội dung trên memory.
Ngăn xếp (Stack)
Stack là cấu trúc dữ liệu dùng rất phổ biến trong hệ điều hành.Tổ chức của nó theo kiểu LIFO – Last In First Out – vào sau ra trước. Stack được dùng để chứa các biến cục bộ trong hàm, các tham số truyền cho hàm, các địa chỉ trở về của hàm, hỗ trợ tổ chức trong ngắt, Exception... Có thể hàm của bạn không dùng biến cục bộ lưu trong Stack và chỉ dùng Heap, nhưng để truy cập được đến Heap vẫn cần con trỏ và chính con trỏ đó được lưu vào stack. Một cấu trúc stack được quản lý bởi hai con trỏ đỉnh stack và đáy stack. Một phần từ mới được đưa vào sẽ được đẩy vào đình, và phần tử nào được đưa vào đầu tiên sẽ được lấy ra sau cùng. Windows quản lý stack bởi hai con trỏ EBP và ESP, trong đó ESP là thanh ghi 32 bit trỏ đến đỉnh stack, EBP cũng là thanh ghi 32 Bit trỏ đến đáy stack. Nếu một chương trình hoạt động bình thường, đỉnh stack luôn có địa chỉ vật lý thấp hơn đáy stack.
Có 2 thao tác với stack là PUSH và POP:
Khi ghi dữ liệu lên stack, đầu tiên CPU sẽ giảm ESP đi 4 rồi cất nội dung toán hạng vào ô nhớ trỏ bởi ESP. Khi lấy dữ liệu ra khỏi stack, CPU sẽ cất nội dung trỏ bởi ESP ra toán hạng và tự tăng ESP lên 4. Bạn sẽ thấy nhiều lệnh PUSH khi chuẩn bị thực hiện một hàm và nhiều lệnh POP khi chuẩn bị kết thúc 1 hàm. Đó là để cất nội dung các thanh ghi khi bắt đầu thực hiện một chương trình con và khôi phục lại thanh ghi trước khi trả điều khiển về hàm gọi nó.
CPU Registers
Thanh ghi là một bộ nhớ dung lượng nhỏ và rất nhanh, được sử dụng để tăng tốc độ xử lý của các chương trình máy tính bằng cách cung cấp các truy cập trực tiếp đến các giá trị cần dùng. Hầu hết, nhưng không phải tất cả, các máy tính hiện đại hoạt động theo nguyên lý chuyển dữ liệu từ bộ nhớ chính vào các thanh ghi, tính toán trên chúng, sau đó chuyển kết quả vào bộ nhớ chính.
Buffer Overflow là gì?
Buffer – tạm dịch là bộ đệm – được định nghĩa là một vùng nhớ liên tục có kích thước giới hạn. Bộ đệm thường dùng nhất trong C là một mảng. Bộ đệm thường được dùng để lưu trữ các giá trị tạm thời của một xâu phục vụ cho quá trình xử lý.
Tràn bộ đệm hiểu theo nghĩa đơn giản nhất là sao chép dữ liệu có kích thước lớn hơn vào một bộ đệm mà không thực hiện các thao tác kiểm tra biên dẫn đến vùng nhớ phía sau bộ đệm bị ghi đè, vùng nhớ này có thể lại là một bộ đệm khác hoặc là vùng nhớ hệ thống... Hậu quả có thể làm chương trình hoạt động không chính xác hoặc đổ vỡ.
Các ngôn ngữ lập trình họ C và C++ không có khả năng kiểm tra kích thước dữ liệu sẽ sao chép vào so với kích thước của bộ đệm. Do đó nếu người thiết kế chương trình không thêm đoạn mã kiểm tra kích thước dữ liệu vào, hoàn toàn có thể ghi đè lên vùng nhớ phía sau bộ đệm và những điều không lường trước có thể xảy ra.
Bộ đệm có hai loại, một là bộ đệm trên Stack dùng để chứa các biến cục bộ kích thước thường nhỏ, sử dụng trong hàm, và được cấp phát khi hàm khởi tạo, giả phóng khi hàm trở về.
Bộ đệm trên heap có cấu trúc phức tạp hơn và được tham chiếu đến bởi con trỏ - thường là một biến trong stack, tuy nhiên cả hai đều có thể tràn nếu không kiểm tra cẩn thận trước khi thao tác.
Xét một ví dụ C đơn giản sau:
Biên dịch chương trình không hề có lỗi, điều đó chứng tỏ C không hề thực hiện kiểm tra biên của mảng.
Xét thêm một ví dụ nữa
Trình biên dịch cũng không báo lỗi nhưng lần này khi thực hiện , chương trình sẽ đổ vỡ.
Ta xét thêm 1 ví dụ khác
Chương trình trên yêu khai báo biến buffer kiểu char với kích thước 64 bytes sau đó lấy tham số đầu vào copy vào biến buffer qua hàm strcpy.
Biên dịch và chạy chương trình với 12 ký tự chữ A liền nhau thì ta thấy chương trình vẫn hoạt động và kết thúc bình thường. Tuy nhiên khi ta nhập đầu vào là 80 ký tự chữ A liền nhau thì chương trình xuất hiện dấu hiệu bị crash. Và đây là những gì đã xảy ra trong bộ nhớ
Khi đầu vào là 80 chữ AAAA vượt quá kích thước buffer được cấp thì các buffer phía sau bị ghi đè trong đó địa chỉ hàm trả về cũng bị ghi đè bằng 4 bytes AAAA. Tất nhiên 4 bytes AAAA không phải là 1 địa chỉ hợp lệ trong máy tính do đó chương trình không thể tìm thấy hàm tiếp theo cần thực thi là gì do đó dẫn tới hiện tượng chương trình bị crash.
Như ví dụ ở trên ta sẽ có câu hỏi là điều gì sẽ xảy ra khi địa chỉ trả về thay vì 4 chữ AAAA mà được ghi đè với 1 địa chỉ của 1 hàm tồn tại? Tất nhiên lúc đó chương trình sẽ chạy tới hàm được định nghĩa ở giá trị trả về kia và luồng hoạt động của chương trình đã bị thay đổi.
Chúng ta sẽ lợi dụng việc này để khởi chạy các đoạn code, các đoạn mã thực thi mà chúng ta mong muốn ở bài thực hành sau nhé.
Update bài tiếp theo ở đây nhé anh em https://whitehat.vn/threads/thuc-hanh-khai-thac-bof-tren-windows.15132/
Memory
Ngăn xếp (Stack)
Stack là cấu trúc dữ liệu dùng rất phổ biến trong hệ điều hành.Tổ chức của nó theo kiểu LIFO – Last In First Out – vào sau ra trước. Stack được dùng để chứa các biến cục bộ trong hàm, các tham số truyền cho hàm, các địa chỉ trở về của hàm, hỗ trợ tổ chức trong ngắt, Exception... Có thể hàm của bạn không dùng biến cục bộ lưu trong Stack và chỉ dùng Heap, nhưng để truy cập được đến Heap vẫn cần con trỏ và chính con trỏ đó được lưu vào stack. Một cấu trúc stack được quản lý bởi hai con trỏ đỉnh stack và đáy stack. Một phần từ mới được đưa vào sẽ được đẩy vào đình, và phần tử nào được đưa vào đầu tiên sẽ được lấy ra sau cùng. Windows quản lý stack bởi hai con trỏ EBP và ESP, trong đó ESP là thanh ghi 32 bit trỏ đến đỉnh stack, EBP cũng là thanh ghi 32 Bit trỏ đến đáy stack. Nếu một chương trình hoạt động bình thường, đỉnh stack luôn có địa chỉ vật lý thấp hơn đáy stack.
Có 2 thao tác với stack là PUSH và POP:
Khi ghi dữ liệu lên stack, đầu tiên CPU sẽ giảm ESP đi 4 rồi cất nội dung toán hạng vào ô nhớ trỏ bởi ESP. Khi lấy dữ liệu ra khỏi stack, CPU sẽ cất nội dung trỏ bởi ESP ra toán hạng và tự tăng ESP lên 4. Bạn sẽ thấy nhiều lệnh PUSH khi chuẩn bị thực hiện một hàm và nhiều lệnh POP khi chuẩn bị kết thúc 1 hàm. Đó là để cất nội dung các thanh ghi khi bắt đầu thực hiện một chương trình con và khôi phục lại thanh ghi trước khi trả điều khiển về hàm gọi nó.
CPU Registers
Thanh ghi là một bộ nhớ dung lượng nhỏ và rất nhanh, được sử dụng để tăng tốc độ xử lý của các chương trình máy tính bằng cách cung cấp các truy cập trực tiếp đến các giá trị cần dùng. Hầu hết, nhưng không phải tất cả, các máy tính hiện đại hoạt động theo nguyên lý chuyển dữ liệu từ bộ nhớ chính vào các thanh ghi, tính toán trên chúng, sau đó chuyển kết quả vào bộ nhớ chính.
Buffer Overflow là gì?
Buffer – tạm dịch là bộ đệm – được định nghĩa là một vùng nhớ liên tục có kích thước giới hạn. Bộ đệm thường dùng nhất trong C là một mảng. Bộ đệm thường được dùng để lưu trữ các giá trị tạm thời của một xâu phục vụ cho quá trình xử lý.
Tràn bộ đệm hiểu theo nghĩa đơn giản nhất là sao chép dữ liệu có kích thước lớn hơn vào một bộ đệm mà không thực hiện các thao tác kiểm tra biên dẫn đến vùng nhớ phía sau bộ đệm bị ghi đè, vùng nhớ này có thể lại là một bộ đệm khác hoặc là vùng nhớ hệ thống... Hậu quả có thể làm chương trình hoạt động không chính xác hoặc đổ vỡ.
Các ngôn ngữ lập trình họ C và C++ không có khả năng kiểm tra kích thước dữ liệu sẽ sao chép vào so với kích thước của bộ đệm. Do đó nếu người thiết kế chương trình không thêm đoạn mã kiểm tra kích thước dữ liệu vào, hoàn toàn có thể ghi đè lên vùng nhớ phía sau bộ đệm và những điều không lường trước có thể xảy ra.
Bộ đệm có hai loại, một là bộ đệm trên Stack dùng để chứa các biến cục bộ kích thước thường nhỏ, sử dụng trong hàm, và được cấp phát khi hàm khởi tạo, giả phóng khi hàm trở về.
Bộ đệm trên heap có cấu trúc phức tạp hơn và được tham chiếu đến bởi con trỏ - thường là một biến trong stack, tuy nhiên cả hai đều có thể tràn nếu không kiểm tra cẩn thận trước khi thao tác.
Xét một ví dụ C đơn giản sau:
Mã:
int main() {
int array[5] = {1,2,3,4,5};
printf(“%d\n”,array[5]);
}
Xét thêm một ví dụ nữa
Mã:
int main() {
int array[5];
int i;
for (i = 0; i <=255; i ++) array[i] = 10;
};
Ta xét thêm 1 ví dụ khác
Mã:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char buffer[64];
if (argc < 2)
{
return 1;
}
strcpy(buffer, argv[1]);
}
Chương trình trên yêu khai báo biến buffer kiểu char với kích thước 64 bytes sau đó lấy tham số đầu vào copy vào biến buffer qua hàm strcpy.
Biên dịch và chạy chương trình với 12 ký tự chữ A liền nhau thì ta thấy chương trình vẫn hoạt động và kết thúc bình thường. Tuy nhiên khi ta nhập đầu vào là 80 ký tự chữ A liền nhau thì chương trình xuất hiện dấu hiệu bị crash. Và đây là những gì đã xảy ra trong bộ nhớ
Như ví dụ ở trên ta sẽ có câu hỏi là điều gì sẽ xảy ra khi địa chỉ trả về thay vì 4 chữ AAAA mà được ghi đè với 1 địa chỉ của 1 hàm tồn tại? Tất nhiên lúc đó chương trình sẽ chạy tới hàm được định nghĩa ở giá trị trả về kia và luồng hoạt động của chương trình đã bị thay đổi.
Chúng ta sẽ lợi dụng việc này để khởi chạy các đoạn code, các đoạn mã thực thi mà chúng ta mong muốn ở bài thực hành sau nhé.
Update bài tiếp theo ở đây nhé anh em https://whitehat.vn/threads/thuc-hanh-khai-thac-bof-tren-windows.15132/
Happy hacking
Chỉnh sửa lần cuối: