پیمایش مسیر زیپ

دسته OWASP: MASVS-STORAGE: ذخیره سازی

نمای کلی

آسیب‌پذیری Zip Path Traversal که با نام ZipSlip نیز شناخته می‌شود، مربوط به مدیریت بایگانی‌های فشرده است. در این صفحه، ما این آسیب‌پذیری را با استفاده از فرمت ZIP به عنوان مثال نشان می‌دهیم، اما مشکلات مشابهی ممکن است در کتابخانه‌هایی که فرمت‌های دیگر را مدیریت می‌کنند، مانند TAR، RAR یا 7z ایجاد شود.

دلیل اصلی این مشکل این است که در بایگانی ZIP، هر فایل بسته بندی شده با یک نام کاملاً واجد شرایط ذخیره می شود که به کاراکترهای خاصی مانند اسلش و نقطه اجازه می دهد. کتابخانه پیش‌فرض بسته java.util.zip نام ورودی‌های بایگانی را برای نویسه‌های پیمایش دایرکتوری بررسی نمی‌کند ( ../ )، بنابراین هنگام الحاق نام استخراج‌شده از بایگانی با مسیر دایرکتوری هدف باید دقت ویژه‌ای داشت. .

بسیار مهم است که هر قطعه کد یا کتابخانه استخراج شده با ZIP را از منابع خارجی تأیید کنید. بسیاری از این گونه کتابخانه ها در برابر Zip Path Traversals آسیب پذیر هستند.

تاثیر

از آسیب‌پذیری Zip Path Traversal می‌توان برای بازنویسی فایل دلخواه استفاده کرد. بسته به شرایط، تأثیر ممکن است متفاوت باشد، اما در بسیاری از موارد این آسیب‌پذیری می‌تواند منجر به مسائل امنیتی مهمی مانند اجرای کد شود.

اقدامات کاهشی

برای کاهش این مشکل، قبل از استخراج هر ورودی، همیشه باید بررسی کنید که مسیر هدف فرزند دایرکتوری مقصد است. کد زیر فرض می‌کند که دایرکتوری مقصد امن است - فقط توسط برنامه شما قابل نوشتن است و تحت کنترل مهاجم نیست - در غیر این صورت برنامه شما می‌تواند مستعد آسیب‌پذیری‌های دیگر مانند حملات Symlink باشد.

کاتلین

companion object {
    @Throws(IOException::class)
    fun newFile(targetPath: File, zipEntry: ZipEntry): File {
        val name: String = zipEntry.name
        val f = File(targetPath, name)
        val canonicalPath = f.canonicalPath
        if (!canonicalPath.startsWith(
                targetPath.canonicalPath + File.separator)) {
            throw ZipException("Illegal name: $name")
        }
        return f
    }
}

جاوا

public static File newFile(File targetPath, ZipEntry zipEntry) throws IOException {
    String name = zipEntry.getName();
    File f = new File(targetPath, name);
    String canonicalPath = f.getCanonicalPath();
    if (!canonicalPath.startsWith(targetPath.getCanonicalPath() + File.separator)) {
      throw new ZipException("Illegal name: " + name);
    }
    return f;
 }

برای جلوگیری از بازنویسی تصادفی فایل‌های موجود، قبل از شروع فرآیند استخراج، باید مطمئن شوید که فهرست مقصد خالی است. در غیر این صورت شما در معرض خطر خرابی برنامه یا در موارد شدید، به خطر افتادن برنامه هستید.

کاتلین

@Throws(IOException::class)
fun unzip(inputStream: InputStream?, destinationDir: File) {
    if (!destinationDir.isDirectory) {
        throw IOException("Destination is not a directory.")
    }
    val files = destinationDir.list()
    if (files != null && files.isNotEmpty()) {
        throw IOException("Destination directory is not empty.")
    }
    ZipInputStream(inputStream).use { zipInputStream ->
        var zipEntry: ZipEntry
        while (zipInputStream.nextEntry.also { zipEntry = it } != null) {
            val targetFile = File(destinationDir, zipEntry.name)
            // ...
        }
    }
}

جاوا

void unzip(final InputStream inputStream, File destinationDir)
      throws IOException {
  if(!destinationDir.isDirectory()) { 
    throw IOException("Destination is not a directory.");
  }

  String[] files = destinationDir.list();
  if(files != null && files.length != 0) { 
    throw IOException("Destination directory is not empty.");
  }

  try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {
    ZipEntry zipEntry;
    while ((zipEntry = zipInputStream.getNextEntry()) != null) {
      final File targetFile = new File(destinationDir, zipEntry);
        …
    }
  }
}

منابع

{% کلمه به کلمه %} {% آخر کلمه %}
  • توجه: وقتی جاوا اسکریپت خاموش است، متن پیوند نمایش داده می شود
  • پیمودن مسیر
{% کلمه به کلمه %}
{% آخر کلمه %}