Bỏ qua điều hướng, tới nội dung chính
Học C
Bài 59.428 phút đọc

Checklist bảo mật và fuzzing

Sau bài này bạn sẽ làm được

  • Chạy qua checklist mười hai mục cho một dự án
  • Viết một bộ fuzz bằng libFuzzer hoặc AFL
  • Xóa dữ liệu nhạy cảm đúng cách
  • Biết MISRA C và CERT C dùng cho việc gì

Bài cuối của phần này, và của cả khoá, là một danh sách. Mười hai mục bạn chạy qua trước khi coi một chương trình C là sẵn sàng, cộng với cách để máy tự tìm những lỗ hổng mà bạn không nghĩ ra.

#Checklist mười hai mục

#MụcBắt bằng gìBài
1Mọi hàm chuỗi có giới hạn: không strcpy, strcat, sprintf, getsgrep, -Wformat59.1
2Mọi phép tính kích thước kiểm tra tràn trước khi cấp phátUBSan, xem lại tay59.2
3Mọi độ dài dùng size_t, kiểm tra cả hai cận-Wconversion59.2
4Đối số định dạng của printf luôn là hằng chuỗi-Wformat-security59.2
5Không system hay popen với dữ liệu ngoàigrep59.3
6Đường dẫn được chuẩn hoá và kiểm tra tiền tốxem lại tay59.3
7Thao tác tệp nguyên tử: O_EXCL, O_NOFOLLOW, hàm theo fdgrep access59.3
8Mọi giá trị trả về có thể lỗi đều được kiểm traclang-tidy, cert-err3356.4
9Mọi nhánh lỗi dọn dẹp đúng, không rò rỉASan, Valgrind58.3
10Chạy sạch dưới ASan và UBSansanitizer58.3
11Dữ liệu nhạy cảm được xoá đúng cáchxem lại taymục dưới
12Đã fuzz các bộ phân tích và điểm vào dữ liệu ngoàilibFuzzer, AFLmục dưới

#Fuzzing

Fuzzing
Kỹ thuật đưa hàng triệu đầu vào tự sinh, phần lớn là rác có cấu trúc, vào chương trình để tìm đầu vào làm nó sập, treo, hoặc kích hoạt sanitizer. Fuzzer hiện đại dùng độ phủ để dẫn hướng, nên nó tự học cách đi sâu vào mã.
phan_tich.c, một bộ phân tích có lỗi
#include <stdint.h>
#include <stddef.h>
#include <string.h>

/* Khung: [1 byte loai][1 byte do dai][du lieu]. Tra ve 0 hoac -1. */
int phan_tich(const uint8_t *du_lieu, size_t co) {
    if (co < 2) return -1;
    uint8_t loai   = du_lieu[0];
    uint8_t do_dai = du_lieu[1];
    (void)loai;

    char bo_dem[16];
    /* LOI: khong kiem tra do_dai so voi 16 hay so voi co */
    memcpy(bo_dem, du_lieu + 2, do_dai);
    return bo_dem[0];
}
terminal
# Mo phong bang cach dua tay mot dau vao doc hai
./fuzz.exe
dau vao thuong (do_dai=3): tra ve 97
dau vao doc hai (do_dai=200): sap goi...
Segmentation fault
echo $?
139

#Viết một harness

fuzz_phan_tich.c, harness cho libFuzzer
#include <stdint.h>
#include <stddef.h>

int phan_tich(const uint8_t *du_lieu, size_t co);

/* libFuzzer goi ham nay HANG TRIEU lan voi du_lieu tu sinh. */
int LLVMFuzzerTestOneInput(const uint8_t *du_lieu, size_t co) {
    phan_tich(du_lieu, co);
    return 0;                       /* luon tra ve 0 */
}
terminal
# Dich voi libFuzzer VA sanitizer, chi Clang co san libFuzzer
clang -g -O1 -fsanitize=fuzzer,address,undefined phan_tich.c fuzz_phan_tich.c -o fuzz
# Chay: no tu sinh dau vao va dung khi tim thay loi
./fuzz
INFO: Running with entropic power schedule (0xFF, 100).
INFO: seed corpus: 0 files
#2      INITED
#131072 pulse  cov: 6 ...
==12345==ERROR: AddressSanitizer: stack-buffer-overflow
WRITE of size 200 at 0x7ffc... thread T0
    #0 memcpy
    #1 phan_tich phan_tich.c:14
SUMMARY: AddressSanitizer: stack-buffer-overflow phan_tich.c:14
artifact_prefix='./'; ... Test unit written to ./crash-a1b2c3
# Chay lai DUNG dau vao gay sap de go loi
./fuzz ./crash-a1b2c3
AFL++, cách khác không cần đổi mã
# AFL doc dau vao tu STDIN hoac tu mot tep, nen chuong trinh cua
# ban giu nguyen main() binh thuong. Chi can dich lai bang trinh
# bao boc cua AFL de no chen do do phu:

afl-clang-fast -g -fsanitize=address phan_tich.c chinh.c -o ct

# Tao thu muc hat giong va thu muc ket qua:
mkdir hat_giong ket_qua
printf '\x01\x03abc' > hat_giong/hop_le

# Chay:
afl-fuzz -i hat_giong -o ket_qua -- ./ct @@
#   @@ duoc thay bang ten tep dau vao ma AFL sinh ra

# Man hinh AFL cho thay: so lan chay/giay, so duong da kham pha,
# so lan sap tim duoc. Cac dau vao gay sap nam trong
# ket_qua/crashes/, chay lai duoc de go loi.

# So sanh:
#   libFuzzer  trong tien trinh, nhanh nhat, can viet harness
#   AFL++      ngoai tien trinh, khong can doi ma, ben hon
#              voi chuong trinh phuc tap

#Xoá dữ liệu nhạy cảm

xoa2.c, memset bị tối ưu xoá mất
/* Ham cong khai, nhan mat khau, dung roi xoa. */
void xu_ly(char *mat_khau_ra) {
    char tam[32];
    strncpy(tam, mat_khau_ra, sizeof tam - 1);
    tam[sizeof tam - 1] = 0;
    for (size_t i = 0; i < sizeof tam; ++i)
        mat_khau_ra[i] = tam[i] ^ 0x5A;     /* gia su bam */
    memset(tam, 0, sizeof tam);             /* xoa dau vet */
}
terminal
# -O2: dem so lenh memset trong ham xu_ly
gcc -std=c11 -O2 -S xoa2.c -o - | sed -n '/^xu_ly:/,/ret/p' | grep -c memset
0
xoa3.c, xoá an toàn bằng con trỏ volatile
/* Xoa an toan: khong bi toi uu, vi con tro volatile. */
void xoa_an_toan(void *p, size_t n) {
    volatile unsigned char *v = (volatile unsigned char *)p;
    while (n--) *v++ = 0;
}

void xu_ly(char *mk) {
    char tam[32];
    strncpy(tam, mk, sizeof tam - 1);
    tam[sizeof tam - 1] = 0;
    for (size_t i = 0; i < sizeof tam; ++i) mk[i] = tam[i] ^ 0x5A;
    xoa_an_toan(tam, sizeof tam);       /* KHONG bi toi uu */
}
terminal
gcc -std=c11 -O2 -S xoa3.c -o - | sed -n '/^xoa_an_toan:/,/ret/p' | grep -c 'movb.*0\|mov.*(%'
1

#MISRA C và CERT C

MISRA CCERT C
Của aiMIRA, ngành ô tô AnhĐại học Carnegie Mellon, SEI
Mục đíchAn toàn tính mạng: xe, máy bay, thiết bị y tếAn ninh: chống lỗ hổng khai thác được
Cách tiếp cậnCấm các cấu trúc rủi ro, kể cả khi dùng đúngQuy tắc để tránh lỗ hổng cụ thể
Ví dụ quy tắcCấm cấp phát động, cấm goto, cấm đệ quyKiểm tra tràn, không dùng dữ liệu ngoài làm định dạng
Số quy tắcKhoảng 150 hướng dẫn và quy tắcKhoảng 100 quy tắc, có ví dụ tuân thủ và vi phạm
Kiểm được tự độngMột phần, cần công cụ thương mạiPhần lớn, clang-tidy có nhóm cert-*
Bắt buộc khi nàoHợp đồng ô tô, hàng không, y tếTự nguyện, hoặc yêu cầu của khách hàng

Tự làm thử

  1. Chạy qua checklist mười hai mục cho một dự án của bạn và ghi lại mục nào chưa đạt.
  2. Viết một harness libFuzzer cho một bộ phân tích của bạn và chạy nó trên Linux mười phút.
  3. Chứng minh trên máy bạn rằng memset để xoá mật khẩu bị tối ưu xoá ở -O2.
  4. Cài macro XOA_AN_TOAN đa nền tảng và xác nhận nó sống sót qua -O2.
  5. Chạy clang-tidy --checks='cert-*' trên dự án và đọc từng cảnh báo.
  6. Chọn ba quy tắc MISRA và quyết định dự án của bạn có nên theo chúng không, kèm lý do.

Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.

Tóm tắt

  • Mười trong mười hai mục checklist đã được tự động hoá bằng quy trình năm tầng; chỉ xoá dữ liệu nhạy cảm và fuzzing là làm riêng.
  • Fuzzing thử hàng triệu đầu vào dẫn hướng bằng độ phủ, và mạnh nhất cho bộ phân tích dữ liệu ngoài.
  • Harness phải nhanh, không trạng thái, và luôn dịch kèm sanitizer.
  • memset để xoá bí mật bị trình biên dịch tối ưu xoá; dùng con trỏ volatile hoặc hàm xoá an toàn có sẵn.
  • CERT C là cách gọi tên có hệ thống cho kỷ luật bạn đã học; MISRA C khắt khe hơn, cho hệ thống an toàn tính mạng.