Lỗ hổng này thường xảy ra khi lập trình dùng các lệnh để include file, chẳng hạn như đối với PHP thì là include
, require
, include_once
, và require_once
. Cần lưu ý là lỗ hổng này vẫn có thể xảy ra với những ngôn ngữ khác.
Để thực hiện tấn công LFI, ta sẽ sử dụng kỹ thuật Path Traversal.
First scenario
Giả sử trang web có hai ngôn ngữ là tiếng Anh và tiếng Việt.
Người dùng thay đổi ngôn ngữ bằng cách gửi request đến đường dẫn http://webapp.thm/index.php?lang=EN.php
hoặc http://webapp.thm/index.php?lang=VI.php
. Trong đó EN.php
và VI.php
là hai files cùng thư mục với index.php
.
Đoạn code xử lý của server như sau:
// index.php
<?php
include($_GET["lang"]);
?>
Có thể thấy, toàn bộ file được included vào index.php
và được hiển thị ra cho người dùng. Ngoài ra, do không specify một directory cụ thể nên đường dẫn truyền vào hàm include
có thể là bất cứ thứ gì 😱.
Khi đó, chúng ta có thể thay đổi tên file thành một đường dẫn tùy ý, chẳng hạn như /etc/passwd
(là một file có chứa những thông tin nhạy cảm về các người dùng của hệ điều hành Linux).
Give Lab #1 a try to read /etc/passwd. What would the request URI be?
/lab1.php?file=/etc/passwd
Second scenario
Nếu ta thêm specify một thư mục ở phía trước:
// index.php
<?PHP
include("languages/". $_GET['lang']);
?>
Mặc dù không thể truyền vào /etc/passwd
như trước nhưng chúng ta vẫn có thể sử dụng path traversal để mò ra file /etc/passwd
. Chẳng hạn như: http://webapp.thm/index.php?lang=../../../../etc/passwd
.
In Lab #2, what is the directory specified in the include function?
includes
Third scenario
Xét trường hợp kiểm thử hộp đen, chúng ta không có mã nguồn. Khi đó, manh mối duy nhất để tấn công chính là các thông báo lỗi. Giả sử ta nhập vào một input sai, chẳng hạn THM
, ta sẽ nhận được thông báo lỗi như sau:
Warning: include(languages/THM.php): failed to open stream: No such file or directory in /var/www/html/THM-4/index.php on line 12
Qua thông báo này, ta biết được rằng hàm include
được gọi sẽ có dạng:
include(languages/THM.php)
Đồng thời, ta cũng biết được rằng code sẽ tự thêm vào đuôi .php
phía sau input. Tức là, input nếu hợp lệ sẽ là EN
hoặc VI
thay vì EN.php
hoặc VI.php
.
Ngoài ra, thông báo này còn để lộ đường dẫn của toàn bộ ứng dụng web: /var/www/html/THM-4/
🥶. Khi có thông tin này, ta sử dụng 4 lần ../
để truy ngược về thư mục gốc của hệ điều hành (bởi vì đường dẫn có 4 level). Sau đó truy cập vào đường dẫn /etc/passwd
như các kịch bản ở trên:
http://webapp.thm/index.php?lang=../../../../etc/passwd
Tuy nhiên, đến lúc này, ta vẫn bị một lỗi như sau:
Warning: include(languages/../../../../etc/passwd.php): failed to open stream: No such file or directory in /var/www/html/THM-4/index.php on line 12
Lỗi này xảy ra vì code tự thêm vào đuôi .php
làm cho tên file trở thành không chính xác. Để giải quyết vấn đề này, ta có thể sử dụng NULL BYTE, tức là giá trị %00
. Giá trị này là một giá trị giúp kết thúc chuỗi được sử dụng trong URL-encoded (trong chuỗi đường dẫn).
Khi sử dụng giá trị này, câu lệnh:
include("languages/../../../../etc/passwd%00" . ".php");
Sẽ tương đương với:
include("languages/../../../../etc/passwd");
Null byte injection
Trick
%00
không áp dụng với PHP 5.3.4 trở lên 😉.
Give Lab #3 a try to read /etc/passwd. What is the request look like?
/lab3.php?file=../../../../etc/passwd%00
Fourth scenario
Giả sử developer lọc các request có input là /etc/passwd
. Khi đó, ta có thể sử dụng trick %00
như ở trên hoặc dùng /.
. Chuỗi ký tự /.
tương đương với việc truy cập vào tập tin/thư mục hiện tại: http://webapp.thm/index.php?lang=/etc/passwd/.
.
Khi đó, do input /etc/passwd/.
và /etc/passwd/%00
khác /etc/passwd
nên chúng ta có thể bypass được filter.
Which function is causing the directory traversal in Lab #4?
file_get_contents
Fifth scenario
Giả sử ta gửi đến server một request:
http://webapp.thm/index.php?lang=../../../../etc/passwd
Và server trả về một lỗi như sau:
Warning: include(languages/etc/passwd): failed to open stream: No such file or directory in /var/www/html/THM-5/index.php on line 15
Có thể thấy, các chuỗi ký tự ../
ở trong request đã bị thay thế bằng chuỗi rỗng. Nói cách khác, developer đã lọc ra các chuỗi ký tự dùng để thực hiện path traversal.
Chúng ta có thể bypass bằng cách thay input trong request thành: ....//....//....//....//....//etc/passwd
.
Hàm thay thế của PHP chỉ thay thế tất cả các chuỗi con ../
một lần duy nhất mỗi lần gọi hàm nên input của chúng ta sẽ trở thành ../../../../etc/passwd
.
Minh họa:
Sixth scenario
Trường hợp cuối cùng, nếu developer bắt buộc input phải có một thư mục được định nghĩa trước nào đó, chẳng hạn như includes
như trong request sau đây:
http://webapp.thm/index.php?lang=languages/EN.php
Việc khai thác vô cùng đơn giản: chúng ta dùng path traversal (thêm một lần nữa) để truy ngược ra /etc/passwd
. Cụ thể như sau:
http://webapp.thm/index.php?lang=languages/../../../../../etc/passwd
Try out Lab #6 and check what is the directory that has to be in the input field?
THM-profile
Try out Lab #6 and read /etc/os-release. What is the VERSION_ID value?
12.04