Phân tích kỹ thuật các lỗ hổng SQL Injection trên ThinkPHP và CmsEasy

1. Lỗ hổng SQL Injection trong ThinkPHP (Phương thức select)

Lỗ hổng này phát sinh trong phương thức parseWhereItem thuộc lớp Mysql. Nguyên nhân chính do hệ thống không kiểm soát chặt chẽ các toán tử đầu vào, cho phép dữ liệu từ người dùng được nối trực tiếp vào câu lệnh SQL. Lỗ hổng này ảnh hưởng đến các phiên bản ThinkPHP 5.0.x.

1.1 Cấu hình môi trường mô phỏng

Để kiểm tra, cần thiết lập tệp composer.json với phiên bản framework cụ thể:

"require": {
    "php": ">=5.4.0",
    "topthink/framework": "5.0.15"
}

Xây dựng một Controller mẫu tại application/index/controller/Index.php để tiếp nhận dữ liệu:

<?php
namespace app\index\controller;

class Index
{
    public function index()
    {
        $inputData = request()->get('id_search');
        // Sử dụng toán tử 'exp' không an toàn
        $data = db('users')->where('username', 'exp', $inputData)->select();
        return 'Query processed';
    }
}

Kích hoạt chế độ app_debugapp_trace trong tệp cấu hình để theo dõi quá trình thực thi câu lệnh SQL.

1.2 Cơ chế phát sinh lỗi

Khi ứng dụng gọi phương thức select(), Framework sẽ bắt đầu quá trình xây dựng truy vấn (Query Building). Luồng xử lý đi qua parseWhereExp và tiến tới buildWhere. Tại đây, phương thức parseWhereItem được gọi để phân tích các điều kiện truy vấn.

Trong mã nguồn của ThinkPHP 5.0.x, khi toán tử được xác định là EXP, giá trị đầu vào từ người dùng sẽ được đưa trực tiếp vào chuỗi SQL mà không thông qua cơ chế ràng buộc tham số (parameter binding). Điều này cho phép kẻ tấn công chèn các hàm SQL hoặc cấu trúc truy vấn bất hợp pháp thông qua mảng đầu vào.

2. Lỗ hổng SQL Injection qua phương thức Order By và Aggregate

Lỗ hổng này xuất hiện trong quá trình xử lý các hàm gộp (aggregation) như max(), min(), avg(). Phiên bản bị ảnh hưởng thường nằm trong khoảng từ 5.1.16 đến 5.1.22.

2.1 Thiết lập thử nghiệm

Cập nhật phiên bản framework trong composer.json:

"require": {
    "php": ">=5.6.0",
    "topthink/framework": "5.1.22"
}

Mã triển khai tại Controller:

<?php
namespace app\index\controller;

class Index
{
    public function index()
    {
        $fieldParam = request()->get('target_field');
        // Lỗ hổng xảy ra khi gọi hàm aggregate thông qua max()
        $maxValue = db('products')->max($fieldParam);
        return var_dump($maxValue);
    }
}

2.2 Phân tích luồng thực thi

Phương thức max() sẽ gọi đến aggregate() bên trong lớp Query. Dữ liệu từ tham số $fieldParam sau đó được chuyển tới phương thức parseKey của lớp Builder để xử lý tên cột.

Vấn đề nằm ở chỗ hàm parseKey trong các phiên bản này không lọc bỏ các ký tự đặc biệt một cách triệt để nếu tên cột chứa các dấu đóng ngoặc hoặc các biểu thức phức tạp. Khi chuỗi này được nối vào câu lệnh SQL cuối cùng, nó làm thay đổi logic của truy vấn nguyên bản.

2.3 Phương pháp khắc phục

ThinkPHP đã phát hành bản vá bằng cách bổ sung kiểm tra biểu thức chính quy trong hàm parseKey. Nếu tên trường chứa các ký tự không hợp lệ (ngoài chữ cái, chữ số, dấu chấm và dấu sao), hệ thống sẽ ném ra ngoại lệ thay vì tiếp tục xử lý.

3. Phân tích bảo mật trên hệ thống CMS (Trường hợp CmsEasy)

3.1 Truy cập trái phép và Giải mã tham số

Trong một số trường hợp, cấu trúc điều hướng của CMS cho phép bỏ qua kiểm tra quyền quản trị nếu không được cấu hình đúng trong phương thức khởi tạo. Một điểm yếu quan trọng nằm ở việc xử lý tham số qua các hàm mã hóa tùy chỉnh như XXTEA.

Logic xử lý thường như sau:

  1. Lấy dữ liệu thô từ request.
  2. Giải mã Base64.
  3. Giải mã bằng hàm xxtea_decrypt với một mã khóa bảo mật (security key) được lưu trong cấu hình hệ thống.
  4. Tiến hành unserialize dữ liệu sau giải mã để đưa vào truy vấn cơ sở dữ liệu.

3.2 Rủi ro từ Deserialize và Injection

Nếu mã khóa bảo mật bị lộ hoặc có thể dự đoán được, kẻ tấn công có thể tạo ra các chuỗi dữ liệu đã được serialize, sau đó mã hóa ngược lại bằng thuật toán XXTEA để gửi lên máy chủ. Khi máy chủ giải mã và thực thi truy vấn từ dữ liệu này, lỗ hổng SQL Injection sẽ xảy ra tại các hàm xử lý mảng (như getrow) do việc nối chuỗi không an toàn từ các khóa (keys) hoặc giá trị (values) trong mảng đã được giải mã.

Để phòng tránh, các hệ thống này cần:

  • Thay đổi mã khóa bảo mật mặc định ngay sau khi cài đặt.
  • Sử dụng các phương thức truy vấn an toàn (Prepared Statements) ngay cả với dữ liệu đã được giải mã nội bộ.
  • Hạn chế sử dụng hàm unserialize trên các nguồn dữ liệu có thể bị tác động từ bên ngoài.

Thẻ: ThinkPHP SQL Injection Web Security Vulnerability Analysis PHP Framework

Đăng vào ngày 24 tháng 9 lúc 08:01