File upload vulnerabilities - Các lỗ hổng upload tệp tin (Phần 2)
II. Phân tích và khai thác các lỗ hổng File upload (tiếp)
3. Kết hợp kỹ thuật path traversal
Đôi khi, hệ thống cài đặt thư mục lưu trữ các tệp do người dùng tải lên không có quyền thực thi. Đây là một cách ngăn chặn tốt, tuy nhiên, kẻ tấn công vẫn có thể tìm kiếm sự "may mắn" ở các thư mục khác bằng cách kết hợp với kỹ thuật path traversal.
Phân tích lab Web shell upload via path traversal
Miêu tả: Trang web chứa lỗ hổng upload file, trong đó cơ chế ngăn chặn không cho phép người dùng thực thi file. Để giải quyết bài lab, chúng ta cần vượt qua cơ chế này, khai thác lỗ hổng nhằm đọc nội dung tệp /home/carlos/secret
. Tài khoản hợp lệ được cung cấp wiener:peter
.
Đăng nhập với tài khoản wiener:peter
. Upload một file php với nội dung như sau:
<?php
echo file_get_contents('/home/carlos/secret');
?>
File được upload thành công:
Tuy nhiên, khi truy cập đường dẫn file chúng ta không thu được nội dung mong muốn, response chỉ trả về nội dung file được upload:
Điều này cho thấy chúng ta đã upload file thành công, tuy nhiên có thể thư mục chứa file upload đang được hệ thống cài đặt để không cho phép thực thi file với định dạng php. Bởi vậy chúng ta có thể kết hợp lỗ hổng Directory traversal, mục đích để đưa file upload tới thư mục khác cho phép thực thi file php, thay đổi tên file thành ../shell.php
.
, trong response trả về, dòng thông báo chỉ còn avatars/shell.php
, có vẻ cơ chế ngăn chặn đã loại bỏ chuỗi ../
. Chúng ta có thể bypass cơ chế này với URL encode: ..%2fshell.php
:
Bình thường file tải lên được lưu trữ tại thư mục /files/avatars
(Có thể kiểm tra bằng cách upload một hình ảnh bình thường), còn trong trường hợp này do đã "lùi" một thư mục nên file được lưu tại thư mục /files
. Có thể file upload được phép thực thi ở thư mục này, truy cập đường dẫn file upload:
File thực thi thành công và chúng ta có nội dung secret, submit và hoàn thành bài lab:
4. Bypass qua file signature - chữ ký tệp tin
Chữ ký tệp (file signature) là một chuỗi xác định các byte duy nhất được ghi vào tiêu đề của tệp. Trên hệ thống Windows, chữ ký tệp thường được chứa trong 20 byte đầu tiên của tệp. Các loại tệp khác nhau sẽ có các chữ ký tệp khác nhau, ví dụ: các tệp JPEG luôn bắt đầu bằng các byte FF D8 FF. Do đó hệ thống có thể kiểm tra định dạng file upload từ người dùng có phải hình ảnh hay không bằng cách kiểm tra signature của file tải lên.
Xét ví dụ sử dụng hàm exif_imagetype()
thực hiện cơ chế này. Hàm exif_imagetype($filename)
sẽ trả về false
nếu tệp tải lên không thuộc các định dạng ảnh quy định, ngược lại nó sẽ trả về giá trị dạng số nguyên tương ứng với từng định dạng ảnh, ví dụ:
Cách cài đặt như sau:
<?php
$file_tmp_name = $_FILES["file"]["tmp_name"];
if (!exif_imagetype($file_tmp_name)) {
die("Your upload file is not an image!");
}
?>
Lúc này chúng ta không thể upload một file với định dạng .php
nữa:
Lưu ý rằng cách hoạt động của hàm exif_imagetype()
là đọc các bytes đầu tiên của file upload và xác định kết quả có phải một tệp hình ảnh hay không dựa vào signature. Bởi vậy chúng ta có thể bypass cơ chế bảo vệ này bằng cách thêm GIF89a
trong phần bắt đầu nội dung file, khi trang web kiểm tra ảnh bằng hàm exif_imagetype()
sẽ xác định đây là một tệp GIF - thuộc một trong các định dạng ảnh quy định, khi đó hệ thống cho phép người dùng tải lên "hình ảnh" này. Thật vậy:
Ngoài ra có thể bypass bằng một số image signature khác:
...
5. Bypass thông qua phần mở rộng file
Một cơ chế ngăn chặn tấn công file upload tốt hơn là dựa vào phần mở rộng của tệp để kiểm tra đó có phải một hình ảnh hợp lệ hay không, ví dụ .png
, .jpg
, .jpeg
, ...
Xét đoạn code xử lý phần mở rộng file upload như sau:
$file_ext = pathinfo($file_name, PATHINFO_EXTENSION);
$file_ext = str_replace('php', '', $file_ext);
$file_name = pathinfo($file_name, PATHINFO_FILENAME) . '.' . $file_ext;
$file_location = $file_dir . basename($file_name);
Biến $file_ext
trích xuất phần mở rộng của file upload bằng hàm pathinfo()
, sau đó sử dụng hàm str_replace()
để loại bỏ chuỗi php
trong phần mở rộng. Lúc này, khi upload file shell.php
thì tên file sau khi xử lý trở thành shell.
. File upload không còn thực thi được là do code PHP thực thi được cần có đuôi file là .php
(hoặc với trường hợp đặc biệt như apache sử dụng .htaccess
để thực thi tệp tin php), do vậy khi tên file được chuyển thành shell.
thì không đủ điều kiện để hệ thống thực thi file này mặc dù nội dung file là mã PHP.
Do hàm str_replace()
chỉ thực hiện thay thế chuỗi đầu tiên tìm kiếm được, nên chúng ta có thể bypass hàm này bằng cách sử dụng extension pphphp
, như vậy sau khi thay thế thì shell.pphphp
sẽ trở thành shell.php
.
Xét một ví dụ khác xử lý phần mở rộng file:
$file_ext = pathinfo($file_name, PATHINFO_EXTENSION);
$black_list = ['php', 'Php', 'pHp', 'phP', 'PHp', 'PhP', 'pHP', 'PHP'];
if (in_array($file_ext, $black_list)) {
return('Invalid extention!');
}
Sử dụng hàm in_array()
kiểm tra phần mở rộng nếu nằm trong danh sách cấm $black_list
sẽ ngăn cản người dùng upload. Chúng ta có thể bypass điều này bằng cách sử dụng một số kiểu phần mở rộng khác như .php5, .php7, .phps, ...
Phân tích lab Web shell upload via obfuscated file extension
Miêu tả: Trang web chứa lỗ hổng upload file, trong đó có một black list các extension file. Để giải quyết bài lab, chúng ta cần vượt qua cơ chế này, khai thác lỗ hổng nhằm đọc nội dung tệp /home/carlos/secret
. Tài khoản hợp lệ được cung cấp wiener:peter
.
Đăng nhập với tài khoản wiener:peter
, với chức năng upload avatar, thử tải lên một file shell.php
với nội dung như sau:
<?php
echo file_get_contents('/home/carlos/secret');
?>
Hệ thống chỉ cho phép file dạng JPG và PNG. Tất cả các cách bypass phía trên đều không hiệu quả, chúng ta cần nghĩ cách vượt qua black list hệ thống. Trong trường hợp này chúng ta có thể sử dụng ký tự null byte %00
để vượt qua cơ chế này (Lưu ý rằng bắt đầu từ phiên bản PHP 5.3.4, ký tự Null byte đã được fix). Sử dụng tên file shell.php%.png
phần mở rộng trang web nhận được là .png
không nằm trong black list, sau khi xử lý thì các ký tự bắt đầu từ ký tự Null byte được loại bỏ, tên file chỉ còn shell.php
có thể thực thi.
Truy cập tới đường dẫn file upload thu được nội dung /home/carlos/secret
:
Submit nội dung secret, bài lab hoàn thành:
Phân tích lab Web shell upload via extension blacklist bypass
Miêu tả: Trang web chứa lỗ hổng upload file, trong đó có một black list các extension file. Để giải quyết bài lab, chúng ta cần vượt qua cơ chế này, khai thác lỗ hổng nhằm đọc nội dung tệp /home/carlos/secret
. Tài khoản hợp lệ được cung cấp wiener:peter
.
Đăng nhập với tài khoản wiener:peter
, với chức năng upload avatar, thử tải lên một file shell.php
với nội dung như sau:
<?php
echo file_get_contents('/home/carlos/secret');
?>
Hệ thống ngăn chặn người dùng tải lên file dạng php. Tất cả các cách bypass phía trên đều không hiệu quả. Lưu ý rằng chúng ta có thể tải lên nhiều file ảnh với cùng tên, và khi truy cập tới đường dẫn file upload sẽ hiển thị hình ảnh cuối cùng upload (các hình ảnh đã upload có cùng tên). Như vậy khả năng lớn hệ thống sẽ thay thế ảnh cuối cùng vào vị trí ảnh trước đó có cùng tên. Điều này dẫn đến chúng ta có thể ghi đè nội dung các file đã có.
Chúng ta nhắm tới file .htaccess - là một file có ở thư mục gốc của các hostting và do apache quản lý, cấp quyền. File .htaccess có thể điều khiển, cấu hình được nhiều yếu tố với đa dạng các thông số, nó có khả năng thay đổi các giá trị được set mặc định của Apache.
Upload một file với tên .htaccess
, thay đổi header Content-Type thành giá trị text/plain, nội dung file như sau:
AddType application/x-httpd-php .viblo
Dòng lệnh trên sẽ tạo ra một ánh xạ cho phép các file có phần mở rộng là .viblo
có quyền thực thi với Content-Type: application/x-httpd-php.
Như vậy, hiện tại chúng ta có thể upload các file với phần mở rộng .viblo
và có thể thực thi các đoạn code tương đương với một file php.
Truy cập tới /avatars/shell.viblo
:
Submit nội dung secret và bài lab hoàn thành:
V. Các tài liệu tham khảo
- https://portswigger.net/web-security/file-upload
- https://en.wikipedia.org/wiki/List_of_file_signatures
©️ Tác giả: Lê Ngọc Hoa từ Viblo
All rights reserved