diff --git a/testdata/readme.relativenameszip b/testdata/readme.relativenameszip new file mode 100644 index 0000000..2e2927f --- /dev/null +++ b/testdata/readme.relativenameszip @@ -0,0 +1,61 @@ +# Names containing current or parent directories + +The ZIP specifications do not say anything about paths containing the current +directory (`.`) or the parent directory (`..`). The only thing that is said is: + +``` +The name of the file, with optional relative path. +The path stored MUST NOT contain a drive or +device letter, or a leading slash. +``` + +As both `.` and `..` are relative paths this could be interpreted +to read that that these paths are valid. + +Creating a file with any of these paths is trivial using Python's `zipfile` +module: + +``` +>>> import zipfile +>>> z = zipfile.ZipInfo('../../.././tmp/relative') +>>> contents = 10*b'c' +>>> bla = zipfile.ZipFile('relative.zip', mode='w') +>>> bla.writestr(z, contents) +>>> bla.close() +``` + +The relative path with the current and parent directory will be stored in the +file: + +``` +$ unzip -l relative.zip +Archive: relative.zip + Length Date Time Name +--------- ---------- ----- ---- + 10 01-01-1980 00:00 ../../.././tmp/relative +--------- ------- + 10 1 file +``` + +`unzip` processes this file but issues a warning: + +``` +$ unzip relative.zip +Archive: relative.zip +warning: skipped "../" path component(s) in ../../.././tmp/relative + extracting: tmp/relative +``` + +`p7zip` extracts the file without a warning. + +Both implementations will strip all `..` components and basically rewrite +the filename from `../../.././tmp/relative` to `tmp/relative`. + +Other ZIP implementations might not and this could be used for a path traversal +attack. This is actually a very old attack [dating back to 1991][phrack] +although it was [rediscovered in 2018 as Zip Slip][zip_slip] with +[many implementations affected][zip_slip_2]. + +[phrack]:http://phrack.org/issues/34/5.html +[zip_slip]:https://security.snyk.io/research/zip-slip-vulnerability +[zip_slip_2]:https://github.com/snyk/zip-slip-vulnerability diff --git a/testdata/relative.zip b/testdata/relative.zip new file mode 100644 index 0000000..f785ee7 Binary files /dev/null and b/testdata/relative.zip differ diff --git a/ziplinter/src/snapshots/ziplinter__test__relative.zip.snap b/ziplinter/src/snapshots/ziplinter__test__relative.zip.snap new file mode 100644 index 0000000..6a02bdb --- /dev/null +++ b/ziplinter/src/snapshots/ziplinter__test__relative.zip.snap @@ -0,0 +1,98 @@ +--- +source: ziplinter/src/lib.rs +expression: result +--- +{ + "comment": "", + "contents": [ + { + "central": { + "comment": "", + "compressed_size": 10, + "crc32": 4044738111, + "creator_version": { + "host_system": "Unix", + "version": 20 + }, + "disk_nbr_start": 0, + "external_attrs": 25165824, + "extra": [], + "flags": 0, + "header_offset": 0, + "internal_attrs": 0, + "method": "Store", + "mode": 384, + "modified": "1980-01-01T00:00:00Z", + "name": "../../.././tmp/relative", + "reader_version": { + "host_system": "MsDos", + "version": 20 + }, + "uncompressed_size": 10 + }, + "local": { + "accessed": null, + "compressed_size": 10, + "crc32": 4044738111, + "created": null, + "extra": [], + "flags": 0, + "gid": null, + "header_offset": 0, + "method": "Store", + "method_specific": "None", + "mode": 0, + "modified": "1980-01-01T00:00:00Z", + "name": "../../.././tmp/relative", + "reader_version": { + "host_system": "MsDos", + "version": 20 + }, + "uid": null, + "uncompressed_size": 10 + } + } + ], + "encoding": "Utf8", + "eocd": { + "dir": { + "inner": { + "dir_disk_nbr": 0, + "dir_records_this_disk": 1, + "directory_offset": 63, + "directory_records": 1, + "directory_size": 69, + "disk_nbr": 0 + }, + "offset": 132 + }, + "dir64": null, + "global_offset": 0 + }, + "parsed_ranges": [ + { + "contains": "end of central directory record", + "end": 154, + "start": 132 + }, + { + "contains": "central directory header", + "end": 132, + "filename": "../../.././tmp/relative", + "start": 63 + }, + { + "contains": "local file header", + "end": 53, + "filename": "../../.././tmp/relative", + "start": 0 + }, + { + "contains": "file data", + "end": 63, + "filename": "../../.././tmp/relative", + "start": 53 + } + ], + "size": 154 +}