Mô tả:
Điều kiện đua
●
Xảy ra khi nhiều tiến trình (tiểu trình) truy cập
và sửa đổi cùng một dữ liệu vào cùng lúc, kết
quả phụ thuộc vào thứ tự truy cập
Dữ liệu chung
Tiến trình 1
Tiến trình 2
Điều kiện đua
●
Để tận dụng lỗi, người ta thường chạy thật
nhiều tiến trình ngoài để “đua” với tiến trình bị
lỗi → chữ “đua”
●
Còn được gọi là TOC/TOU
●
Xét ví dụ:
●
if (access[argv[1], R_OK) == 0)
{
f = fopen(argv[1], “r”);
}
Điều kiện đua
●
Giữa hàm access() và fopen() có một khoảng
thời gian nhỏ
●
●
CALL
CMP
JNZ
PUSH
PUSH
CALL
access
EAX, 0
...
...
...
fopen
Hệ điều hành có thể chuyển qua tiến trình khác
ở giữa các lệnh đó
Điều kiện đua
Điều kiện đua
●
access() và fopen() nhận tên file
●
Cùng một tên nhưng có thể là 2 file khác nhau
●
●
symlink
Do đó, ta sẽ tận dụng bằng cách:
●
●
●
●
Chỉ symlink đến tập tin có thể đọc được
Chỉ symlink đến tập tin không có quyền đọc nhưng
chương trình bị lỗi có thể đọc được
Lập lại 2 việc này liên tục
Song song đó, ta sẽ chạy chương trình bị lỗi và
truyền tên symlink vào
Điều kiện đua
●
●
●
Điều kiện đua thường gặp ở các ứng dụng xử
lý file, ứng dụng mạng, database, ứng dụng đa
tiểu trình, ứng dụng web
Ứng dụng web đặc biệt dễ mắc phải vì vừa đa
tiến (tiểu) trình, vừa truy cập database, vừa truy
cập file
Cách dễ nhất để tránh lỗi là tuần tự hóa truy
cập vào tài nguyên chung
●
●
File lock, database isolation level
Spinlock, mutex, futex, CRITICAL_SECTION,
semaphore
Điều kiện đua
●
Cẩn thận với deadlock
●
●
Xét ví dụ:
●
●
Khi hai hoặc nhiều tiến (tiểu) trình chờ nhau
lock(resource1)
lock(resource2)
dosomething()
lock(resource2)
lock(resource1)
dosomething()
Cách khắc phục
●
●
●
Luôn luôn chú ý về thứ tự truy cập
Lock phải được cấp theo cùng thứ tự, và mở theo
thứ tự ngược
Lock phải được mở ngay sau khi đã dùng xong
Dư một
●
●
Là trường hợp đặc biệt của tràn bộ đệm trong
đó chỉ tràn duy nhất 01 byte
Xét ví dụ
●
void vuln(char *arg)
{
char buf[8];
strcpy(buf, arg);
}
void main(int argc, char **argv)
{
vuln(argv[1]);
}
Dư một
●
Giả sử argv[1] dài 8 ký tự
●
Khi vào vuln() thì biến buf sẽ chứa 8 ký tự này
●
Và ký tự kết thúc (NUL) sẽ lem ra ngoài
●
Ký tự \x00 này lem vào ô chứa EBP cũ
●
●
Trong phần kết thúc của vuln(), POP EBP vì thế
sẽ khiến EBP mang giá trị XXXXXX00
Trong phần kết thúc của main(), MOV ESP,
EBP sẽ khiến ESP có giá trị XXXXXX00
Dư một
●
●
●
●
Sau đó POP EBP sẽ làm ESP tăng thêm 4
Cuối cùng RET sẽ lấy giá trị hiện tại trên đỉnh
ngăn xếp để quay về → lỗi xảy ra trong vuln()
nhưng tận dụng trong main()
Hai điểm cần chú ý:
●
Giá trị EBP mới sẽ nhỏ hơn giá trị EBP đã lưu
●
Và do đó có thể chỉ tới phần ngăn xếp trong vuln()
Giả sử biến buf có địa chỉ tận cùng là 00 → cơ
hội EBP chỉ tới buf sẽ cao → địa chỉ trở về của
main() sẽ là từ địa chỉ của biến buf + 4
Dư một
Dư một
Dư một
●
Yêu cầu phải đạt để tận dụng được lỗi
●
●
●
Ngăn xếp của vuln() không bị phá (nội dung biến
buf không bị mất đi)
Vùng nhớ kiểm soát được phải nằm “gần” với giá trị
EBP cũ, chỉ được khác byte thấp
Cách khắc phục lỗi như với lỗi tràn bộ đệm
Tràn số nguyên
●
●
●
●
●
Số nguyên trong máy tính được biểu diễn
●
Ở dạng bù 2
●
Có dấu, hoặc không dấu
Giá trị âm lớn nhất cũng có thể là giá trị dương
cao nhất
Số có dấu bị lệch về giá trị âm (-128 → 127)
Hiện tượng quay vòng (127 + 1 = -128, 255 + 1
= 0)
Kích thước kiểu dữ liệu ảnh hưởng quan trọng
(255 kiểu short là -1 kiểu char)
Tràn số nguyên
●
●
Tràn số nguyên xảy ra khi giá trị số nằm ngoài
phạm vi biểu diễn của kiểu dữ liệu
Xét ví dụ:
●
●
●
●
int i;
i = atoi(argv[1]);
if (i <= 0x1000) fgets(buf, i, stdin);
atoi() trả về số nguyên có dấu
Tham số thứ 2 của fgets() là kiểu số nguyên
không dấu
Nếu argv[1] là số âm thì sẽ gây tràn
Tràn số nguyên
●
Xét ví dụ:
●
int i = -maxint; int j = -1;
printf(“%d\n”, i / j);
●
i là số âm nhất (giả sử -128)
●
j là -1
●
Khi thực hiện phép chia sẽ ra số 128, nhưng
128 vượt quá phạm vi biểu diễn (127) → crash
- Xem thêm -