UNION-Based SQL Injection (SQL injection-4).
Ý kiến
0
Chưa có ý kiến nào. Hãy là người đầu tiên chia sẻ!
Chưa có ý kiến nào. Hãy là người đầu tiên chia sẻ!
-SQL Injection (SQLi) là lỗ hổng bảo mật cho phép kẻ tấn công chèn các câu lệnh SQL độc hại vào ô nhập liệu của ứng dụng. Khi ứng dụng "nối chuỗi" trực tiếp input người dùng vào câu lệnh SQL, nó sẽ vô tình cho phép kẻ tấn công điều khiển cơ sở dữ liệu.
Để thực hành, bạn cần XAMPP (Apache + MySQL).
Bước 1: Tạo database Truy cập http://localhost/phpmyadmin, tạo database shop_lab và chạy lệnh sau:
SQL
-- Tạo database
CREATE DATABASE IF NOT EXISTS shop_lab;
USE shop_lab;
-- Tạo bảng sản phẩm
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
description TEXT,
category VARCHAR(50)
);
-- Tạo bảng users (tên ngẫu nhiên để mô phỏng bài lab)
CREATE TABLE users_zhgcyo (
id INT AUTO_INCREMENT PRIMARY KEY,
username_kolqfw VARCHAR(50),
password_imavym VARCHAR(50)
);
-- Chèn dữ liệu mẫu
INSERT INTO products (name, description, category) VALUES
('Gấu bông Teddy', 'Đồ chơi mềm mại cho bé', 'Toys & Games'),
('Bộ xếp hình LEGO', 'Phát triển tư duy', 'Toys & Games'),
('Ly rượu vang', 'Pha lê cao cấp', 'Accessories');
-- Chèn tài khoản mục tiêu
INSERT INTO users_zhgcyo (username_kolqfw, password_imavym) VALUES
('administrator', 'mat_khau_sieu_bi_mat_123'),
('carlos', 'carlos_is_here_99');
Bước 2: File index.php chứa lỗi Đây là đoạn code PHP "mở cửa" cho SQLi do không tách biệt code và dữ liệu:
PHP
<?php
// Kết nối Database
$host = 'localhost'; // Sửa thành localhost để tránh lỗi kết nối
$db = 'shop_lab';
$user = 'root';
$pass = '';
try {
$pdo = new PDO("mysql:host=$host;dbname=$db;charset=utf8mb4", $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Lỗi kết nối DB: " . $e->getMessage());
}
// Lấy category từ URL
$category = isset($_GET['category']) ? $_GET['category'] : 'Toys & Games';
// 🚨 LỖ HỔNG: Nối chuỗi trực tiếp (SQL Injection)
$sql = "SELECT name, description FROM products WHERE category = '$category'";
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SQLi Lab</title>
<style>
.query-box { background: #fee; border: 1px solid red; padding: 10px; margin-bottom: 20px; color: red; font-family: monospace; }
table, th, td { border: 1px solid #ccc; border-collapse: collapse; padding: 10px; }
</style>
</head>
<body>
<h1>🛒 Cửa hàng Demo SQL Injection</h1>
<div class="query-box"><strong>SQL:</strong> <?php echo htmlspecialchars($sql); ?></div>
<ul>
<li><a href="?category=Toys & Games">Toys & Games</a></li>
<li><a href="?category=Accessories">Accessories</a></li>
</ul>
<table>
<tr><th>Name</th><th>Description</th></tr>
<?php
try {
$stmt = $pdo->query($sql);
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$cols = array_values($row);
echo "<tr><td>".htmlspecialchars($cols[0])."</td><td>".htmlspecialchars($cols[1])."</td></tr>";
}
} catch (Exception $e) {
echo "<tr><td colspan='2' style='color:red;'>SQL Error!</td></tr>";
}
?>
</table>
</body>
</html>
Mục tiêu: Lấy mật khẩu administrator.
Payload: .../index.php?category=Accessories' UNION SELECT null, null%23
Giải thích: Toán tử UNION bắt buộc số lượng cột của truy vấn chèn vào phải bằng khớp với số lượng cột của truy vấn gốc.
Chúng ta thử null, null (tương ứng 2 cột). Nếu trang web tải bình thường, chứng tỏ truy vấn gốc có đúng 2 cột.
Nếu bạn thử null, null, null (3 cột) mà trang web báo lỗi, nghĩa là số lượng cột không khớp. null được sử dụng vì nó tương thích với mọi kiểu dữ liệu.
Payload: .../index.php?category=Accessories' UNION SELECT table_name, null FROM information_schema.tables%23
Giải thích: MySQL có một cơ sở dữ liệu hệ thống đặc biệt tên là information_schema. Trong đó, bảng tables chứa danh sách tất cả các bảng tồn tại trong server.
Bằng cách truy vấn table_name từ đây, chúng ta ép trang web in ra tên của các bảng dữ liệu mà lập trình viên đã tạo ra.
Chúng ta tìm kiếm tên bảng lạ (ví dụ: users_zhgcyo) – nơi lưu trữ thông tin người dùng.
Payload: .../index.php?category=Accessories' UNION SELECT column_name, null FROM information_schema.columns WHERE table_name='users_zhgcyo'%23
Giải thích: Sau khi có tên bảng (users_zhgcyo), ta cần biết "tên ngăn kéo" (tên cột) bên trong đó để lấy dữ liệu.
Bảng information_schema.columns chứa thông tin chi tiết về các cột của mọi bảng.
Mệnh đề WHERE table_name='users_zhgcyo' giúp ta lọc chỉ lấy các cột thuộc về bảng mục tiêu, loại bỏ các bảng hệ thống rác.
Payload: .../index.php?category=Accessories' UNION SELECT username_kolqfw, password_imavym FROM users_zhgcyo%23
Giải thích: Đây là bước "kết liễu". Bây giờ chúng ta đã biết tên cột chính xác (username_kolqfw và password_imavym) và tên bảng (users_zhgcyo).
Câu lệnh này yêu cầu database lấy nội dung của 2 cột đó và hiển thị lên đúng 2 vị trí mà trang web thường dùng để hiển thị "Tên sản phẩm" và "Mô tả".
Dữ liệu nhạy cảm (Username: administrator / Password: mat_khau_sieu_bi_mat_123) sẽ được trích xuất và hiển thị ngay trên bảng sản phẩm của trang web.
Lỗ hổng: Ứng dụng tin tưởng dữ liệu đầu vào.
Bản chất: Kẻ tấn công dùng UNION để ép DB trả về dữ liệu của bảng mình muốn.
Key: Luôn hiểu cấu trúc information_schema (bản đồ DB).
Nguyên tắc vàng: "Dữ liệu là dữ liệu, code là code".
Cách vá lỗi: Sử dụng Prepared Statements Thay vì nối chuỗi, hãy tách biệt chúng:
PHP
<?php
// ... (Phần kết nối DB giữ nguyên như trên) ...
$category = isset($_GET['category']) ? $_GET['category'] : 'Toys & Games';
// ✅ CÁCH VÁ LỖI: Sử dụng Prepared Statements
$sql = "SELECT name, description FROM products WHERE category = :cat";
$stmt = $pdo->prepare($sql); // Biên dịch khung truy vấn trước
$stmt->execute(['cat' => $category]); // Gắn dữ liệu sau (an toàn tuyệt đối)
// ... (Phần hiển thị HTML giữ nguyên như trên) ...
?>
Lời khuyên:
Luôn dùng Prepared Statements: Đây là lớp khiên vững chắc nhất.
Giới hạn quyền: Tài khoản DB kết nối web chỉ nên có quyền SELECT trên bảng sản phẩm, tuyệt đối không có quyền truy cập vào các bảng hệ thống.
Tắt hiển thị lỗi: Không bao giờ in lỗi DB (PDOException) ra màn hình trình duyệt của người dùng.
Hy vọng bài blog này giúp bạn làm chủ được lỗ hổng SQLi cơ bản!