php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #77214 ZipArchive::extractTo() errors on directories ending with .
Submitted: 2018-11-28 16:21 UTC Modified: 2018-11-28 19:19 UTC
Votes:6
Avg. Score:3.8 ± 1.1
Reproduced:6 of 6 (100.0%)
Same Version:1 (16.7%)
Same OS:5 (83.3%)
From: nystrom dot lars dot egon at gmail dot com Assigned:
Status: Verified Package: Zip Related
PHP Version: 7.2.12 OS: Linux
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: nystrom dot lars dot egon at gmail dot com
New email:
PHP Version: OS:

 

 [2018-11-28 16:21 UTC] nystrom dot lars dot egon at gmail dot com
Description:
------------
This is closely related to bug #69477, but I'm experiencing a different behavior.

When you have a zip archive that contains a directory with a name that ends with . (a dot) ZipArchive errors out with the message:

ZipArchive::extractTo(/path/to/extract/to/): failed to open stream: Is a directory



Test script:
---------------
$extractPath = "/path/to/extract/to";
$zip = new ZipArchive;
$zip->open("/path/to/zipfile");
// Next line causes the error if the zip file contains a directory named 'myfolder.'
$zip->extractTo($extractPath, ['myfolder.']); 


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2018-11-28 19:19 UTC] cmb@php.net
-Status: Open +Status: Verified
 [2018-11-28 19:19 UTC] cmb@php.net
I get this error message if the archive contains an *empty*
directory entry with a trailing dot.  Test script:

    <?php

    $zip = new ZipArchive;
    $zip->open(__DIR__ . '/77214.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
    $zip->addEmptyDir('foo.');
    $zip->close();

    $zip->open(__DIR__ . '/77214.zip');
    $zip->extractTo(__DIR__ . '/77214');
    $zip->close();

The culprit is php_zip_make_relative_path()[1], which mistakenly
returns an empty string instead of "foo."

[1] <https://github.com/php/php-src/blob/php-7.3.0RC6/ext/zip/php_zip.c#L115-L130>
 [2021-01-14 18:58 UTC] david at moodle dot com
I was experiencing this problem as well and I was able to debug it to what I believe causes the bug.

Let us have a simple ZIP archive to reproduce the problem, having a single file with the path

    dot./sub/file.txt

When using ZipArchive::extractTo() to extract this file, the file was actually extracted into

    sub/file.txt

Looking at the function source code at https://github.com/php/php-src/blob/533af1eb14dbeb10c83f291b07da62b70d0df8ae/ext/zip/php_zip.c#L2714 I saw that it calls `php_zip_extract_file()` from the same file to do the job.

The function in turn calls `php_zip_make_relative_path()` to clean the file paths and avoid path traversal attack - see https://github.com/php/php-src/blob/533af1eb14dbeb10c83f291b07da62b70d0df8ae/ext/zip/php_zip.c#L146-L150

From the implementation of `php_zip_make_relative_path()` at https://github.com/php/php-src/blob/533af1eb14dbeb10c83f291b07da62b70d0df8ae/ext/zip/php_zip.c#L83-L117 I understood that it iterates through the file path from the end to the start and when it finds a slash character with a dot before it, it trims that whole part and starts from the position after the slash.

I believe that is wrong. To avoid traversal attack, it should check for two dots before the slash (like ../something). Also it seems there seems to be off-by-one mistake as the comment there reads "i is the position of ." but I think that i is the position of the slash.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 18:01:29 2024 UTC