3.6. TẤN CÔNG DIRECTORY TRAVERSAL
Thông thường, web server sẽ từ chối client truy cập vào các tài nguyên không nằm trong thư mục gốc của web server.
Ví dụ: Nếu thư mục gốc của web server là
/home/www, client yêu cầu truy cập vào tài nguyên
/etc/passwd thì sẽ bị web server từ chối vì tài nguyên trên không nằm trong
/home/www. Tuy nhiên, có một hình thức tấn công có thể truy cập vào tài nguyên này mà không cần sử dụng một công cụ nào, chỉ đơn thuần thao tác với các biến với
../ (dot-dot-slash) để thực hiện liên kết mềm (symbolic) truy cập đến các file nằm trong thư mục cha của thư mục hiện hành. Đó là tấn công Directory Traversal.
Dưới đây là một ví dụ cụ thể về loại hình tấn công này.
Code: http://example.com/getpage.php?page=../../../etc/passwd
Như ta thấy trên URI, thực hiện chèn biến
../ ba lần để liên kết mềm về thư mục gốc
/ . Sau đó thêm vào etc/passwd để tạo thành request đến thư mục /etc/passwd. Nếu không cấu hình cản lọc để request này, nội dung của file
/etc/passwd sẽ được hiển thị trên trình duyệt của hacker.
Hacker có thể không chèn trực tiếp biến
../ mà thực hiện chèn các biến đã được mã hoá như
%2e%2e%2f, khi giải mã thì nội dung vẫn là
../
Dưới dây là danh sách các chuỗi chúng ta cần chặn:
- ../
- ..%2f
- %2e%2e/
- %2e%2e%2f
- %2e./
Với ModSecurity, chỉ cần sử dụng chức năng chuyển đổi
t:urlDecode để chuyển tất cả chuỗi được mã hoá thành giá trị nguyên thuỷ của nó. Dưới đây là rule để chặn hình thức tấn công này:
Code: SecRule REQUEST_URI "../" "t:urlDecode,deny"
3.7. TẤN CÔNG SQL INJECTION
3.7.1. Giới thiệu
Các cuộc tấn công SQL injection có thể xảy ra nếu hacker cung cấp dữ liệu cho ứng dụng web mà các dữ liệu này không được cản lọc, làm sai lệch các câu lệnh SQL khi thực thi. Điển hình là nhập vào các chuỗi đặc biệt để bỏ qua bước kiểm tra mật khẩu của người dùng và làm cho đăng nhập hợp lệ:
Ví dụ, chúng ta xét câu lệnh truy vấn khi thực hiện đăng nhập dưới đây:
Code: SELECT * FROM user WHERE username = '%s' AND password = '%s';
Với truy vấn trên, nếu hacker muốn đăng nhập vào với tài khoản admin mà chưa biết mật khẩu, hacker sẽ thực hiện nhập tên đăng nhập là admin và mật khẩu là
‘ OR ‘1’ = ‘1 -- Khi thực hiện truy vấn, thông tin đăng nhập được đưa vào sẽ trở thành:
Code: SELECT * FROM user WHERE username = 'admin' AND password = '’ OR ‘1’ = ‘1’;
Câu lệnh trên có nghĩa: Truy vấn lấy thông tin tất cả các trường từ bảng user với điều kiện trường username có giá trị là admin và mật khẩu là trống
‘’ hoặc
1 = 1. Mà
1 = 1 là luôn đúng nên mặc dù mật khẩu không đúng, câu lệnh trên vẫn được thực thi và truy vấn lấy thông tin user admin thành công. Đăng nhập sẽ được chấp nhận.
3.7.2. Các hình thức tấn công SQL injection thông thường
3.7.2.1. Lấy dữ liệu từ nhiều bảng với UNION
Câu lệnh SQL UNION có thể được sử dụng để lấy dữ liệu từ hai bảng khác nhau. Ví dụ: Nếu có một bảng có tên cooking_recipes và bảng khác có tên
ser_credentials, lệnh SQL sau sẽ lấy dữ liệu từ cả hai bảng:
Code: SELECT dish_name FROM cooking_recipe UNION SELECT username, password FROM user_credentials;
Một câu lệnh SQL tương tự là
UNION ALL, hoạt động gần giống như UNION. Sự khác biệt duy nhất là UNION ALL sẽ không loại bỏ các dòng bị trùng lặp trong kết quả trả về.
3.7.2.2. Nhiều truy vấn trong một lời gọi
Nếu SQL engine cho phép nhiều câu lệnh SQL trong một truy vấn SQL đơn, ta sẽ thấy nguy cơ bảo mật xuất hiện. Ví dụ sau có thể giải thích nguy cơ này.
Code: SELECT * FROM products WHERE id = %d;
Nếu hacker cung cấp ID bằng một đoạn lệnh
1;DROP TABLE products; câu lệnh SQL sẽ trở thành:
Code: SELECT * FROM products WHERE id = 1; DROP TABLE products;
Khi câu lệnh này được thực hiện, đầu tiên nó sẽ truy vấn tất cả các trường trong bảng products có id bằng 1, sau đó bảng products sẽ bị xoá.
3.7.2.3. Đọc nội dung của một file
MySQL có thể được sử dụng để đọc nội dung của một file trên hệ thống bằng cách sử dụng hàm LOAD_FILE(). Ví dụ:
Code: SELECT LOAD_FILE(“/etc/passwd”);
Lệnh sẽ trả về nội dung của file /etc/passwd nếu tiến trình MySQL có quyền truy cập.
3.7.2.4. Ghi dữ liệu vào tập tin
MySQL cũng hỗ trợ lệnh ghi dữ liệu vào tập tin với
OUTFILE. Rất nguy hiểm nếu hacker lợi dụng và khai thác được lệnh này, bởi thực thi OUTFILE không những ảnh hưởng đến cơ sở dữ liệu mà còn ảnh gây hưởng đến tập tin hệ thống.
Dưới đây là một ví dụ đơn giản sử dụng MySQL để ghi dữ liệu vào file text.txt
Code: SELECT "vi du ghi du lieu" INTO OUTFILE "test.txt";
3.7.3. Ngăn chặn tấn công SQL injection
Có ba bước quan trọng để ngăn chặn tấn công SQL injection:
1. Làm “trong sáng” câu lệnh SQL trong ứng dụng web.
2. Lọc dữ liệu người dùng đưa lên.
3. Sử dụng ModSecurity để chặn mã SQL injection khi hacker khai thác lỗi ứng dụng web.
Trong ba bước trên, quan trọng nhất vẫn là bước đầu tiên. Sử dụng các câu lệnh SQL “trong sáng” nghĩa là sử dụng các câu lệnh chuẩn, không không gây ra lỗ hổng SQL injection cho ứng dụng web.
Bước thứ hai là đảm bảo tất cả dữ liệu người dùng cung cấp cho truy vấn SQL đểu được cản lọc, tránh bị chèn vào các đoạn mã nguy hiểm như dấu ngoặc đơn, dấu nháy đơn… Nếu ứng dụng web viết bằng PHP, có thể sử dụng các
mysql_real_escape_string() để cản lọc.
Và cuối cùng, sử dụng ModSecurity để ngăn chặn triệt để tấn công SQL injection.
Dưới đây là bảng liệt kê danh sách các lệnh thường được sử dụng trong tấn công SQL injection cùng với các biểu thức chính quy dùng để ngăn chặn.
Bảng 3.3 Các lệnh thường được sử dụng trong tấn công SQL injection
Ví dụ về rule để chặn tấn công SQL injection ghi dữ liệu vào tập tin.
Code: SecRule ARGS "into\s+outfile" "t:lowercase,deny,msg:'SQL Injection'"
Cú pháp
\s+ cho phép phát hiện tất các hình thức nhập vào của ký tự trắng (space) ví dụ như
INTO%20%20OUTFILE.
3.8. TẤN CÔNG BRUTE FORCE
Với tấn công Brute Force, hacker thực hiện đoán các thông tin đăng nhập như tên người dùng, mật khẩu, email… và thực hiện đăng nhập liên tục đến khi nào thông tin đăng nhập là đúng. Hầu hết người dùng đều sử dụng thông tin đăng nhập giống nhau trên tất cả các website mà họ thường đăng nhập, dẫn đến tài khoản của họ bị xâm nhập trên hàng loạt các website khi thông tin đăng nhập bị lộ bởi một website khác.
Cách tốt nhất để ngăn chặn hình thức tấn công này là giới hạn số lần đăng nhập không đúng. Ví dụ nếu người sử dụng đăng nhập không đúng quá 3 lần, thực hiện khoá đăng nhập của người này trong 5 phút.
Dưới đây là các rule của ModSecurity cho phép chúng ta thực hiện điều này
Code: # Khoa dang nhap sau 3 lan dang nhap khong thanh cong
#
<LocationMatch ^/login>
# Khoi tao collection ip
SecAction "initcol:ip=%{REMOTE_ADDR},pass,nolog"
# Phat hien dang nhap khong thanh cong
SecRule RESPONSE_BODY "Username does not exist" "phase:4,pass,setvar:
ip.failed_logins=+1,expirevar:ip.failed_logins=300"
# Khoa dang nhap khi so lan dang nhap khong thanh cong bang 3
SecRule IP:FAILED_LOGINS "@gt 3" deny
</Location>
Các rule trên dựa vào đặt điểm trả về của website khi người truy cập đăng nhập không thành công:
Username does not exist
Các rule trên sẽ khởi tạo collection IP và tăng giá trị biến
ip.failed_login lên một đơn vị sau mỗi lần đăng nhập không thành công. Action
expirevar sẽ thiết lập biến ip.failed_login về 0 sau 5 phút. Vì vậy, khi biến
ip.failed lớn hơn hoặc bằng 3, rule cuối sẽ khoá đăng nhập của người dùng trong 5 phút.
Hoặc chúng ta có thể thực hiện trì hoãn (hay dừng) request của người dùng khi số lần đăng nhập sai vượt quá quy định. Do đó, không cần phải từ chối truy cập như các rule được nêu ở trên. Sau đây là rule thực hiện điều trên:
Code: # tri hoan request 3 giay sau 3 lan dang nhap khong thanh cong
<LocationMatch ^/login>
SecAction "initcol:ip=%{REMOTE_ADDR},pass,nolog"
SecRule RESPONSE_BODY "Username does not exist" "phase:4,pass,setvar:
ip.failed_logins=+1,expirevar:ip.failed_logins=10"
SecRule IP:FAILED_LOGINS "@gt 3" "phase:4,allow,pause:3000"
</Location>
Thời gian trì hoãn được tính bằng mili giây, các rule trên sẽ trì hoãn response trong 3 giây khi số lần truy cập không thành công lớn hơn hoặc bằng 3.
3.9. DIRECTORY INDEXING
Khi người sử dụng request đến URL như
http://example.com, URL này không request cụ thể đến một tập tin nào trên web server (ví dụ
http://example.com/page.html request cụ thể đến
page.html), vì vậy Apache sẽ tìm đọc tập tin theo cấu hình DirectoryIndex (thường là index.html, index.php …). Nếu không có tập tin nào có trong danh sách tập tin đã được cấu hình trong DirectoryIndex., Apache xem xét tuỳ chọn Indexes có được kích hoạt hay không. Tuỳ chọn này có thể được kích hoạt theo cách sau
Code: <Directory /home/www/rangdong1>
Options +Indexes
</Directory>
Nếu cấu hình tuỳ chọn Indexes cũng không được tìm thấy hoặc không kích hoạt. Apache sẽ liệt kê ra tất cả các thư mục và tập tin hiển thị cho người dùng.
Hình 3.4 Liệt kê thư mục và tập tin với Indexes
Với việc liệt kê ra danh sách các thư mục, tập tin sẽ làm cho người dùng có thể xem được cấu trúc của mã nguồn website, có thể tải về các tập tin không được phép…Vì vậy, chúng ta có thể tắt indexes bằng cách khai báo trong httpd.conf:
Code: <Directory /home/www>
Options -Indexes
</Directory>
Hoặc cũng có thể tắt indexes bằng cách cấu hình không cho chạy module mod_autoindex.so
Code: # Disable directory indexing
# LoadModule autoindex_module modules/mod_autoindex.so
Lưu ý: Một số module của Apache yêu cầu mod_autoindex.so hoạt động, vì vây nếu tắt module này có thể dẫn đến Apache không khởi động được.
Để đảm bảo chắc chắc rằng người dùng không thể liệt kê được các file và thư mục. Chúng ta có thể sử dụng ModSecurity với rule:
Code: SecRule REQUEST_URI "/$" "phase:4,deny,chain,log, msg:'Directory index returned'"
SecRule RESPONSE_BODY "<h1>Index of /"
Khi người dùng thực hiện liệt kê file, thư mục. Apache sẽ trả về response body có chứa
<h1>Index of /, vì vậy rule trên sẽ chặn lại và ghi log với nội dung Directory index returned
Kết thúc chủ đề Bào mật web server Apache với ModSecurity
Cảm ơn đã quan tâm theo dõi..