Table of Contents
Buffer tràn ngập vẫn còn là một trong những điểm yếu an ninh bền bỉ và nguy hiểm nhất trong chương trình C. Mặc dù được thông báo tốt trong nhiều thập kỷ, chúng vẫn tiếp tục gây ra những vấn đề nghiêm trọng như tham nhũng dữ liệu, hệ thống sụp đổ và thực hiện mã từ xa. Viết mã C bảo mật đòi hỏi sự hiểu biết sâu sắc về cách thức đệm tràn ngập và cách thức sửa chữa để ngăn chặn chúng. Bài này cung cấp một hướng dẫn toàn diện để viết, mã C tràn ngập, bao gồm các khái niệm cơ bản, chức năng an toàn, kỹ thuật biên dịch hợp lệ, và biện pháp bảo vệ hệ thống bảo vệ tính năng hiện đại.
Hiểu được sự tràn ngập
Một bộ đệm chồng chéo xảy ra khi một chương trình viết nhiều dữ liệu hơn cho một khối bộ nhớ liên tục (một bộ đệm) hơn bộ đệm được phân phát để giữ lại. Vì bộ đệm nằm trong ngăn xếp hay đống bộ nhớ, vượt quá ranh giới của chúng ghi đè lên những địa điểm bộ nhớ bên cạnh. Sự tham nhũng này có thể thay đổi tình trạng, giới thiệu hành vi không thể đoán trước, hoặc bị một kẻ tấn công lạm dụng để tiêm và thực hiện mã tự do.
Hậu quả phụ thuộc vào những gì được viết quá mức. Viết một địa chỉ trở lại trên chồng có thể chuyển hướng hành quyết sang mã tấn công. Hơn bút chì có thể dẫn đến bộ nhớ tùy ý viết. Ngay cả những vụ tai nạn đơn giản cũng có thể được lợi thế cho việc từ chối tấn công dịch vụ. hiểu rõ cơ chế là bước đầu tiên để phòng ngừa.
Comment
Biến địa phương, kể cả bộ đệm được công bố bên trong, được cất giữ trong đống. Các vật liệu cũng giữ địa chỉ trở lại, lưu con trỏ khung và các dữ liệu điều khiển khác. Khi một bộ đệm tuyến tính như [FLT: 0] bị tràn vào, dữ liệu tràn vào địa chỉ trở lại và hơn nữa. Việc khai thác cổ điển như giun Morris (198) được dùng để tạo ra các chồng để truy cập trái phép.
Dòng chảy Hê-páp
Các bộ đệm được cấp phát một cách năng động (via ), [FLT: 2], v. v.) cư trú trên đống.) ở đây. Dòng chảy quá nhiều có thể làm hỏng siêu dữ liệu được dùng bởi người điều khiển, dẫn đến việc sụp đổ hoặc khai thác thông qua việc phun thuốc xịt hoặc tấn công không dùng đến thuốc lá sau đó.
Hàm có thể thay thế thông thường và những hàm khác an toàn
Thư viện C cung cấp một số chức năng không kiểm tra giới hạn. Dùng chúng là nguyên nhân thông thường nhất của việc đệm tràn vào.
Name
- [FLT:] [BLT:] — Không an toàn: bản sao cho đến khi vô hạn vô hạn, không hạn chế.
[FLT:] Thay thế — Bản sao ở hầu hết n] — lưu ý rằng nó không vô hiệu hóa nếu nguồn là n, vì vậy luôn luôn vô hiệu lực bằng tay. - Tốt hơn nữa: — có trên hệ thống BSD và nhiều hệ thống Linux; luôn luôn vô hiệu hóa và trả lại chiều dài của chuỗi nguồn cho phát hiện sự cố tai nạn.
- [FLT:] ) — Không an toàn: phân loại không giới hạn.
Thay thế [FLT: 7) [FLT: 7) — phụ thêm vào phần lớn n ký tự và luôn luôn vô hiệu hóa.
Định dạng đầu ra và nhập
- [FLT: 0] [FLT:] [FLT:] [FLT:] [FLT:] [FLT:] [FLT:]] [FLT:]] [FLT:]] — Giới hạn xuất ra kích cỡ 1- ký tự và vô hiệu hoá.
- ) — tương tự rủi ro; dùng .
- ) — cực kỳ nguy hiểm; bị loại khỏi C11 tiêu chuẩn.
- [FLT:] [FLT:] — Không có giới hạn. Hãy dùng hoặc với sự tối đa hóa đồng ruộng.
Chép bộ nhớ và di chuyển
- ) ) ) ) ) [FLT:] [FLT:] [b] [b] và luôn luôn bảo đảm kích cỡ n .
- Một số nền tảng cung cấp từ Annex K (tùy chọn vào năm C11), nhưng việc nhận con nuôi bị giới hạn.
Quản lý độ chính xác và cỡ
Ngay cả khi có chức năng an toàn, bạn cũng phải xác nhận độ dài đầu vào, kích cỡ bộ đệm thích hợp và xử lý việc đi xe hơi một cách khéo léo.
Kiểm tra độ dài nhập
Trước khi sao chép hoặc xử lý dữ liệu nhập bên ngoài ( dữ liệu nhập, mạng, nội dung tập tin), hãy xác định độ dài tối đa được chấp nhận và từ chối hoặc cắt giảm dữ liệu. Ví dụ:
#define MAX_INPUT 255
char buffer[MAX_INPUT + 1]; // +1 for null
if (strlen(user_input) > MAX_INPUT) {
// Handle error: reject or truncate
fputs("Input too long", stderr);
return -1;
}
strncpy(buffer, user_input, sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0';
Dùng bộ đệm cố định có hạn
Bất cứ khi nào có thể, xác định bộ đệm với kích cỡ không đổi và áp dụng nó trong mã. Tránh dãy dãy biến (VLAs) có thể gây ra chồng chồng chồng nếu kích cỡ lớn được cung cấp. Thay vào đó, hãy phân bổ năng động với kiểm tra kích cỡ rõ ràng.
Giải quyết triệt để
Hàm như và có thể cắt giảm dữ liệu. Hãy ý thức giá trị trả về giá trị để phát hiện sự bị cắt giảm và quyết định liệu dữ liệu bị cắt xén có chấp nhận hay không hoặc nếu có lỗi nên được tăng lên. Bỏ qua việc sử dụng bộ đệm có thể để lại trạng thái bất ngờ.
Name
Những biên dịch hiện đại cung cấp cờ cho phép thêm khả năng phát hiện và giảm cường độ đệm tràn lên mà không cần thay đổi mã. Cho phép chúng trong hệ thống xây dựng của bạn.
- [FLT:] / — Chèn chồng các thông tin mật (giá trị khách lạ) trước khi trở về. Nếu bộ đệm tràn lên bảng ghi đè thẻ trước khi sửa đổi địa chỉ trở lại, chương trình sẽ hủy bỏ trước khi khai thác hoàn tất.
- [FLT:] ) ) [FLT: 1] [FLT:] [FLT:] [FLT:]] [LT:27] [LT] [LT:25] [LT]] [LTMMMMMMMMMMMMS] [F: 1] — Thay thế các chức năng không an toàn như và [FLT: 26] [FLT: 27] [BLT: 27] [BLT: 27]] [LT] [KC: 27] [KLC: 27] [KC: khả năng hủy bỏ nếu bộ đệm đệm đệm đích là quá nhỏ].
- ) — Cảnh báo về các yếu tố định dạng chuỗi ix vulneras có thể dẫn đến việc đệm tràn hay rò rỉ thông tin.
- ) ) — Địa chỉSanitzer (AAAAAAN) để phát hiện các luồng đệm tràn, dùng- sau đó không có lỗi bộ nhớ khác trong thời gian chạy. Thực hiện chậm nhưng là vô giá cho việc thử nghiệm.
- ) — Tránh việc kiểm tra quá mức (dùng thận trọng) để tránh bị kiểm tra.
Hệ thống bảo vệ hoạt động
Các công nghệ phân tích hạt nhân trong hệ điều hành hiện đại bao gồm:
- Phòng chống tử hình (DEP) / NX bit ) — Dấu chồng chồng và chất như không thể cắt, ngăn chặn việc thực hiện mã hóa mã trình bao.
- Bố trí Không gian ngẫu nhiên [ALR] — ngẫu nhiên biến địa chỉ bộ nhớ (sack, chất đống, thư viện chia sẻ) khó đoán mục tiêu hơn.
- Định vị Đọc (RELRO) ) — Bảo vệ có (bản đồ mở rộng) từ việc viết quá nhiều.
Bật các bảo vệ này (thường mặc định) nâng cao thanh để bóc lột nhưng không thay thế mã bảo mật.
Phân tích ngữ âm và phát âm mã
Kết hợp với phân tích tĩnh tĩnh điện tự động có thể bắt gặp vấn đề tràn qua các vấn đề về đệm, kết hợp chúng với dòng công việc phát triển của bạn.
- Xem lại mã hướng dẫn — Tìm cách sử dụng các chức năng không an toàn, kiểm tra kích thước thiếu và vòng lặp ghi ra ngoài biên giới bộ đệm.
- công cụ phân tích thông thường ) — Công cụ như , , , và phát hiện các khả năng tiềm năng, sử dụng các chức năng nguy hiểm, và lỗi off-one.
- Khả năng — Dùng libFuzzer, AFL hoặc các Fzzers khác để tự động kiểm tra dữ liệu có thể gây ra tràn ngập.
Những gương thực tiễn về bộ luật an toàn
Bản sao chuỗi an toàn với giới hạn kiểm tra
#include <stdio.h>
#include <string.h>
int safe_string_copy(char *dest, size_t dest_size, const char *src) {
if (!dest || !src || dest_size == 0) {
return -1; // Invalid parameters
}
size_t src_len = strlen(src);
if (src_len >= dest_size) {
// Source too large; truncation or error
// Option: copy what fits and null-terminate
strncpy(dest, src, dest_size - 1);
dest[dest_size - 1] = '\0';
return 1; // Truncation occurred
}
strncpy(dest, src, dest_size);
// strncpy fills remaining with null, so dest_size fits; no need to null-terminate if src shorter
return 0; // Success, no truncation
}
Giải quyết an toàn cho kích cỡ bộ đệm
Lắp chồng bộ đệm cũng có thể kết quả từ các dòng số nguyên tràn khi kích cỡ tính toán. Luôn kiểm tra số học trước khi định vị.
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
void *safe_malloc_array(size_t nmemb, size_t size) {
if (nmemb == 0 || size == 0) {
return NULL; // Or handle zero-size allocation
}
if (nmemb > SIZE_MAX / size) {
// Integer overflow would occur
errno = ENOMEM;
return NULL;
}
return malloc(nmemb * size);
}
Dùng snprintf cho chuỗi định dạng
char log_message[256];
int ret = snprintf(log_message, sizeof(log_message),
"User %s logged in from %s", username, ip_address);
if (ret < 0) {
// Output error
} else if ((size_t)ret >= sizeof(log_message)) {
// Truncation occurred; handle if needed
}
Các thực hành tốt nhất thêm
- Đang khởi động bộ đệm ) — Luôn luôn kích hoạt bộ đệm không kích hoạt để tránh rò rỉ bộ nhớ chưa khởi động.
- Không được đòi lại với độ sâu ) — Xếp chồng có thể tràn từ mức đệ quy sâu; hãy dùng lặp lại hoặc giới hạn.
- Dùng ) — Giúp bộ biên dịch tối ưu hóa và có thể bắt được những vấn đề bí danh, mặc dù không trực tiếp ngăn chặn sự tràn ngập.
- Prefer - Sửa ) — ngăn chặn sự sửa đổi tình cờ chuỗi nhập và thực thi mục đích.
- lỗi bất chấp ) — Đừng bỏ qua giá trị từ những chức năng như , , , v.
Tài nguyên để học hỏi thêm
- SEI CERT Coding Standard ) — Phối hợp các quy tắc mã C bảo mật.
- CEE-120: Bản sao Buffer mà không kiểm tra kích cỡ của nhập ) — MITRE phân loại các yếu tố đệm tràn ngập.
- OWAP Buffer over ) — Hướng dẫn thực tiễn từ Dự án Bảo mật Ứng dụng Mở.
- Sổ tay Thư viện GNU C: Chuỗi và Array Ulicities ) — Tài liệu cho các chức năng dây an toàn.
- Trình quản lý các bộ nhớ ) — Một bộ phát hiện lỗi bộ nhớ nhanh.
Kết luận
Ngăn chặn sự tràn vào vùng C không phải là tùy chọn; đó là trách nhiệm cơ bản của bất kỳ nhà phát triển nào làm việc với ngôn ngữ. Bằng cách hiểu các cơ chế tràn, thay thế các chức năng nguy hiểm bằng các chức năng thay thế, xác định chặt chẽ các dữ liệu nhập và kích cỡ, cho phép biên dịch bảo vệ, sử dụng các phân tích tĩnh và thử nghiệm, bạn có thể giảm đáng kể nguy cơ của các tính năng này. Không có một kỹ thuật nào là đủ; trong trình tự vệ, sự kết hợp sâu sắc hơn, cờ điều chỉnh, giảm biên dịch và kiểm tra kỹ lưỡng — với các thực hiện mạnh mẽ nhất, bạn có thể viết mã C mạnh mẽ, có khả năng hoạt động và có khả năng bảo mật. Hãy nhớ rằng: không có một tính năng sửa chữa liên tục và không sửa chữa được thông tin mật khẩu.