Zip Slip

Xảy ra khi ứng dụng giải nén một tập tin nén mà có một file với file name chứa path traversal payload chẳng hạn như ../../evil.sh nhưng không thực hiện validate. Bằng cách ghi đè các file thực thi của hệ thống rồi gọi nó từ xa hoặc chờ hệ thống hay user gọi nó, attacker có thể có khả năng RCE trên máy của nạn nhân. Lỗ hổng này có thể ảnh hưởng đến nhiều định dạng file nén chẳng hạn như tar, jar, war, cpio, apk, rar và 7z.

Để khai thác, attacker cần tạo ra file nén có một entry với file name là path traversal payload:

5 Tue Jun 5 11:04:29 BST 2018 good.sh
20 Tue Jun 5 11:04:42 BST 2018 ../../../../../../../../tmp/evil.sh

Khi đứng ở root directory, nếu ta dùng cd .. thì vẫn ở thư mục root. Do đó, ta có thể dùng bao nhiêu chuỗi ../ tùy thích để hy vọng rằng là file mà sẽ được extract ra sẽ nằm đúng vào thư mục ta cần, chẳng hạn như tmp trong ví dụ trên.

Việc tạo ra file nén có nội dung như trên cần phải làm thủ công và do các công cụ truyền thống không hỗ trợ, mặc dù đặc tả của ZIP có mô tả.

Sau khi có file nén, attacker cần gửi lên ứng dụng và ứng dụng phải thực hiện giải nén nhưng không validate file name.

Enumeration<ZipEntry> entries ​=​ zip​.​getEntries();
while (entries​.​hasMoreElements()) {
	ZipEntry e ​=​ entries.nextElement();
	File f = new File(destinationDir, e.getName()); // vulnerable
	InputStream input ​=​ zip​.​getInputStream(e);
	IOUtils​.​copy(input, write(f));
}

Info

Có thể thử khai thác thông qua vulnerable app này: snyk-labs/java-goof. Sau khi chạy Docker container thì có thể truy cập ở http://localhost:8080/todolist/.

Là một kỹ thuật sử dụng một file nén có symlink để đọc hoặc ghi đè file của server.

Symlink (hay symbolic link) đối với Linux là một reference (hay shortcut trên Windows) trỏ đến file gốc. Việc thay đổi nội dung của symlink cũng sẽ làm thay đổi nội dung của file gốc. Tuy nhiên, nếu ta xóa symlink thì file gốc không bị ảnh hưởng gì.

Ta thường gọi symlink là soft link để phân biệt với hard link, vốn chỉ có thể trỏ đến các file thông thường chứ không trỏ được đến là folder hay các file đặc biệt cũng như là không hoạt động đối với các file nằm ở file system khác1.

Cú pháp tạo symlink:

ln -s (Đường dẫn tới file bạn muốn trỏ tới) (Đường dẫn file mới)

Cú pháp tạo hard link:

ln (Đường dẫn file gốc) (Đường dẫn file mới)

Trên Windows, soft link còn được gọi là junction và nó cho phép trỏ đến file hoặc thư mục ở các volumes khác nhau2. Nói một cách đơn giản, cách hoạt động của nó tương tự với soft link của UNIX. Trong khi đó, hard link ở trên Windows chỉ cho phép tham chiếu đến các file có cùng volume2.

Ta sẽ nghĩ rằng bằng cách tạo ra một symlink đến một file nào đó (chẳng hạn /etc/passwd) và upload lên server, khi đọc nội dung của symlink thì ta sẽ đọc được nội dung của file /etc/passwd ở trên server. Tuy nhiên, sự thật không phải vậy. Ngay tại thời điểm upload symlink lên browser, browser sẽ đi tìm file được trỏ đến của symlink rồi mới upload lên.

Để có thể link đến file ở trên server mà ta muốn, ta cần bọc symlink trong một file nén chẳng hạn như zip hoặc tar.

Sau đây là đoạn script dùng để tạo zip symlink payload:

import os
import argparse
 
def main():
    parser = argparse.ArgumentParser(
        description='ZIP symlink payload generator (without -g flag)'
    )
    parser.add_argument('-f', '--file', type=str, required=True, help="Create ZIP payloads based on a wordlist")
    args = parser.parse_args()
 
    # Read the list of target paths from the wordlist file
    with open(args.file, "r") as f:
        paths = f.readlines()
 
    # Create a symlink and zip file for each target path
    for idx, target in enumerate(paths):
        target = target.strip()
        linkname = f"link{idx}"
        zipname = f"{linkname}.zip"
        os.system(f"ln -s '{target}' '{linkname}' 1>/dev/null 2>/dev/null")
        os.system(f"zip --symlinks '{zipname}' '{linkname}' 1>/dev/null 2>/dev/null")
 
    # Remove all created symlinks
    os.system("find . -type l -delete")
    print("[+] Payloads have been created")
 
if __name__ == "__main__":
    main()

Giải thích một chút:

  • Script sẽ tạo file nén chứa symlink với input là file chứa các target file từ argument -f, chẳng hạn như /etc/passwd, /home/user/.ssh/id_rsa, etc.
  • Cụ thể hơn, nó sẽ tạo symlink bằng lệnh ln -s '{target}' '{linkname}.
  • Sau đó, sử dụng zip --symlinks '{zipname}' '{linkname} để nén linkname. Flag --symlinks là để cho phép file nén chứa symlink.

Ngoài khả năng có thể đọc được nội dung của file thì ta còn có thể dùng zip symlink attack để ghi đè file nhằm thực hiện RCE.

Như trong bước 2 của hình trên, sau khi thực hiện xong bước 1 và liên kết link_to_html đến /var/www/html, ta sẽ tiến hành tạo ra một thư mục link_to_html chứa file shell.php và nén lại trong file step2.zip.

Khi được upload và giải nén, server sẽ nhận ra là folder link_to_html đã tồn tại và trỏ đến /var/www/html. Dẫn đến, server sẽ ghi file shell.php vào /var/www/html và giúp ta có khả năng RCE.

Ta gọi đây là tấn công File Collisions.

Tools

Resources

Footnotes

  1. Giải thích về Hard links và soft links trong Linux - Cloudzone

  2. Hard Links and Junctions - Win32 apps | Microsoft Learn 2